为构建系统定义二进制文件

构建系统的一部分工作是编译 C/C++ 并链接生成的对象以生成可执行文件和/或库。本文档描述了定义要构建的内容以及如何构建的基本知识。以下所有内容都描述了在 moz.build 文件中使用的构造。

源文件

要在给定目录中使用的源文件在 SOURCESUNIFIED_SOURCES 变量中注册。 UNIFIED_SOURCES 具有特殊的行为,即它们以 16 个为一批进行聚合,例如,要求这些源文件中没有冲突的变量。

SOURCESUNIFIED_SOURCES 是必须追加到的列表,并且每次追加都需要给定列表按字母顺序排序。

UNIFIED_SOURCES += [
    'FirstSource.cpp',
    'SecondSource.cpp',
    'ThirdSource.cpp',
]

SOURCES += [
    'OtherSource.cpp',
]

SOURCESUNIFIED_SOURCES 可以包含各种不同文件类型的混合,例如 C、C++ 和 Objective C。

静态库

要构建静态库,除了定义源文件(见上文)之外,只需要使用 Library 模板定义库名称。

Library('foo')

库文件名在 UNIX 系统上将为 libfoo.a,在 Windows 上将为 foo.lib

如果静态库需要聚合其他静态库,则可以将 Library 名称列表添加到 USE_LIBS 变量中。与 SOURCES 一样,它要求追加的列表按字母顺序排序。

USE_LIBS += ['bar', 'baz']

如果有多个目录包含相同的 Library 名称,可以通过添加所需库的路径(相对或绝对)来区分。

USE_LIBS += [
    '/path/from/topsrcdir/to/bar',
    '../relative/baz',
]

请注意,这些路径中的叶名称是 Library 名称,而不是实际的文件名。

请注意,当前,构建系统可能不会为静态库创建实际的库。这是一个实现细节,无需担心。

作为特殊规则,USE_LIBS 允许包含对共享库的引用。在这种情况下,链接此静态库的程序和共享库将继承这些共享库依赖项。

中间(静态)库

在树中的许多情况下,静态库的构建目的仅仅是为了链接到另一个更大的库(如 libxul)。与其将所有必需的库添加到较大库的 USE_LIBS 中,不如告诉构建系统当前目录中构建的库旨在使用 FINAL_LIBRARY 变量链接到该较大库。

FINAL_LIBRARY = 'xul'

FINAL_LIBRARY 值必须与树中某个地方的唯一 Library 名称匹配。

作为特殊规则,这些中间库不需要自己的 Library 名称。

共享库

有时,我们需要共享库,也称为动态库。此类库的定义方式类似于静态库,使用 SharedLibrary 模板而不是 Library

SharedLibrary('foo')

使用此模板时,不会构建静态库。请参阅下文以构建这两种类型的库。

如果 SharedLibrary 名称为 foo,则库文件名在 OSX 上将为 libfoo.dylib,在 ELF 系统(Linux 等)上将为 libfoo.so,在 Windows 上将为 foo.dll。在 Windows 上,还有一个名为 foo.lib 的导入库,在链接器命令行上使用。 libfoo.dyliblibfoo.so 分别被视为 OSX 和 ELF 系统的导入库名称。

在 OSX 上,可能需要创建一种特殊类型的动态库:框架。这可以通过 Framework 模板完成。

Framework('foo')

如果 Framework 名称为 foo,则框架文件名将为 foo。但是,此模板会影响所有平台上的行为,因此仅需在 OSX 上设置。

可执行文件

可执行文件,也称为程序,在最简单的形式中,使用 Program 模板定义。

Program('foobar')

在 UNIX 系统上,可执行文件名将为 foobar,而在 Windows 上,将为 foobar.exe

与静态库和共享库一样,可以使用 USE_LIBS 指示构建系统将库链接到可执行文件,其中列出了各种 Library 名称。

在某些情况下,我们希望为当前目录中的每个源文件创建一个可执行文件,在这种情况下,我们可以使用 SimplePrograms 模板

SimplePrograms([
    'FirstProgram',
    'SecondProgram',
])

与需要相应 SOURCESProgram 不同,使用 SimplePrograms 时,相应的 SOURCES 是隐含的。如果相应的 sources 的扩展名不同于 .cpp,则可以指定正确的扩展名

SimplePrograms([
    'ThirdProgram',
    'FourthProgram',
], ext='.c')

请注意,此构造是为了与 mozilla 树中已有的内容兼容而添加的;建议不要使用扩展名不同于 .cpp 的源文件添加新的简单程序。

类似于 SimplePrograms,还有 CppUnitTests 模板,它使用相同的规则定义 C++ 单元测试程序。与 SimplePrograms 一样,它接受一个 ext 参数来指定相应 SOURCES 的扩展名,如果它不同于 .cpp

链接系统库

程序和库通常需要链接系统库,例如窗口小部件工具包等。可以使用 OS_LIBS 变量提供这些必需的依赖项。

OS_LIBS += [
    'foo',
    'bar',
]

使用 MSVC 构建时,它扩展为 foo.lib bar.lib,否则扩展为 -lfoo -lbar

为了方便使用 pkg-configOS_LIBS 还可以接受链接器标志,例如 -L/some/path-llib,以便可以直接分配来自 CONFIGLIBS 变量,例如

OS_LIBS += CONFIG['MOZ_PANGO_LIBS']

(假设 CONFIG['MOZ_PANGO_LIBS'] 是一个列表,而不是一个字符串)

USE_LIBS 一样,此变量适用于静态库和共享库以及程序。

来自第三方构建系统的库

树中的一些库不是由 moz.build 控制的构建系统构建的,并且没有与它们对应的 Library

但是,USE_LIBS 允许通过提供完整路径来引用此类库(例如,在区分相同的 Library 名称时)。与 USE_LIBS 的其他用法相同,因此仅应提供不带前缀和后缀的库名称。

USE_LIBS += [
    '/path/from/topsrcdir/to/third-party/bar',
    '../relative/third-party/baz',
]

请注意,/path/from/topsrcdir/to/third-party../relative/third-party/baz 必须位于子配置目录(在 configure.in 中具有 AC_OUTPUT_SUBDIRS 的目录)或 security/nss 下。

构建静态库和共享库

当这两种类型的库都必需时,需要设置 FORCE_SHARED_LIBFORCE_STATIC_LIB 这两个布尔变量。

FORCE_SHARED_LIB = True
FORCE_STATIC_LIB = True

但是,由于静态库和 Windows 导入库具有相同的文件名,因此静态库或共享库名称需要与提供给 Library 模板的名称不同。

STATIC_LIBRARY_NAMESHARED_LIBRARY_NAME 变量可用于更改静态库或共享库名称。

Library('foo')
STATIC_LIBRARY_NAME = 'foo_s'

使用上述方法,在 Windows 上,foo_s.lib 将是静态库,foo.dll 是共享库,foo.lib 是导入库。

在某些情况下,为了方便起见,可以同时设置 STATIC_LIBRARY_NAMESHARED_LIBRARY_NAME。例如

Library('mylib')
STATIC_LIBRARY_NAME = 'mylib_s'
SHARED_LIBRARY_NAME = CONFIG['SHARED_NAME']

这允许在另一个库或可执行文件的 USE_LIBS 中使用 mylib

USE_LIBS 中引用构建这两种类型库的 Library 名称时,将选择共享库进行链接。但有时,需要链接静态版本,在这种情况下,需要在 USE_LIBS 中使用 static: 作为 Library 名称的前缀

a/moz.build:
   Library('mylib')
   FORCE_SHARED_LIB = True
   FORCE_STATIC_LIB = True
   STATIC_LIBRARY_NAME = 'mylib_s'
b/moz.build:
   Program('myprog')
   USE_LIBS += [
       'static:mylib',
   ]

其他

变量 SONAME 为库声明了一个“共享对象名称”。它默认为 Library 名称或如果已设置则为 SHARED_LIBRARY_NAME。当链接到具有 SONAME 的库时,生成的库或程序将依赖于名称对应于 SONAME 而不是 Library 名称的库。这仅影响 ELF 系统。

a/moz.build:
   Library('mylib')
b/moz.build:
   Library('otherlib')
   SONAME = 'foo'
c/moz.build:
   Program('myprog')
   USE_LIBS += [
       'mylib',
       'otherlib',
   ]

例如在 Linux 上,上述 myprog 将具有针对 libmylib.solibfoo.so 的 DT_NEEDED 标记,而不是 libmylib.solibotherlib.so(如果没有 SONAME)。这意味着 myprog 的运行时需求是 libfoo.so 而不是 libotherlib.so