一个主目标是一个用户定义的命名实体,可以被构建,例如一个可执行文件。声明一个主目标通常使用在“内置规则”部分描述的其中一个主目标规则。用户也可以声明自定义的主目标规则,如“主目标规则”部分所示。
Boost.Build 中大多数主目标规则具有相同的通用签名
rule rule-name
(
main-target-name :
sources + :
requirements * :
default-build * :
usage-requirements * )
main-target-name
是在命令行上请求目标以及从其他主目标中使用该目标时使用的名称。主目标名称可以包含字母数字字符、连字符(‘-
’)和下划线(‘_
’)。sources
是必须组合的源文件和其他主目标的列表。requirements
是构建此主目标时必须始终存在的属性列表。default-build
是将在未指定相同功能的其他值(例如,在命令行上或从依赖目标传播)的情况下使用的属性列表。usage-requirements
是将传播到所有使用此目标的主目标(即所有其依赖项)的属性列表。某些主目标规则具有不同的参数列表,如文档中明确说明。
目标的实际需求是通过将目标声明所在的项目的需求与显式指定的需求进行细化而获得的。usage-requirements 也是如此。更多详细信息可以在“属性细化”部分找到。
主目标的名称有两个用途。首先,它用于从其他目标和命令行引用此目标。其次,它用于计算生成文件的名称。通常,文件名是通过附加系统相关的后缀和前缀从主目标名称获得的。
主目标的名称可以包含字母数字字符、连字符、下划线和点。在解析来自其他目标的引用时,整个名称都具有意义。为了确定文件名,只取第一个点之前的部分。例如
obj test.release : test.cpp : <variant>release ; obj test.debug : test.cpp : <variant>debug ;
将生成两个名为test.obj
(在两个不同的目录中)的文件,而不是两个名为test.release.obj
和test.debug.obj
的文件。
源文件列表指定了为了获得结果目标而应该处理的内容。大多数情况下,它只是一个文件列表。有时,您希望自动构建源文件列表,而不是手动拼写它,在这种情况下,您可以使用glob规则。以下两个示例
exe a : a.cpp ; # a.cpp is the only source file exe b : [ glob *.cpp ] ; # all .cpp files in this directory are sources
除非您指定具有绝对路径的文件,否则名称将被视为相对于源目录——通常是 Jamfile 所在的目录,但可以更改,如“项目”部分所述。
源文件列表还可以引用其他主目标。同一项目中的目标可以通过名称引用,而其他项目中的目标必须使用目录或符号项目名称进行限定。目录/项目名称与目标名称之间用双斜杠分隔。没有特殊的语法来区分目录名称和项目名称——双斜杠之前的部分首先作为项目名称查找,然后作为目录名称查找。例如
lib helper : helper.cpp ; exe a : a.cpp helper ; # Since all project ids start with slash, ".." is a directory name. exe b : b.cpp ..//utils ; exe c : c.cpp /boost/program_options//program_options ;
第一个 exe 使用在同一项目中定义的库。第二个使用在上一级 Jamfile 中定义的某个目标(很可能是库)。最后,第三个目标使用C++ Boost库,使用其绝对符号名称引用它。有关目标引用的更多信息,请参阅“依赖目标”部分和“目标标识符和引用”部分。
需求是在构建目标时应始终存在的属性。通常,它们是包含和定义
exe hello : hello.cpp : <include>/opt/boost <define>MY_DEBUG ;
还有许多其他功能,如“内置功能”部分所列。例如,如果库只能静态构建,或者由于编译器错误导致文件无法进行优化编译,则可以使用
lib util : util.cpp : <link>static ; obj main : main.cpp : <optimization>off ;
有时,需要在目标的构建属性之间维护特定的关系。这可以通过条件需求来实现。例如,您可能希望在以共享方式构建库时设置特定的#defines
,或者在以发布模式构建目标的release
变体时设置特定的#defines
。
lib network : network.cpp
: <link>shared:<define>NETWORK_LIB_SHARED
<variant>release:<define>EXTRA_FAST
;
在上面的示例中,每当network
以<link>shared
构建时,<define>NETWORK_LIB_SHARED
也将位于其属性中。
您可以在条件中使用多个属性,例如
lib network : network.cpp : <toolset>gcc,<optimization>speed:<define>USE_INLINE_ASSEMBLER ;
条件需求的更强大的变体是间接条件需求。您可以提供一个规则,该规则将使用当前构建属性进行调用,并可以计算要添加的其他属性。例如
lib network : network.cpp : <conditional>@my-rule ; rule my-rule ( properties * ) { local result ; if <toolset>gcc <optimization>speed in $(properties) { result += <define>USE_INLINE_ASSEMBLER ; } return $(result) ; }
此示例等效于上一个示例,但对于复杂情况,间接条件需求可能更容易编写和理解。
为目标显式指定的属性通常与为包含项目指定的属性相结合。您可以使用语法导致目标完全忽略特定项目需求,方法是在属性前添加减号,例如
exe main : main.cpp : -<define>UNNECESSARY_DEFINE ;
此语法是忽略自由属性(如定义)的唯一方法。它对于普通属性也很有用。考虑以下示例
project test : requirements <threading>multi ; exe test1 : test1.cpp ; exe test2 : test2.cpp : <threading>single ; exe test3 : test3.cpp : -<threading>multi ;
这里,test1
继承项目需求,并将始终以多线程模式构建。test2
目标覆盖项目的需求,并将始终以单线程模式构建。相反,test3
目标移除项目需求中的属性,并将以单线程或多线程模式构建,具体取决于用户请求的变体。
请注意,需求的移除是完全文本化的:您需要指定完全相同的属性才能将其移除。
default-build
参数是一组属性,如果构建请求未对该组中的功能指定值,则将使用这些属性。例如
exe hello : hello.cpp : : <threading>multi ;
将构建一个多线程目标,除非用户明确请求单线程版本。需求和默认构建之间的区别在于,需求无法以任何方式被覆盖。
目标的构建方式可能差别很大,以至于使用条件需求来描述它们将很困难。例如,假设库实际上使用不同的源文件取决于用于构建它的工具集。我们可以使用目标替代来表达这种情况
lib demangler : dummy_demangler.cpp ; # alternative 1 lib demangler : demangler_gcc.cpp : <toolset>gcc ; # alternative 2 lib demangler : demangler_msvc.cpp : <toolset>msvc ; # alternative 3
在上面的示例中,当使用gcc
或msvc
构建时,demangler
将使用特定于工具集的源文件。否则,它将使用通用源文件dummy_demangler.cpp
。
可以内联声明目标,即“sources”参数可以包含对其他主规则的调用。例如
exe hello : hello.cpp [ obj helpers : helpers.cpp : <optimization>off ] ;
将导致“helpers.cpp”始终在没有优化的情况下进行编译。引用内联主目标时,其声明的名称必须以其父目标的名称和两个点作为前缀。在上面的示例中,要仅构建 helpers,应运行b2 hello..helpers
。
当命令行上未请求任何目标时,将构建当前项目中的所有目标。如果目标应仅通过显式请求构建,则可以使用explicit规则来表达
explicit install_programs ;