Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

1. 简介

想了解 B2 的功能吗?从教程开始,继续阅读概述。当您准备好在实践中使用 B2 时,请转到安装

使用 B2 构建项目?请参阅安装,然后阅读概述

在您的项目上设置 B2?请查看概述扩展器手册

如果您在此文档中发现任何不清楚的地方,请直接在 issue tracker 中报告问题。对于更一般的问题,请将其发布到我们的讨论论坛 (https://github.com/bfgroup/b2/discussions)。

版权所有 2018-2021 René Ferdinand Rivera Morell;版权所有 2006, 2014 Vladimir Prus。根据 Boost 软件许可证 1.0 版分发。(请参阅随附文件 LICENSE.txt 或复制于 https://www.bfgroup.xyz/b2/LICENSE.txt

2. 安装

要从 GitHub 上的官方版本安装 B2,请按照以下步骤操作

  1. 解压发行版。在命令行中,转到解压树的根目录。

  2. 运行 .\bootstrap.bat (在 Windows 上) 或 ./bootstrap.sh (在其他操作系统上)。

  3. 运行

    $ ./b2 install --prefix=PREFIX

    其中 PREFIX 是您要安装 B2 的目录。

  4. 可选地,将 PREFIX/bin 添加到您的 PATH 环境变量。

$ PREFIX/bin/b2

应该构建一个简单的可执行文件。

构建 b2 引擎需要一个支持 C++11 的编译器。但是使用 b2 引擎和构建系统不需要 C++11。

3. 教程

本节将引导您了解 B2 的最基本功能。我们将从“你好,世界”示例开始,学习如何使用库,并以测试和安装功能结束。

3.1. 你好,世界

B2 可以构建的最简单的项目存储在 example/hello/ 目录中。该项目由名为 Jamfile 的文件描述,其中包含

exe hello : hello.cpp ;

即使使用这个简单的设置,您也可以做一些有趣的事情。首先,只需调用 b2 即可通过编译和链接 hello.cpp 来构建 hello 可执行文件。默认情况下,构建调试变体。现在,要构建 hello 的发布变体,请调用

b2 release

请注意,调试和发布变体是在不同的目录中创建的,因此您可以在变体之间切换,甚至一次构建多个变体,而无需任何不必要的重新编译。让我们通过在项目的 Jamfile 中添加另一行来扩展示例

exe hello2 : hello.cpp ;

现在让我们再次构建项目的调试和发布变体

b2 debug release

请注意,链接了 hello2 的两个变体。由于我们已经构建了 hello 的两个变体,因此 hello.cpp 不会重新编译;相反,现有的目标文件将仅链接到 hello2 的相应变体中。现在让我们删除所有已构建的产品

b2 --clean debug release

也可以构建或清理特定目标。以下两个命令分别仅构建或清理 hello2 的调试版本。

b2 hello2
b2 --clean hello2

3.2. 属性

为了可移植地表示目标配置的各个方面,例如调试和发布变体,或单线程和多线程构建,B2 使用具有关联值的特性。例如,debug-symbols 特性可以具有 onoff 的值。属性只是一个(特性,值)对。当用户启动构建时,B2 会自动将请求的属性转换为适当的命令行标志,以调用工具集组件,如编译器和链接器。

有许多内置特性可以组合起来生成任意构建配置。以下命令构建项目的 release 变体,禁用内联并启用调试符号

b2 release inlining=off debug-symbols=on

命令行上的属性使用以下语法指定

feature-name=feature-value

我们在 b2 调用中看到的 releasedebug 只是指定 variant 特性值的简写方式。例如,上面的命令也可以这样写

b2 variant=release inlining=off debug-symbols=on

variant 非常常用,因此已被赋予特殊地位,作为隐式特性——B2 将仅从其一个值的名称中推断出其身份。

有关特性的完整描述,请参阅名为“特性和属性”的部分。

3.2.1. 构建请求和目标需求

在命令行上指定的属性集构成了构建请求——对构建请求的目标(或者,如果没有明确请求目标,则为当前目录中的项目)所需属性的描述。用于构建目标的实际属性通常是构建请求和从项目的 Jamfile(及其其他 Jamfile,如名为“项目层级”的部分所述)派生的属性的组合。例如,#included 头文件的位置通常不在命令行上指定,而是在 Jamfile 中描述为目标需求,并自动与这些目标的构建请求结合使用。多线程编译是典型目标需求的另一个示例。下面的 Jamfile 片段说明了如何指定这些需求。

exe hello
    : hello.cpp
    : <include>boost <threading>multi
    ;

当构建 hello 时,上面指定的两个需求将始终存在。如果在 b2 命令行上给出的构建请求与目标的需求明确冲突,则目标需求通常会覆盖(或者,在“free”特性(如 <include>)的情况下,[1] 扩充)构建请求。

<include> 特性的值相对于使用它的 Jamfile 的位置。

3.2.2. 项目属性

如果我们希望我们的另一个目标 hello2 具有相同的需求,我们可以简单地复制它们。但是,随着项目的增长,这种方法会导致 Jamfile 中出现大量重复的样板代码。幸运的是,有一种更好的方法。每个项目都可以指定一组属性,包括需求

project
    : requirements <include>/home/ghost/Work/boost <threading>multi
    ;

exe hello : hello.cpp ;
exe hello2 : hello.cpp ;

效果就像我们为 hellohello2 指定了相同的需求一样。

3.3. 项目层级

到目前为止,我们只考虑了一个项目的示例,其中包含一个用户编写的 Jamfile 文件。典型的、大型代码库将由组织成树状结构的许多项目组成。树的顶部称为项目根目录。每个子项目都由项目根目录的后代目录中名为 Jamfile 的文件定义。子项目的父项目由祖先目录中最近的 Jamfile 文件定义。例如,在以下目录布局中

top/
  |
  +-- Jamfile
  |
  +-- app/
  |    |
  |    +-- Jamfile
  |    `-- app.cpp
  |
  `-- util/
       |
       +-- foo/
       .    |
       .    +-- Jamfile
       .    `-- bar.cpp

项目根目录是 top/top/app/top/util/foo/ 中的项目是根项目的直接子项目。

当我们提到以普通字体设置的“Jamfile”时,我们指的是名为 JamfileJamroot 的文件。当我们需要更具体时,文件名将设置为“Jamfile”或“Jamroot”。

项目从其父项目继承所有属性(例如需求)。继承的需求与子项目指定的任何需求相结合。例如,如果 top/Jamfile 具有

<include>/home/ghost/local

在其需求中,那么它的所有子项目也将在其需求中具有它。当然,任何项目都可以将包含路径添加到其父项目指定的路径中。[2] 更多详细信息可以在名为“项目”的部分中找到。

在命令行上未明确指定任何目标的情况下调用 b2 将构建根目录位于当前目录中的项目。构建项目不会自动导致其子项目被构建,除非父项目的 Jamfile 明确要求这样做。在我们的示例中,top/Jamfile 可能包含

build-project app ;

这将导致在构建 top/ 中的项目时构建 top/app/ 中的项目。但是,只有当 top/top/app/ 中的目标需要 top/util/foo/ 中的目标时,才会构建这些目标。

3.4. 依赖目标

当构建依赖于首先构建另一个目标 Y(例如必须与 X 链接的库)的目标 X 时,Y 称为 X依赖项X 称为 Y依赖项

为了感受目标依赖关系,让我们继续上面的示例,看看 top/app/Jamfile 如何使用 top/util/foo 中的库。如果 top/util/foo/Jamfile 包含

lib bar : bar.cpp ;

那么要在 top/app/Jamfile 中使用此库,我们可以编写

exe app : app.cpp ../util/foo//bar ;

虽然 app.cpp 指的是常规源文件,但 ../util/foo//bar 是对另一个目标的引用:在 ../util/foo 的 Jamfile 中声明的库 bar

一些其他构建系统具有用于列出依赖库的特殊语法,例如 LIBS 变量。在 B2 中,您只需将库添加到源列表即可。

假设我们使用以下命令构建 app

b2 app optimization=full define=USE_ASM

将使用哪些属性来构建 foo?答案是某些特性会被传播——B2 尝试使用具有相同传播特性值的依赖项。<optimization> 特性会被传播,因此 appfoo 都将以完全优化进行编译。但 <define> 不会被传播:它的值将按原样添加到 a.cpp 的编译器标志中,但不会影响 foo

让我们进一步改进这个项目。该库可能有一些头文件必须在编译 app.cpp 时使用。我们可以手动将必要的 #include 路径作为 <include> 特性的值添加到 app 需求中,但这对于所有使用 foo 的程序都将重复此工作。更好的解决方案是以这种方式修改 util/foo/Jamfile

project
    : usage-requirements <include>.
    ;

lib foo : foo.cpp ;

用法需求不应用于正在声明的目标,而是应用于其依赖项。在这种情况下,<include>. 将应用于直接依赖于 foo 的所有目标。

另一个改进是使用符号标识符来引用库,而不是 Jamfile 位置。在一个大型项目中,一个库可以被许多目标使用,如果它们都使用 Jamfile 位置,则目录组织的更改将导致大量工作。解决方案是使用项目 ID——不与目录布局绑定的符号名称。首先,我们需要通过将此代码添加到 Jamfile 来分配项目 ID

use-project /library-example/foo : util/foo ;

其次,我们修改 app/Jamfile 以使用项目 ID

exe app : app.cpp /library-example/foo//bar ;

/library-example/foo//bar 语法用于引用项目 ID 为 /library-example/foo 的项目中的目标 bar。我们已经实现了我们的目标——如果库被移动到不同的目录,则只需要修改 top/Jamfile。请注意,项目 ID 是全局的——不允许两个 Jamfile 将相同的项目 ID 分配给不同的目录。

如果您希望某个项目中的所有应用程序都链接到某个库,则可以通过使用 <library> 属性来避免直接指定每个目标的源。例如,如果 /boost/filesystem//fs 应该链接到项目中的所有应用程序,则可以将 <library>/boost/filesystem//fs 添加到项目的需求中,如下所示

project
   : requirements <library>/boost/filesystem//fs
   ;

3.5. 静态库和共享库

库可以是静态库(意味着它们包含在使用它们的exe文件中),也可以是共享库(又名动态库),这些库仅从exe文件中引用,并且必须在运行时可用。B2 可以创建和使用这两种库。

lib 目标生成的库类型由 link 特性的值决定。默认值为 shared,要构建静态库,值应为 static。您可以在命令行上请求静态构建

b2 link=static

或在库的需求中

lib l : l.cpp : <link>static ;

我们还可以使用 <link> 属性来表示每个目标的链接需求。例如,如果某个特定的可执行文件只能使用库的静态版本正确构建,我们可以将可执行文件对库的目标引用限定如下

exe important : main.cpp helpers/<link>static ;

无论在 b2 命令行上指定什么参数,important 都将仅与 helpers 的静态版本链接。

如果您使用在其他一些项目(您无法更改的项目)中定义的库,但您仍然希望在所有情况下都静态(或动态)链接到该库,则在目标引用中指定属性特别有用。如果该库被许多目标使用,您可以在任何地方使用目标引用

exe e1 : e1.cpp /other_project//bar/<link>static ;
exe e10 : e10.cpp /other_project//bar/<link>static ;

但这远非方便。更好的方法是引入一层间接性。创建一个本地 alias 目标,该目标引用 foo 的静态(或动态)版本

alias foo : /other_project//bar/<link>static ;
exe e1 : e1.cpp foo ;
exe e10 : e10.cpp foo ;

alias 规则专门用于重命名对目标的引用,并可能更改属性。

当一个库使用另一个库时,您将第二个库放在第一个库的源列表中。例如

lib utils : utils.cpp /boost/filesystem//fs ;
lib core : core.cpp utils ;
exe app : app.cpp core ;

无论使用哪种链接,这都有效。当 core 构建为共享库时,它会将 utils 直接链接到其中。静态库无法链接到其他库,因此当 core 构建为静态库时,它对 utils 的依赖项会传递给 core 的依赖项,从而导致 appcoreutils 链接。

(非 UNIX 系统注意事项)。通常,共享库必须安装到动态链接器的搜索路径中的目录中。否则,使用共享库的应用程序将无法启动。在 Windows 上,动态链接器的搜索路径由 PATH 环境变量给出。当您使用 B2 测试工具时,此限制将被解除——PATH 变量将在运行可执行文件之前自动调整。

3.6. 条件和备选项

有时,需要在目标构建属性之间维护特定的关系。例如,您可能希望在库构建为共享库时或在构建目标的 release 变体时设置特定的 #define。这可以使用条件需求来实现。

lib network : network.cpp
    : <link>shared:<define>NETWORK_LIB_SHARED
      <variant>release:<define>EXTRA_FAST
    ;

在上面的示例中,每当使用 <link>shared 构建 network 时,<define>NETWORK_LIB_SHARED 也将位于其属性中。此外,每当构建其发布变体时,<define>EXTRA_FAST 将出现在其属性中。

有时,目标的构建方式非常不同,以至于使用条件需求来描述它们会很困难。例如,假设一个库实际上根据用于构建它的工具集使用不同的源文件。我们可以使用目标备选项来表达这种情况

lib demangler : dummy_demangler.cpp ;                # (1)
lib demangler : demangler_gcc.cpp : <toolset>gcc ;   # (2)
lib demangler : demangler_msvc.cpp : <toolset>msvc ; # (3)

在构建 demangler 时,B2 会将每个备选项的需求与构建属性进行比较,以找到最佳匹配项。例如,当使用 <toolset>gcc 构建时,将选择备选项 (2),而当使用 <toolset>msvc 构建时,将选择备选项 (3)。在所有其他情况下,将构建最通用的备选项 (1)

3.7. 预构建目标

要链接到其构建指令未在 Jamfile 中给出的库,您需要使用适当的 file 属性创建 lib 目标。目标备选项可用于将多个库文件与单个概念目标关联。例如

# util/lib2/Jamfile
lib lib2
    :
    : <file>lib2_release.a <variant>release
    ;

lib lib2
    :
    : <file>lib2_debug.a <variant>debug
    ;

此示例为 lib2 定义了两个备选项,并为每个备选项命名了一个预构建文件。自然,没有源文件。相反,<file> 特性用于指定文件名。

一旦声明了预构建目标,就可以像任何其他目标一样使用它

exe app : app.cpp ../util/lib2//lib2 ;

与任何目标一样,选择的备选项取决于从 lib2 的依赖项传播的属性。如果我们构建 app 的发布和调试版本,它将分别与 lib2_release.alib2_debug.a 链接。

系统库——工具集通过搜索一组预定路径自动找到的库——应该像常规库一样声明

lib pythonlib : : <name>python22 ;

我们再次不指定任何源,但给出一个应传递给编译器的 name。如果使用 gcc 工具集将可执行目标链接到 pythonlib,则 -lpython22 将出现在命令行中(其他编译器可能会使用不同的选项)。

我们还可以指定工具集应在何处查找库

lib pythonlib : : <name>python22 <search>/opt/lib ;

当然,目标备选项可以以通常的方式使用

lib pythonlib : : <name>python22 <variant>release ;
lib pythonlib : : <name>python22_d <variant>debug ;

更高级的预构建目标用法在名为“site-config.jam 中的目标”的部分中描述。

4. 概述

本节将提供使用 B2 创建您自己的项目所需的信息。此处提供的信息相对高级,必须使用参考手册以及在线帮助系统来获取低级文档(请参阅 --help)。

B2 有两个部分——一个带有自己的解释语言的构建引擎,以及 B2 本身,用该语言实现。当您在命令行上键入 b2 时,事件链如下

  1. B2 可执行文件尝试查找 B2 模块并加载顶级模块。确切的过程在名为“初始化”的部分中描述

  2. 顶级模块加载用户定义的配置文件 user-config.jamsite-config.jam,这些文件定义了可用的工具集。

  3. 读取当前目录中的 Jamfile。这反过来可能会导致读取更多 Jamfile。结果,创建了一个项目树,项目内部包含目标。

  4. 最后,使用在命令行上指定的构建请求,B2 决定应该构建哪些目标以及如何构建。该信息被传递回 Boost.Jam,Boost.Jam 负责实际运行计划的构建操作命令。

因此,为了能够成功使用 B2,您只需要了解四件事

4.1. 概念

B2 有一些独特的概念,本节将介绍这些概念。解释这些概念的最佳方法是将其与更经典的构建工具进行比较。

当使用任何风格的 make 时,您可以直接指定目标和用于从其他目标创建它们的命令。下面的示例使用硬编码的编译器调用命令从 a.c 创建 a.o

a.o: a.c
    g++ -o a.o -g a.c

这是一种相当低级的描述机制,并且很难根据使用的编译器和操作系统调整命令、选项和创建目标的集合。

为了提高可移植性,大多数现代构建系统提供了一组更高级别的函数,这些函数可以在构建描述文件中使用。考虑这个例子

add_program ("a", "a.c")

这是一个函数调用,它创建从源文件 a.c 创建可执行文件所需的目标。根据配置的属性,可以使用不同的命令行。但是,add_program 是更高级别的,但级别相当薄。所有目标都在解析构建描述时立即创建,这使得无法执行多变体构建。通常,任何构建属性的更改都需要完全重新配置构建树。

为了支持真正的多变体构建,B2 引入了元目标的概念——一个在解析构建描述时创建的对象,并且可以在以后使用特定的构建属性调用以生成实际目标。

考虑一个例子

exe a : a.cpp ;

当解析此声明时,B2 会创建一个元目标,但尚未决定必须创建哪些文件或必须使用哪些命令。在解析所有构建文件后,B2 会考虑在命令行上请求的属性。假设您已使用以下命令调用 B2

b2 toolset=gcc toolset=msvc

在这种情况下,元目标将被调用两次,一次使用 toolset=gcc,一次使用 toolset=msvc。两次调用都将生成具体的目标,这些目标将具有不同的扩展名并使用不同的命令行。

另一个关键概念是构建属性。构建属性是影响构建过程的变量。它可以在命令行上指定,并在调用元目标时传递。虽然所有构建工具都具有类似的机制,但 B2 的不同之处在于它要求预先声明所有构建属性,并提供一组具有可移植语义的属性。

最终概念是属性传播。B2 不要求每个元目标都使用相同的属性调用。相反,“顶级”元目标使用在命令行上指定的属性调用。每个元目标都可以选择扩充或覆盖某些属性(特别是使用需求机制,请参阅名为“需求”的部分)。然后,使用修改后的属性调用依赖元目标,并生成在构建过程中使用的具体目标。当然,依赖元目标也可能反过来修改构建属性并具有自己的依赖项。

有关需求和概念的更深入处理,您可以参考 SYRCoSE 2009 B2 文章

4.2. Boost.Jam 语言

本节将介绍 Boost.Jam 语言的基础知识——仅足以编写 Jamfile。有关更多信息,请参阅 Boost.Jam 文档。

Boost.Jam 具有解释型的过程式语言。在最低级别,Boost.Jam 程序由变量和规则(Jam 术语中的函数)组成。它们被分组到模块中——有一个全局模块和许多命名模块。除此之外,Boost.Jam 程序还包含类和类实例。

从语法上讲,Boost.Jam 程序由两种元素组成——关键字(对 Boost.Jam 具有特殊含义)和字面量。考虑以下代码

a = b ;

它将值 b 分配给变量 a。在此,=; 是关键字,而 ab 是字面量。

所有语法元素,甚至关键字,都必须用空格分隔。例如,省略 ; 前面的空格字符将导致语法错误。

如果您想使用与某些关键字相同的字面量值,则可以引用该值

a = "=" ;

Boost.Jam 中的所有变量都具有相同的类型——字符串列表。要定义变量,请为其赋值,如前面的示例所示。未定义的变量与值为空的变量相同。可以使用 $(variable) 语法访问变量。例如

a = $(b) $(c) ;

规则通过指定规则名称、参数名称和每个参数允许的值列表大小来定义。

rule example
 (
     parameter1 :
     parameter2 ? :
     parameter3 + :
     parameter4 *
 )
 {
    # rule body
 }

当调用此规则时,作为第一个参数传递的列表必须恰好有一个值。作为第二个参数传递的列表可以有一个值,也可以为空。其余两个参数可以任意长,但第三个参数不能为空。

下面给出了 Boost.Jam 语言语句的概述

helper 1 : 2 : 3 ;
x = [ helper 1 : 2 : 3 ] ;

此代码使用指定的参数调用命名规则。当调用的结果必须在某些表达式内部使用时,您需要像第二行所示那样在调用周围添加括号。

if cond { statements } [ else { statements } ]

这是一个常规的 if 语句。条件由以下部分组成

  • 字面量(如果至少一个字符串不为空,则为真)

  • 比较:a operator b,其中 operator=!=<>>= 之一。比较是在左参数和右参数中的每个字符串之间成对完成的。

  • 逻辑运算:! aa && ba || b

  • 分组:( cond )

for var in list { statements }

为列表中的每个元素执行语句,并将变量 var 设置为元素值。

while cond { statements }

在 cond 在入口处保持为真时重复执行语句。

return values ;

此语句应仅在规则内部使用,并将 values 返回给规则的调用者。

import module ;
import module : rule ;

第一种形式导入指定的模块。来自该模块的所有规则都使用限定名称 module.rule 提供。第二种形式仅导入指定的规则,并且可以使用非限定名称调用它们。

有时,您需要指定在创建目标时要使用的实际命令行。在 jam 语言中,您使用命名操作来执行此操作。例如

actions create-file-from-another
{
    create-file-from-another $(<) $(>)
}

这指定了一个名为 create-file-from-another 的命名操作。大括号内的文本是要调用的命令。$(<) 变量将扩展为生成的文件列表,$(>) 变量将扩展为源文件列表。

为了灵活地调整命令行,您可以定义一个与操作同名并接受三个参数(目标、源和属性)的规则。例如

rule create-file-from-another ( targets * : sources * : properties * )
{
   if <variant>debug in $(properties)
   {
       OPTIONS on $(targets) = --debug ;
   }
}
actions create-file-from-another
{
    create-file-from-another $(OPTIONS) $(<) $(>)
}

在此示例中,该规则检查是否指定了某个构建属性。如果是,则它设置变量 OPTIONS,然后在操作内部使用该变量。请注意,设置为“在目标上”的变量仅在构建该目标的操作内部可见,而不是全局可见。如果它们是全局设置的,则在两个不相关的操作中使用名为 OPTIONS 的变量将是不可能的。

更多详细信息可以在 Jam 参考手册的名为“规则”的部分中找到。

4.3. 配置

启动时,B2 搜索并读取三个配置文件:site-config.jamuser-config.jamproject-config.jam。第一个通常由系统管理员安装和维护,第二个供用户修改。您可以编辑 B2 安装顶级目录中的文件,或者在您的主目录中创建一个副本并编辑该副本。第三个用于项目特定的配置。下表解释了在何处搜索文件。

表 1. 配置文件的搜索路径
site-config.jam user-config.jam project-config.jam

Linux

/etc

$HOME

$BOOST_BUILD_PATH

$HOME

$BOOST_BUILD_PATH

.

..

../..

Windows

%SystemRoot%

%HOMEDRIVE%%HOMEPATH%

%HOME%

%BOOST_BUILD_PATH%

%HOMEDRIVE%%HOMEPATH%

%HOME%

%BOOST_BUILD_PATH%

.

..

../..

这些文件中的任何一个也可以在命令行上被覆盖。

您可以使用 --debug-configuration 选项来查找实际加载的配置文件。

通常,user-config.jam 仅定义可用的编译器和其他工具(有关更高级的用法,请参阅名为“site-config.jam 中的目标”的部分)。工具使用以下语法配置

using tool-name : ... ;

using 规则被赋予工具的名称,并将使该工具可用于 B2。例如,

using gcc ;

将使 GCC 编译器可用。

您可以将 using <tool> ; 放在 Jamfile 中,而无需其他参数,前提是该 tool 支持此用法。在所有其他情况下,using 规则应位于配置文件中。一般原则是 Jamfile 中的描述应保持可移植性,而配置文件是系统特定的。

所有支持的工具都记录在名为“内置工具”的部分中,包括它们采用的特定选项。下面是一些适用于大多数 C++ 编译器的通用注意事项。

对于 B2 开箱即支持的所有 C++ 编译器工具集,using 的参数列表是相同的:toolset-nameversioninvocation-commandoptions

如果您只有一个编译器,并且编译器可执行文件

  • 具有其“常用名称”并且在 PATH 中,或者

  • 安装在标准的“安装目录”中,或者

  • 可以使用全局系统(如 Windows 注册表)找到。

可以通过简单地配置它

using tool-name ;

如果编译器安装在自定义目录中,您应该提供调用编译器的命令,例如

using gcc : : g++-3.2 ;
using msvc : : "Z:/Programs/Microsoft Visual Studio/vc98/bin/cl" ;

一些 B2 工具集将使用该路径来执行调用编译器之前所需的额外操作,例如调用供应商提供的脚本来设置其所需的环境变量。当 C 和 C++ 的编译器可执行文件不同时,必须指定 C++ 编译器可执行文件的路径。该命令可以是操作系统允许的任何命令。例如

using msvc : : echo Compiling && foo/bar/baz/cl ;

将会工作。

要配置工具集的多个版本,只需多次调用 using 规则即可

using gcc : 3.3 ;
using gcc : 3.4 : g++-3.4 ;
using gcc : 3.2 : g++-3.2 ;
using gcc : 5 ;
using clang : 3.9 ;
using msvc : 14.0 ;

请注意,在首次调用 using 时,将使用在 PATH 中找到的编译器,并且无需显式指定命令。

许多工具集都有一个 options 参数来微调配置。B2 的所有标准编译器工具集都接受四个选项 cflagscxxflagscompileflagslinkflags 作为 options,用于指定将始终传递给相应工具的标志。选项名称的标签和值之间不得有空格。cflags 功能的值直接传递给 C 编译器,cxxflags 功能的值直接传递给 C++ 编译器,而 compileflags 功能的值则传递给两者。例如,要配置一个 gcc 工具集,使其始终生成 64 位代码,您可以编写

using gcc : 3.4 : : <compileflags>-m64 <linkflags>-m64 ;

如果需要多个相同类型的选项,可以使用引号连接它们,或者拥有多个选项标签实例。

using gcc : 5 : : <cxxflags>"-std=c++14 -O2" ;
using clang : 3.9 : : <cxxflags>-std=c++14 <cxxflags>-O2 ;

大多数工具都可以使用同一工具的多个变体。这些变体通过传入的版本来区分。由于此处不能使用破折号 '-',因此约定使用波浪号 '~' 来分隔变体。

using gcc : 5 : g++-5 : ; # default is C++ 98
using gcc : 5~c++03 : g++-5 : <cxxflags>-std=c++03 ; # C++ 03
using gcc : 5~gnu03 : g++-5 : <cxxflags>-std=gnu++03 ; # C++ 03 with GNU
using gcc : 5~c++11 : g++-5 : <cxxflags>-std=c++11 ; # C++ 11
using gcc : 5~c++14 : g++-5 : <cxxflags>-std=c++14 ; # C++ 14

这些变体随后可以像普通工具集一样使用

b2 toolset=gcc-5 stage
b2 toolset=gcc-5~c++14 stage
尽管用于指定工具集选项的语法与用于在 Jamfiles 中指定需求的语法非常相似,但工具集选项与功能并不相同。请勿尝试在工具集初始化中指定功能值。

4.4. 调用

要调用 B2,请在命令行中键入 b2。接受三种类型的命令行标记,顺序不限

选项

选项以一个或两个破折号开头。标准选项在下面列出,每个项目都可以添加其他选项

属性

属性指定您要构建的内容的详细信息(例如,调试或发布变体)。从语法上讲,所有命令行标记中带有等号的都被视为指定属性。在最简单的形式中,属性看起来像 feature=value

目标

所有既不是选项也不是属性的标记都指定要构建的目标。可用的目标完全取决于您正在构建的项目。

4.4.1. 示例

要使用默认属性构建当前目录中 Jamfile 中定义的所有目标,请运行

b2

要构建特定目标,请在命令行中指定它们

b2 lib1 subproject//lib2

要为某些属性请求特定值,请将 property=value 添加到命令行

b2 toolset=gcc variant=debug optimization=space

4.4.2. 选项

B2 识别以下命令行选项。

--help

调用在线帮助系统。这将打印有关如何使用帮助系统以及其他 --help* 选项的常规信息。

--clean

清除当前目录和任何子项目中的所有目标。请注意,与 make 中的 clean 目标不同,您可以将 --clean 与目标名称一起使用以清除特定目标。

--clean-all

清除所有目标,无论它们在哪里定义。特别是,它将清除父 Jamfile 中的目标以及在其他项目根目录下定义的目标。

--build-dir

更改正在构建的所有项目根目录的构建目录。指定此选项后,所有 Jamroot 文件都必须声明项目名称。项目根目录的构建目录将通过连接 --build-dir 选项的值、Jamroot 中指定的项目名称以及 Jamroot 中指定的构建目录(如果未指定,则为 bin)来计算。当从只读介质构建时,此选项主要有用,此时您无法修改 Jamroot。

--abbreviate-paths

通过缩写每个组件来压缩目标路径。此选项对于防止路径变得比文件系统支持的长度更长很有用。另请参阅 名为“目标路径”的部分

--hash

使用 MD5 哈希压缩目标路径。此选项对于防止路径变得比文件系统支持的长度更长很有用。此选项生成的路径比 --abbreviate-paths 更短,但代价是使其更难以理解。另请参阅 名为“目标路径”的部分

--version

打印有关 B2 和 Boost.Jam 版本的信息。

-a

导致重建所有文件。

-n

不执行命令,仅打印它们。

-q

在第一个错误处停止,而不是继续构建不依赖于失败目标的目标。

-j N

并行运行最多 N 个命令。默认作业数是检测到的可用 CPU 线程数。注意:在某些情况下,该默认值可能大于分配的 CPU 资源,例如在某些虚拟化容器安装中。

--config=filename

覆盖所有 配置文件

--site-config=filename

覆盖默认的 site-config.jam

--user-config=filename

覆盖默认的 user-config.jam

--project-config=filename

覆盖默认的 project-config.jam

--debug-configuration

生成有关 B2 和工具集文件加载的调试信息。

--debug-building

打印正在构建的目标以及使用的属性。

--debug-generators

从生成器搜索过程生成调试输出。对于调试自定义生成器很有用。

-d0

禁止所有信息性消息。

-d N

启用从 1 到 n 的累积调试级别。值包括

  1. 显示构建目标时执行的操作(默认值)。

  2. 显示“静默”操作并显示所有操作文本,因为它们正在执行。

  3. 显示依赖性分析以及目标/源时间戳/路径。

  4. 显示 shell 调用的参数和计时。

  5. 显示规则调用和变量扩展。

  6. 显示目录/头文件/存档扫描以及绑定到目标的尝试。

  7. 显示变量设置。

  8. 显示变量获取、变量扩展以及 '"if"' 表达式的评估。

  9. 显示变量操作、扫描器标记和内存使用情况。

  10. 显示规则的配置文件信息,包括时间和内存。

  11. 显示 Jamfile 的解析进度。

  12. 显示目标依赖关系图。

  13. 显示更改目标状态(命运)。

-d +N

启用调试级别 N

-o file

将更新操作写入指定文件,而不是运行它们。

-s var=value

在 jam 语言解释器的全局作用域中将变量 var 设置为 value,覆盖从环境中导入的变量。

--command-database=format

将编译命令数据库输出为 format。目前 format 可以是:json。(有关详细信息,请参阅 命令数据库。)

--command-database-out=file

指定输出命令数据库的 file 路径。

4.4.3. 属性

在最简单的情况下,构建是使用一组属性执行的,您可以在命令行中使用 feature=value 形式的元素指定这些属性。功能的完整列表可以在 名为“内置功能”的部分 中找到。最常见的功能总结如下。

功能 允许的值 注释

variant

debug,release

link

shared,static

确定 B2 创建共享库还是静态库

threading

single,multi

使生成的二进制文件是线程安全的。这需要在源代码本身中提供适当的支持。

address-model

32,64

显式请求生成 32 位或 64 位代码。这通常需要您的编译器配置正确。如果遇到问题,请参阅 名为“C++ 编译器”的部分 和您的编译器文档。

toolset

(取决于配置)

要使用的 C++ 编译器。有关详细列表,请参阅 名为“C++ 编译器”的部分

include

(任意字符串)

C 和 C++ 编译器的其他包含路径。

define

(任意字符串)

C 和 C++ 编译器的其他宏定义。字符串应为 SYMBOLSYMBOL=VALUE

cxxflags

(任意字符串)

传递给 C++ 编译器的自定义选项。

cflags

(任意字符串)

传递给 C 编译器的自定义选项。

linkflags

(任意字符串)

传递给 C++ 链接器的自定义选项。

runtime-link

shared,static

确定应使用 C 和 C++ 运行时的共享版本还是静态版本。

如果您有多个版本的给定 C++ 工具集(例如,在 user-config.jam 中配置,或自动检测到,例如 msvc),您可以通过将 toolset-version 作为 toolset 功能的值来请求特定版本,例如 toolset=msvc-8.0

如果一个功能具有固定的值集,则可以在命令行上多次指定它。在这种情况下,所有内容将被构建多次 — 功能的每个指定值构建一次。例如,如果您使用

b2 link=static link=shared threading=single threading=multi

然后将执行总共 4 次构建。为了方便起见,您可以将功能的请求值用逗号分隔,而不是在单独的命令行元素中指定所有请求值,例如

b2 link=static,shared threading=single,multi

逗号仅在功能具有固定的值集时才具有这种特殊含义,因此

b2 include=static,shared

不会被特殊对待。

可以使用正斜杠对多个功能进行分组。

b2 gcc/link=shared msvc/link=static,shared

这将总共构建 3 种不同的变体。

4.4.4. 目标

所有既不是选项也不是属性的命令行元素都是要构建的目标的名称。请参阅 名为“目标标识符和引用”的部分。如果未指定目标,则构建当前目录中的项目。

4.5. 声明目标

主目标是一个用户定义的命名实体,可以构建,例如可执行文件。声明主目标通常使用 名为“内置规则”的部分 中描述的主目标规则之一来完成。用户还可以声明自定义主目标规则,如 名为“主目标规则”的部分 中所示。

B2 中的大多数主目标规则都具有相同的通用签名

rule rule-name (
     main-target-name :
     sources + :
     requirements * :
     default-build * :
     usage-requirements * )
  • main-target-name 是用于在命令行上请求目标以及从其他主目标中使用它的名称。主目标名称可以包含字母数字字符、破折号 ('-') 和下划线 ('_')。

  • sources 是源文件和其他必须组合的主目标的列表。

  • requirements 是在构建此主目标时必须始终存在的属性列表。

  • default-build 是将使用的属性列表,除非同一功能的其他值已指定,例如在命令行上或通过从依赖目标传播。

  • usage-requirements 是将传播到所有使用此主目标的主目标的属性列表,即传播到其所有依赖项。

某些主目标规则具有不同的参数列表,这在其文档中明确说明。

目标的实际需求是通过使用显式指定的需求来细化声明目标的项目的需求而获得的。使用需求也是如此。更多详细信息可以在 名为“属性细化”的部分 中找到。

4.5.1. 名称

主目标的名称有两个用途。首先,它用于从其他目标和命令行引用此目标。其次,它用于计算生成文件的名称。通常,文件名是通过在主目标名称后附加系统相关的后缀和前缀来获得的。

主目标的名称可以包含字母数字字符、破折号、下划线和点。当解析来自其他目标的引用时,整个名称都很重要。对于确定文件名,仅采用第一个点之前的部分。例如

obj test.release : test.cpp : <variant>release ;
obj test.debug : test.cpp : <variant>debug ;

将生成两个名为 test.obj 的文件(在两个不同的目录中),而不是两个名为 test.release.objtest.debug.obj 的文件。

4.5.2. 源

源列表指定应处理哪些内容以获取结果目标。大多数时候,它只是一个文件列表。有时,您可能希望自动构造源文件列表,而不是手动拼写出来,在这种情况下,您可以使用 glob 规则。以下是两个示例

exe a : a.cpp ; (1)
exe b : [ glob *.cpp ] ; (2)
  1. a.cpp 是唯一的源文件

  2. 此目录中的所有 .cpp 文件都是源文件

除非您使用绝对路径指定文件,否则该名称被认为是相对于源目录的 — 源目录通常是 Jamfile 所在的目录,但可以按照 名为“项目”的部分 中的描述进行更改。

源列表还可以引用其他主目标。同一项目中的目标可以通过名称引用,而其他项目中的目标必须使用目录或符号项目名称进行限定。目录/项目名称与目标名称之间用双正斜杠分隔。没有特殊的语法来区分目录名称和项目名称 — 双正斜杠之前的部分首先被查找为项目名称,然后作为目录名称。例如

lib helper : helper.cpp ;
exe a : a.cpp helper ;
exe b : b.cpp ..//utils ; (1)
exe c : c.cpp /boost/program_options//program_options ;
  1. 由于所有项目 ID 都以斜杠开头,“..” 是目录名称。

第一个 exe 使用在同一项目中定义的库。第二个 exe 使用由高一级别的 Jamfile 定义的某个目标(很可能是一个库)。最后,第三个目标使用 C++ Boost 库,使用其绝对符号名称引用它。有关目标引用的更多信息,可以在 名为“依赖目标”的部分名为“目标标识符和引用”的部分 中找到。

4.5.3. 需求

需求是在构建目标时应始终存在的属性。通常,它们是 includes 和 defines

exe hello : hello.cpp : <include>/opt/boost <define>MY_DEBUG ;

还有许多其他功能,在 名为“内置功能”的部分 中列出。例如,如果库只能静态构建,或者由于编译器错误而无法使用优化编译文件,则可以使用。

lib util : util.cpp : <link>static ;
obj main : main.cpp : <optimization>off ;

有时,需要在目标构建属性之间维护特定的关系。这可以通过条件需求来实现。例如,您可能希望在库构建为共享库时,或者在目标的 release 变体在发布模式下构建时,设置特定的 #defines

lib network : network.cpp
    : <link>shared:<define>NETWORK_LIB_SHARED
     <variant>release:<define>EXTRA_FAST
    ;

在上面的示例中,每当使用 <link>shared 构建 network 时,<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 ;

此语法是忽略父级自由属性(例如 defines)的唯一方法。它对于普通属性也很有用。考虑以下示例

project test : requirements <threading>multi ;
exe test1 : test1.cpp ;
exe test2 : test2.cpp : <threading>single ;
exe test3 : test3.cpp : -<threading>multi ;

在此处,test1 继承了项目需求,并且始终在多线程模式下构建。test2 目标覆盖了项目需求,并且始终在单线程模式下构建。相比之下,test3 目标从项目需求中删除了一个属性,并将根据用户请求的变体在单线程或多线程模式下构建。

请注意,需求的删除是完全文本的:您需要指定完全相同的属性才能删除它。

4.5.4. 默认构建

如果构建请求未以其他方式指定集合中功能的值,则 default-build 参数是要使用的一组属性。例如

exe hello : hello.cpp : : <threading>multi ;

将构建一个多线程目标,除非用户显式请求单线程版本。需求和 default-build 之间的区别在于需求无法以任何方式覆盖。

4.5.5. 附加信息

目标的构建方式可能非常不同,以至于使用条件需求描述它们会很困难。例如,假设一个库实际上使用不同的源文件,具体取决于用于构建它的工具集。我们可以使用目标备选项来表达这种情况

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

在上面的示例中,当使用 gccmsvc 构建时,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 ;

4.6. 项目

如前所述,目标被分组到项目中,每个 Jamfile 都是一个单独的项目。项目很有用,因为它们允许我们将相关目标组合在一起,定义所有这些目标通用的属性,并为项目分配一个符号名称,该名称可以在引用其目标时使用。

项目使用 project 规则命名,该规则具有以下语法

project id : attributes ;

在此,attributes 是规则参数的序列,每个参数都以属性名称开头,后跟任意数量的构建属性。属性名称列表及其处理方式也在下表中显示。例如,可以编写

project tennis
    : requirements <threading>multi
    : default-build release
    ;

可能的属性列在下面。

项目 ID 是一种表示项目的简短方式,与 Jamfile 的路径名相反。它是一个分层路径,与文件系统无关,例如 "boost/thread"。 目标引用 使用项目 ID 来指定目标。

源位置指定项目源文件所在的目录。

项目需求是适用于项目中所有目标以及所有子项目的需求。

默认构建是在未显式指定构建请求时应使用的构建请求。

这些属性的默认值在下表中给出。

属性 名称 默认值 project 规则的处理方式

项目 ID

从 'project' 规则的第一个参数分配。它被假定为表示绝对项目 ID。

源位置

source-location

项目 Jamfile 的位置

设置为传递的值

需求

requirements

父级需求

父级需求使用传递的需求进行细化,结果用作项目需求。

默认构建

default-build

设置为传递的值

构建目录

build-dir

如果父级未设置构建目录,则为空。否则,父级的构建目录加上从父级到当前项目的相对路径附加到其后。

设置为传递的值,解释为相对于项目位置。

除了定义项目和主目标之外,Jamfile 通常还调用各种实用程序规则。有关可以直接在 Jamfile 中使用的规则的完整列表,请参阅 名为“内置规则”的部分

每个子项目都从其父项目继承属性、常量和规则,父项目由子项目上方祖先目录中最近的 Jamfile 定义。顶级项目在名为 JamrootJamfile 的文件中声明。加载项目时,B2 会查找 JamrootJamfile。它们以相同的方式处理,但如果文件名为 Jamroot,则不会执行父项目的搜索。没有父项目的 Jamfile 也被视为顶级项目。

即使在子项目目录中构建,父项目文件也始终在其子项目文件之前加载,以便父项目中进行的每个定义始终对其子项可用。任何其他项目的加载顺序都未指定。即使一个项目通过 use-project 或目标引用引用另一个项目,也不应假定任何特定顺序。

为根项目提供特殊名称“Jamroot”可确保 B2 不会因为目录包含 Jamfile 而将上方的目录误解为项目根目录。

4.7. 构建过程

当您描述了您的目标后,您希望 B2 运行正确的工具并创建所需的目标。本节将描述两件事:如何指定要构建的内容,以及主目标实际上是如何构建的。

需要注意的最重要的一点是,在 B2 中,与其他构建工具不同,您声明的目标不对应于特定的文件。您在 Jamfile 中声明的内容更像是“元目标”。根据您在命令行上指定的属性,每个元目标将生成一组与请求的属性相对应的实际目标。同一个元目标很可能使用不同的属性构建多次,从而生成不同的文件。

这意味着对于 B2,您无法直接从 Jamfile 获取构建变体。用户可能请求了多个变体,并且每个目标都可以使用不同的属性构建。

4.7.1. 构建请求

命令行指定要构建的目标以及使用的属性。例如

b2 app1 lib1//lib1 toolset=gcc variant=debug optimization=full

将使用指定的属性构建两个目标 "app1" 和 "lib1//lib1"。您可以使用 目标 ID 引用任何目标,并指定任意属性。某些属性非常常见,对于这些属性,可以省略属性的名称。例如,以上可以写成

b2 app1 lib1//lib1 gcc debug optimization=full

完整的语法(其中包含一些其他快捷方式)在 名为“调用”的部分 中描述。

4.7.2. 构建主目标

当您直接或间接地请求使用特定需求构建主目标时,将执行以下步骤。提供了一些简要说明,更多详细信息在 名为“构建过程”的部分 中给出。

  1. 应用默认构建。如果目标的 default-build 属性指定了构建请求中不存在的功能的值,则会添加该值。

  2. 选择要使用的主目标备选项。对于每个备选项,我们查看有多少属性同时存在于备选项的需求和构建请求中。选择具有最多匹配属性的备选项。

  3. 确定“公共”属性。构建请求使用目标的需求进行 细化。需求中的条件属性也会得到处理。最后,添加功能的默认值。

  4. 构建源列表和依赖属性引用的目标。源列表和属性可以使用 目标引用 引用其他目标。对于每个引用,我们获取所有 传播的 属性,通过目标引用中显式指定的属性对其进行细化,并将生成的属性作为构建请求传递给其他目标。

  5. 将构建依赖项时生成的使用需求添加到“公共”属性。当在上一步中构建依赖项时,它们会返回创建的“实际”目标集和使用需求。使用需求被添加到公共属性中,生成的属性集将用于构建当前目标。

  6. 使用生成器构建目标。为了将源转换为所需的类型,B2 使用“生成器” — 与编译器和链接器等工具相对应的对象。每个生成器声明它可以生成的目标类型以及它需要的源类型。使用此信息,B2 确定必须运行哪些生成器才能从特定源生成特定目标。当生成器运行时,它们会返回“实际”目标。

  7. 计算要返回的使用需求。使用需求中的条件属性被扩展,并返回结果。

4.7.3. 构建项目

通常,用户构建的是完整项目,而不仅仅是一个主目标。实际上,调用不带参数的 b2 会构建当前目录中定义的项目。

当构建项目时,构建请求将未经修改地传递给该项目中的所有主目标。可以使用 explicit 规则来防止隐式构建项目中的目标

explicit hello_test ;

将导致 hello_test 目标仅在用户或某些其他目标显式请求时才构建。

项目的 Jamfile 可以包含许多 build-project 规则调用,这些调用指定要构建的其他项目。

5. 常见任务

本节介绍 B2 开箱即用的主要目标类型。除非另有说明,否则所有提到的主目标规则都具有 名为“声明目标”的部分 中描述的通用签名。

5.1. 程序

程序使用 exe 规则创建,该规则遵循通用语法。例如

exe hello
    : hello.cpp some_library.lib /some_project//library
    : <threading>multi
    ;

这将从源文件创建可执行文件——在本例中,一个 C++ 文件,一个位于同一目录中的库文件,以及另一个由 B2 创建的库。通常,源文件可以包括 C 和 C++ 文件、目标文件和库。B2 将自动尝试转换其他类型的目标。

在 Windows 上,如果应用程序使用共享库,并且应用程序和库都是使用 B2 构建的,则无法立即运行该应用程序,因为 PATH 环境变量应包含库的路径。这意味着您必须手动添加路径,或者让构建过程将应用程序和库放置到同一目录中。请参阅名为“安装”的章节

5.2. 库

库目标使用 lib 规则创建,该规则遵循通用语法。例如

lib helpers : helpers.cpp ;

这将定义一个名为 helpers 的库目标,该目标由 helpers.cpp 源文件构建。它可以是静态库或共享库,具体取决于 <link> 功能的值。

库目标可以表示

  • 应从源代码构建的库,如以上示例所示。

  • 系统上已存在的预构建库。工具可以使用这些库进行搜索(通常使用链接器的 -l 选项),或者构建系统可以预先知道它们的路径。

预构建库的语法如下

lib z : : <name>z <search>/home/ghost ;
lib compress : : <file>/opt/libs/compress.a ;

name 属性指定库的名称,不带标准前缀和后缀。例如,根据系统,z 可以指名为 z.solibz.az.lib 等的文件。search 功能指定除了默认编译器路径之外,还可以在哪些路径中搜索库。search 可以指定多次,也可以省略,在这种情况下,只会搜索默认编译器路径。file 属性指定文件位置。

使用 file 功能与组合使用 namesearch 功能之间的区别在于 file 更精确。

search 功能的值只是添加到链接器搜索路径中。当链接到多个库时,search 指定的路径会被合并,而无需考虑每个路径来自哪个 lib 目标。因此,给定

lib a : : <name>a <search>/pool/release ;
lib b : : <name>b <search>/pool/debug ;

如果 /pool/release/a.so、/pool/release/b.so、/pool/debug/a.so 和 /pool/release/b.so 都存在,则链接器可能会从同一目录中获取 ab,而不是在 /pool/release 中找到 a,在 /pool/debug 中找到 b。如果您需要在同名的多个库之间进行区分,则使用 file 更安全。

为了方便起见,允许以下语法

lib z ;
lib gui db aux ;

它具有与以下代码完全相同的效果

lib z : : <name>z ;
lib gui : : <name>gui ;
lib db : : <name>db ;
lib aux : : <name>aux ;

当一个库引用另一个库时,您应该将另一个库放在其源文件列表中。在所有情况下,这都会做正确的事情。为了保证可移植性,即使对于搜索到的和预构建的库,您也应该指定库依赖项,否则,Unix 上的静态链接将无法工作。例如

lib z ;
lib png : z : <name>png ;

当一个库以共享库作为源文件,或者一个静态库以另一个静态库作为源文件时,那么任何链接到第一个库的目标也会自动链接到其源库。

另一方面,当共享库以静态库作为源文件时,将构建第一个库,使其完全包含第二个库。

如果您不希望共享库包含其源文件中指定的所有库(尤其是静态链接的库),则需要使用以下代码

lib b : a.cpp ;
lib a : a.cpp : <use>b : : <library>b ;

这指定库 a 使用库 b,并导致所有链接到 a 的可执行文件也链接到 b。在这种情况下,即使对于共享链接,a 库也不会引用 b

用法要求对于定义库目标通常非常有用。例如,假设您要构建一个 helpers 库,并且其接口在其 helpers.hpp 头文件中描述,该文件与 helpers.cpp 源文件位于同一目录中。然后,您可以将以下内容添加到位于同一目录中的 Jamfile 中

lib helpers : helpers.cpp : : : <include>. ;

这将自动将定义目标所在的目录(以及库的头文件所在的目录)添加到使用 helpers 库的所有目标的编译器包含路径中。此功能大大简化了 Jamfile。

5.3. 别名

alias 规则为一组目标提供备用名称。例如,要使用以下代码为一组其他三个目标命名为 core

alias core : im reader writer ;

在命令行上或任何其他目标的源文件列表中使用 core 与显式使用 imreaderwriter 相同。

alias 规则的另一个用途是更改构建属性。例如,如果您想静态链接到 Boost Threads 库,您可以编写以下代码

alias threads : /boost/thread//boost_thread : <link>static ;

并在您的 Jamfile 中仅使用 threads 别名。

您还可以为 alias 目标指定用法要求。如果您编写以下代码

alias header_only_library : : : :  <include>/usr/include/header_only_library ;

那么在源文件中使用 header_only_library 将仅添加包含路径。另请注意,当别名具有源文件时,它们的用法要求也会被传播。例如

lib library1 : library1.cpp : : : <include>/library/include1 ;
lib library2 : library2.cpp : : : <include>/library/include2 ;
alias static_libraries : library1 library2 : <link>static ;
exe main : main.cpp static_libraries ;

将使用使用指定的静态库所需的其他包含项来编译 main.cpp

5.4. 安装

本节介绍安装构建目标和任意文件的各种方法。

5.4.1. 基本安装

要安装构建的目标,您应该使用 install 规则,该规则遵循通用语法。例如

install dist : hello helpers ;

将导致目标 hellohelpers 移动到相对于 Jamfile 目录的 dist 目录。可以使用 location 属性更改目录

install dist : hello helpers : <location>/usr/bin ;

虽然您可以通过将目标名称更改为 /usr/bin 来达到相同的效果,但使用 location 属性更好,因为它允许您使用助记符目标名称。

当位置不固定,而是取决于构建变体或环境变量时,location 属性特别方便

install dist : hello helpers :
    <variant>release:<location>dist/release
    <variant>debug:<location>dist/debug ;
install dist2 : hello helpers : <location>$(DIST) ;

另请参阅条件属性环境变量

5.4.2. 安装所有依赖项

指定要安装的所有库的名称可能很乏味。install 允许您仅指定要安装的顶级可执行目标,并自动安装所有依赖项

install dist : hello :
    <install-dependencies>on <install-type>EXE
    <install-type>LIB
    ;

将查找 hello 依赖的所有目标,并安装所有可执行文件或库。更具体地说,对于每个目标,将递归查找指定为源文件或依赖项属性的其他目标。一个例外是使用 use 功能引用的目标不被考虑,因为该功能通常用于引用仅包含头文件的库。如果指定了目标类型集,则仅安装该类型的目标,否则,将安装所有找到的目标。

5.4.3. 保留目录层次结构

默认情况下,install 规则将从其源文件中剥离路径。因此,如果源文件包含 a/b/c.hpp,则 a/b 部分将被忽略。要使 install 规则保留目录层次结构,您需要使用 <install-source-root> 功能来指定要安装的层次结构的根目录。将保留来自该根目录的相对路径。例如,如果您编写

install headers
    : a/b/c.h
    : <location>/tmp <install-source-root>a
    ;

将创建一个名为 /tmp/b/c.h 的文件。

glob-tree 规则可用于查找给定目录下的所有文件,从而轻松安装整个目录树。

5.4.4. 安装到多个目录

当需要将目标安装到多个目录时,可以使用 alias 规则

alias install : install-bin install-lib ;
install install-bin : applications : /usr/bin ;
install install-lib : helper : /usr/lib ;

由于 install 规则仅复制目标,因此大多数自由功能 [3]install 规则的要求中使用时无效。 唯一两个重要的功能是 dependency 和 Unix 上的 dll-path

(Unix 特有)在 Unix 上,使用 B2 构建的可执行文件通常包含所有使用的共享库的路径列表。对于安装,这是不需要的,因此 B2 使用空路径列表重新链接可执行文件。您还可以使用 dll-path 功能为已安装的可执行文件指定其他路径。

5.5. 测试

B2 方便地支持运行单元测试。最简单的方法是 unit-test 规则,该规则遵循通用语法。例如

unit-test helpers_test : helpers_test.cpp helpers ;

unit-test 规则的行为类似于 exe 规则,但在创建可执行文件后,它也会运行。如果可执行文件返回错误代码,则构建系统也会返回错误,并将尝试在下次调用时运行可执行文件,直到它成功运行为止。此行为确保您不会错过单元测试失败。

下面列出了一些专门的测试规则

rule compile ( sources : requirements * : target-name ? )
rule compile-fail ( sources : requirements * : target-name ? )
rule link ( sources + : requirements * : target-name ? )
rule link-fail ( sources + : requirements * : target-name ? )

它们被赋予源文件和要求列表。如果未提供目标名称,则使用第一个源文件的名称代替。compile* 测试尝试编译传递的源文件。link* 规则尝试从所有传递的源文件编译和链接应用程序。compilelink 规则期望编译/链接成功。compile-faillink-fail 规则期望编译/链接失败。

有两个用于运行可执行文件的专门规则,它们比 unit-test 规则更强大。run 规则具有以下签名

rule run ( sources + : args * : input-files * : requirements * : target-name ?
    : default-build * )

该规则从提供的源文件构建应用程序并运行它,将 argsinput-files 作为命令行参数传递。args 参数按字面传递,input-files 参数的值被视为相对于包含 Jamfile 的路径,并且如果从不同的目录调用 b2,则会进行调整。run-fail 规则与 run 规则相同,只是它期望运行失败。

本节中描述的所有规则,如果成功执行,都会创建一个特殊的清单文件,以指示测试已通过。对于 unit-test 规则,文件名为 target-name.passed,对于其他规则,则称为 target-name.testrun* 规则还会捕获程序的所有输出,并将其存储在名为 target-name.output 的文件中。

如果 preserve-test-targets 功能的值为 off,则 runrun-fail 规则将在运行后删除可执行文件。这在一定程度上减少了持续测试环境的磁盘空间需求。preserve-test-targets 功能的默认值为 on

可以通过传递 --dump-tests 命令行选项来打印项目中声明的所有测试目标(unit-test 除外)的列表。输出将由以下形式的行组成

boost-test(test-type) path : sources

可以将测试列表、B2 输出以及测试通过时创建的 *.test 文件的存在/不存在处理为人类可读的测试状态表。此类处理实用程序不包含在 B2 中。

以下功能调整了测试元目标的行为。

testing.arg

定义在执行目标时,在输入文件列表之前传递给目标的参数。

unit-test helpers_test
    : helpers_test.cpp helpers
    : <testing.arg>"--foo bar"
    ;
testing.input-file

指定要在命令行上在参数之后传递给可执行文件的文件。由于当前实现中的约束,所有文件都必须按字母顺序指定。

testing.launcher

默认情况下,可执行文件直接运行。有时,希望使用一些辅助命令来运行可执行文件。您应该使用此属性来指定辅助命令的名称。例如,如果您编写

unit-test helpers_test
    : helpers_test.cpp helpers
    : <testing.launcher>valgrind
    ;

用于运行可执行文件的命令将是

valgrind bin/$toolset/debug/helpers_test
test-info

测试的描述。这会显示为 --dump-tests 命令行选项的一部分。

5.6. 自定义命令

对于大多数主要目标规则,B2 会自动计算出要运行的命令。当您想使用新的文件类型或支持新的工具时,一种方法是扩展 B2 以平滑地支持它们,如扩展器手册中所述。但是,如果新工具仅在单个位置使用,则显式指定要运行的命令可能更容易。

可以使用三个主要目标规则来实现这一点。make 规则允许您通过运行您指定的命令,从任意数量的源文件构建单个文件。notfile 规则允许您运行任意命令,而无需创建任何文件。最后,generate 规则允许您使用 B2 的虚拟目标来描述转换。这比 make 规则操作的文件名更高级,并且允许您创建多个目标,根据属性创建不同命名的目标,或使用多个工具。

当您想使用某些特定命令从多个源文件创建一个文件时,可以使用 make 规则。notfile 用于无条件运行命令。

假设您想通过运行命令 in2out 从文件 file.in 创建文件 file.out。以下是在 B2 中执行此操作的方法

make file.out : file.in : @in2out ;
actions in2out
{
    in2out $(<) $(>)
}

如果您运行 b2 并且 file.out 不存在,则 B2 将运行 in2out 命令来创建该文件。有关指定操作的更多详细信息,请参阅名为“Boost.Jam 语言”的章节

可能是您只想无条件地运行某些命令,并且该命令不会创建任何特定文件。为此,您可以使用 notfile 规则。例如

notfile echo_something : @echo ;
actions echo
{
    echo "something"
}

make 规则的唯一区别在于,目标的名称不被视为文件名,因此 B2 将无条件地运行该操作。

当您想使用 B2 的虚拟目标而不是仅使用文件名来表达转换时,可以使用 generate 规则。generate 规则具有标准的主要目标规则签名,但您需要指定 generating-rule 属性。属性的值应为 @rule-name 形式,命名规则应具有以下签名

rule generating-rule ( project name : property-set : sources * )

并将使用 project-target 类的实例、主要目标的名称、包含构建属性的 property-set 类的实例以及与源文件对应的 virtual-target 类的实例列表来调用。该规则必须返回 virtual-target 实例的列表。可以通过查看 build/virtual-target.jam 文件来了解 virtual-target 类的接口。B2 发行版中包含的 generate 示例说明了如何使用 generate 规则。

5.7. 预编译头文件

预编译头文件是一种通过创建某些头文件的部分处理版本来加速编译的机制,然后在编译期间使用该版本,而不是重复解析原始头文件。B2 支持 gcc 和 msvc 工具集的预编译头文件。

要使用预编译头文件,请按照以下步骤操作

  1. 创建一个头文件,其中包含您的项目使用的您想要预编译的头文件。最好仅包含足够稳定的头文件——例如来自编译器和外部库的头文件。B2 将自动按需包含该头文件。

  2. 为预编译头文件声明一个新的 B2 目标,并将该预编译头文件添加到您要加速编译的目标的源文件中

    cpp-pch pch : pch.hpp ;
    exe main : main.cpp pch ;

    如果您想在 C 程序中使用预编译头文件,可以使用 c-pch 规则。

B2 发行版中的 pch 示例可以用作参考。

请注意以下事项

  • 用于编译源文件和预编译头文件的构建属性必须相同。考虑使用项目要求来确保这一点。

  • 预编译头文件必须纯粹用作提高编译时间的一种方法,而不是为了节省 #include 语句的数量。如果源文件需要包含某个头文件,请在源文件中显式包含它,即使同一个头文件是从预编译头文件中包含的也是如此。这确保了即使不支持预编译头文件,您的项目也可以构建。

  • 在 4.2 版本之前,gcc 编译器不允许在预编译头文件中使用匿名命名空间,这限制了它们的实用性。有关详细信息,请参阅 错误报告

  • 以前,B2 没有自动包含头文件,用户需要在将要使用预编译头文件的每个源文件的顶部包含该头文件。

5.8. 生成的头文件

通常,B2 完全自动处理隐式依赖项。例如,对于 C++ 文件,所有 #include 语句都会被找到并处理。用户可能需要帮助的唯一方面是对生成文件的隐式依赖项。

默认情况下,B2 在一个主要目标内处理此类依赖项。例如,假设主要目标“app”有两个源文件“app.cpp”和“parser.y”。后一个源文件被转换为“parser.c”和“parser.h”。然后,如果“app.cpp”包含“parser.h”,B2 将检测到此依赖项。此外,由于“parser.h”将生成到构建目录中,因此该目录的路径将自动添加到包含路径中。

使此机制跨主要目标边界工作是可能的,但这会带来一定的开销。因此,如果存在对来自其他主要文件的隐式依赖项,则必须使用 <implicit-dependency> 功能,例如

lib parser : parser.y ;
exe app : app.cpp : <implicit-dependency>parser ;

上面的示例告诉构建系统,在扫描“app”的所有源文件以查找隐式依赖项时,应将“parser”中的目标视为潜在依赖项。

5.9. 交叉编译

B2 支持使用 gcc 和 msvc 工具集进行交叉编译。

使用 gcc 时,您首先需要在 user-config.jam 中指定您的交叉编译器(请参阅名为“配置”的章节),例如

using gcc : arm : arm-none-linux-gnueabi-g++ ;

之后,如果主机和目标操作系统相同,例如 Linux,您只需请求使用此编译器版本即可

b2 toolset=gcc-arm

如果您想以与主机不同的操作系统为目标,则需要额外指定 target-os 功能的值,例如

# On windows box
b2 toolset=gcc-arm target-os=linux
# On Linux box
b2 toolset=gcc-mingw target-os=windows

有关允许的操作系统名称的完整列表,请参阅 target-os 功能的文档。

使用 msvc 编译器时,只能在 32 位主机上交叉编译到 64 位系统。有关详细信息,请参阅名为“64 位支持”的章节

5.10. 包管理器

B2 支持从包管理器自动或手动加载生成的构建文件。例如,使用 Conan 包管理器,它会生成 conanbuildinfo.jam 文件,B2 将在加载同一位置的项目时自动加载这些文件。包含的文件可以在加载到项目中的上下文中定义目标和其他项目声明。对加载哪个包管理器文件的控制可以通过以下方式进行控制(按优先级顺序)

  • 使用 use-packages 规则。

  • 命令行参数 --use-package-manager=X

  • 环境变量 PACKAGE_MANAGER_BUILD_INFO

  • 内置的文件检测。目前包括:“conan”。

use-packages 规则

rule use-packages ( name-or-glob-pattern ? )

use-packages 规则允许在项目本身中指定要使用的包定义类型,既可以作为内置包管理器支持的类型,也可以作为其他类型。例如

use-packages conan ;

或者指定 glob 模式来查找包含定义的文件。例如

use-packages "packages.jam" ;

--use-package-manager 命令行选项

--use-package-manager=NAME 命令行选项允许以非侵入方式指定每次调用要使用的内置包管理器类型。

PACKAGE_MANAGER_BUILD_INFO 变量

PACKAGE_MANAGER_BUILD_INFO 变量,从环境变量中获取或使用 -sX=Y 选项定义,指定用于查找包定义的 glob 模式。

内置检测

有许多内置的 glob 模式来支持流行的包管理器。目前支持的有

5.11. 搜索项目

B2 支持自动搜索引用的全局项目。例如,如果您引用了 /boost/predef 并进行了一些最小配置,B2 可以找到它的 B2 项目并自动解析引用。搜索支持两种模式:查找常规 B2 项目目录,以及包/配置样式加载单个 jam 文件。

5.11.1. 搜索路径

要控制查找哪些项目以及在何处查找项目,可以使用不同的方法

  • B2_PROJECT_PATH 环境变量。

  • --project-search 命令行参数。

  • rule project-search 项目规则。

B2_PROJECT_PATH--project-search 中的搜索路径都指定了 project-idpath 的键值列表。顾名思义,该键值列表的各个部分是以路径分隔符分隔的列表。例如,如果我们有两个想要查找的项目:/zlib/boost/asio,则搜索路径可能如下所示

Linux

/zlib:/usr/local/share/zlib:/boost/asio:/home/user/external/boost-1.81/libs/asio

Windows、VxWorks

/zlib;C:/Dev/zlib;/boost/asio;C:Dev/boost-1.81/libs/asio

VMS

/zlib,X:external.zlib,/boost/asio,X:external.boost181.libs.asio

搜索路径规范中的 project-id 将该项目根目录映射到指示的 path。B2 将使用该路径搜索具有该 project-id 根目录的任何项目和子项目。

5.11.2. 搜索过程

无论搜索路径是如何指定的,搜索方式都是相同的。搜索包括搜索 B2 项目目录(即包含 jamfile 的目录),或搜索要包含的特殊命名的 *.jam 文件(类似于 包管理器支持包含 jam 文件的方式)。

对于给定形式为 /d1/d2/../dnproject-id,我们按以下顺序搜索

  1. 在为 / 根目录注册的任何路径中,d1/d2/../dn 处的项目。

  2. 在为 /d1/d2/../dn-1 根目录注册的任何路径中,dn 处的项目。

  3. 在为 /d1/d2/../dn-1 根目录注册的任何路径中,jamfile dn.jam

  4. 在为 /d1/d2/../dn-2 根目录注册的任何路径中,dn-1_dn 处的项目。

  5. 在为 /d1/d2/../dn-2 根目录注册的任何路径中,jamfile dn-1_dn.jam

  6. 依此类推,直到在为 / 根目录注册的任何路径中搜索项目 d1_d2_.._dn

  7. 并在为 / 根目录注册的任何路径中搜索 jamfile d1_d2_.._dn.jam

例如,使用以下搜索路径

  • /boost: /usr/share/boost-1.81.0, /home/user/boost-dev/libs

  • /: /usr/share/b2/external

给定要解析的 /boost/core project-id,我们搜索

  1. /usr/share/b2/external/boost/core/<jamfile>

  2. /usr/share/boost-1.81.0/core/<jamfile>

  3. /home/user/boost-dev/libs/core/<jamfile>

  4. /usr/share/boost-1.81.0/core.jam

  5. /home/user/boost-dev/libs/core.jam

  6. /usr/share/boost-1.81.0/boost_core/<jamfile>

  7. /home/user/boost-dev/libs/boost_core/<jamfile>

  8. /usr/share/boost-1.81.0/boost_core.jam

  9. /home/user/boost-dev/libs/boost_core.jam

  10. /usr/share/b2/external/boost_core.jam

第一个项目 jamfile 将分配给 project-id。或者将加载找到的第一个 *.jam 文件。

5.11.3. 加载过程

项目加载方式取决于项目 jamfile 还是 *.jam 文件。

加载具有 project-idpath 的项目 jamfile 等同于从具有引用的项目的上下文中调用 use-project project-id : path ;

*.jam 文件作为 path 加载等同于从具有引用的项目的上下文中调用:use-packages path ;。在这种情况下,这意味着该文件将作为引用项目的一部分加载,因此它声明的任何裸目标或信息都将成为项目的一部分。

5.12. 命令数据库和 IDE 集成

许多 IDE 程序接受使用 compile_commands.json 文件来了解项目的构建方式和内容。B2 支持为您进行的任何构建生成此类文件。B2 通过通用设施来支持这一点,该设施从它执行的操作中提取命令。有两个选项可以控制这一点。--command-database=format 选项指示为给定的 format 生成文件。指定后,它具有以下几个效果

  • 它告诉 B2 开始观察和从操作中提取命令(由工具集指定)。

  • 它禁用操作的执行。即,等效于添加 -n 选项。

  • 它启用构建所有默认和指定的目标。即,等效于添加 -a 选项。

  • 它禁用所有操作执行输出。即,就像指定 -d0 选项一样。

  • 在主构建结束时,它将观察到的结果写入数据库文件。

目前,json 是唯一支持的格式,它遵循 Clang JSON 编译数据库格式规范

--command-database-out=file 选项控制生成文件的名称以及可选的位置。默认情况下,filecompile_commands.json,以遵循生态系统约定。默认情况下,它在以下位置之一生成

  • 相对于根项目的 build-dir,如果它由项目指定。使用默认的 file 名称或给定的名称。

  • 在绝对 file 路径(如果它是根路径)。

  • 当前工作目录

以下字段在生成的数据库中填充

  • directory - 这将始终是当前目录,因为 B2 使所有路径都相对于该目录(或绝对路径)。

  • file - 记录的每个操作的第一个源文件。

  • command - 工具集提取的带引号的完整命令。

  • output - 记录的每个操作的第一个目标文件。由于 B2 可以一次构建多个变体,因此需要区分同一源文件的多个编译。

每次 b2 调用仅生成一个命令数据库文件。每次生成时,它都会覆盖任何以前的此类文件。

6. 参考

6.1. 通用信息

6.1.1. 初始化

启动后,B2 引擎 (b2) 立即加载实现构建系统的 Jam 代码。为此,它在特定的安装位置搜索构建系统 bootstrap.jam 文件。搜索基于 b2(.exe) 可执行文件的位置。

默认的 bootstrap.jam 在加载一些标准定义后,会加载 site-config.jamuser-config.jam

为了保持向后兼容性,如果存在名为 boost-build.jam 的文件,则会加载该文件。搜索首先从调用目录开始,然后在父目录中进行,依此类推,直到文件系统根目录,最后在环境变量 BOOST_BUILD_PATH 指定的目录中进行搜索。在 Unix 上,BOOST_BUILD_PATH 默认为 /usr/share/b2

6.2. 内置规则

本节包含可在 Jamfile 中使用的所有规则的列表——包括定义新目标的规则和辅助规则。

exe

创建可执行文件。请参阅名为“程序”的章节

lib

创建库文件。请参阅名为“库”的章节

install

安装构建的目标和其他文件。请参阅名为“安装”的章节

alias

为其他目标创建别名。请参阅名为“别名”的章节

unit-test

创建将自动运行的可执行文件。请参阅名为“测试”的章节

compile; compile-fail; link; link-fail; run; run-fail

用于测试的专用规则。请参阅名为“测试”的章节

check-target-builds

check-target-builds 允许您根据某些元目标是否构建来有条件地使用不同的属性。这类似于 autotools 项目中配置脚本的功能。函数签名是

rule check-target-builds ( target message ? : true-properties * : false-properties * )

此函数只能在将要求或用法要求传递给元目标规则时使用。例如,要使应用程序链接到库(如果可用),可以使用以下代码

exe app : app.cpp : [ check-target-builds has_foo "System has foo" : <library>foo : <define>FOO_MISSING=1 ] ;

对于另一个示例,可以使用 alias 规则来整合配置选项,并使其可用于其他元目标,如下所示

alias foobar : : : : [ check-target-builds has_foo "System has foo" : <library>foo : <library>bar ] ;
obj

创建目标文件。当必须使用特殊属性编译单个源文件时很有用。

preprocessed

创建预处理的源文件。参数遵循通用语法

glob

glob 规则接受 shell 模式列表,并返回项目中源目录中与该模式匹配的文件列表。例如

lib tools : [ glob *.cpp ] ;

也可以传递第二个参数——排除模式列表。然后,结果将包括与任何包含模式匹配且与任何排除模式不匹配的文件列表。例如

lib tools : [ glob *.cpp : file_to_exclude.cpp bad*.cpp ] ;
glob-tree

glob-tree 类似于 glob,不同之处在于它从包含 Jamfile 的目录递归运行。例如

ECHO [ glob-tree *.cpp : .svn ] ;

将打印项目中所有 C++ 文件的名称。.svn 排除模式可防止 glob-tree 规则进入 Subversion 版本控制系统的管理目录。

project

声明项目 ID 和属性,包括项目要求。请参阅名为“项目”的章节

use-project

将符号项目 ID 分配给给定路径中的项目。此规则必须更好地记录!

explicit

explicit 规则接受一个参数——目标名称列表。命名的目标将被标记为显式,并且只有在命令行上显式请求它们,或者构建它们的依赖项时才会构建它们。将其与普通目标进行比较,普通目标在其包含项目构建时隐式构建。

always

always 函数接受一个参数,即元目标名称的列表。由指定的元目标生成的目标将始终被视为过时。请看以下示例

exe hello : hello.cpp ;
exe bye : bye.cpp ;
always hello ;

如果请求构建 hello,那么它将始终被重新编译。请注意,如果未请求构建 hello,例如您在命令行中仅指定 bye,则 hello 将不会被重新编译。

constant

设置项目范围的常量。接受两个参数:变量名和一个值,并使指定的变量名在此 Jamfile 和任何子 Jamfile 中都可访问。例如

constant VERSION : 1.34.0 ;
path-constant

constant 相同,不同之处在于该值被视为相对于 Jamfile 位置的路径。例如,如果在当前目录中调用 b2,并且 `helper` 子目录中的 Jamfile 具有

path-constant DATA : data/a.txt ;

那么变量 DATA 将被设置为 helper/data/a.txt,并且如果 b2 是从 `helper` 目录调用的,则变量 DATA 将被设置为 data/a.txt

build-project

导致构建其他项目。此规则接受一个参数,即相对于包含 Jamfile 的目录名。当包含 Jamfile 被构建时,位于该目录的项目也将被构建。目前,此规则的参数应为目录名。不允许项目 ID 或通用目标引用。

test-suite

此规则已弃用,等同于 alias

import-search
Jam

rule import-search ( reference )

将给定的 reference 路径添加到 import 将搜索的目录集中。`reference` 可以是普通目录或已知的项目路径。如果给定项目路径,则将搜索并解析该路径,以包含引用中的任何子项目路径。如果给定目录,则它将以当前项目位置为根目录。项目路径用法示例

import-search /boost/config/checks ;
import-search /boost/predef/tools/checks ;

6.2.1. b2::require_b2

Jam

rule require-b2 ( minimum : maximum ? )

C++

void require_b2(value_ref minimum, value_ref maximum, bind::context_ref_ context_ref);

检查 b2 引擎版本是否至少为 minimum 且严格小于 maximum。如果未给出 maximum,则版本将匹配为至少为 minimum。如果检查失败,b2 将退出并显示错误消息和失败。

6.3. 内建特性

本节介绍 B2 内建的特性。对于具有固定值集的特性,将提供该集合,并将默认值列在首位。

address-model

允许的值: 32, 64

指定编译器应生成 32 位还是 64 位代码。此特性是否有效取决于所使用的编译器、其版本、编译器的配置方式以及 architectureinstruction-set 特性的值。请参阅 C++ 编译器 部分了解详细信息。

address-sanitizer

允许的值: on, norecover

启用地址清理器。值 norecover 禁用清理器的恢复功能。此特性是可选的,因此默认情况下不启用任何清理器。

allow

此特性用于允许特定生成器运行。例如,只有在使用 Qt 库时才能调用 Qt 工具。在这种情况下,`<allow>qt` 将在库的使用要求中。

architecture

允许的值: x86, ia64, sparc, power, mips, mips1, mips2, mips3, mips4, mips32, mips32r2, mips64, parisc, arm, s390x, loongarch

指定要为其生成代码的通用处理器系列。

archiveflags

此特性的值在创建静态库时未经修改地传递给归档工具。

asmflags

此特性的值未经修改地传递给汇编器。

asynch-exceptions

允许的值: off, on

选择是否支持异步 EH(例如捕获 SEGV)。

build

允许的值: no

用于有条件地禁用目标的构建。如果在构建目标时属性中包含 `<build>no`,则会跳过该目标的构建。与条件要求结合使用,这允许您在已知构建会失败的配置中跳过构建某些目标。

cflags; cxxflags; linkflags

这些特性的值未经修改地传递给相应的工具。对于 `cflags`,它是 C 和 C++ 编译器,对于 `cxxflags`,它是 C++ 编译器,对于 `linkflags`,它是链接器。当您尝试执行 B2 中更高级别特性无法实现的一些特殊操作时,这些特性非常方便。

compileflags

此特性的值未经修改地传递给相应的工具。`compileflags` 中的值应用于工具的任何语言的所有编译。

conditional

用于引入间接条件要求。该值应具有以下形式

@rulename

其中 *rulename* 应该是具有以下签名的规则的名称

rule rulename ( properties * )

将为每个目标及其属性调用该规则,并且应返回任何其他属性。另请参阅 要求 部分以获取示例。

coverage

允许的值: off, on

启用代码插桩以在执行期间生成覆盖率数据。

cxxflags

请参阅 <cflags>

cxxstd

允许的值: 98, 03, 0x, 11, 1y, 14, 1z, 17, 2a, 20, 2b, 23, 2c, 26, latest

指定要构建的 C++ 标准语言的版本。包括自 “98” 以来所有官方版本的标准。也可以指定使用实验性的、正在开发中的 `latest` 版本。一些编译器为发布标准版本之前的实验性版本指定了中间版本。这些版本按照 GNU 命名法包含在内,如 0x1y1z2a2b2c。根据编译器的不同,latest 可能会映射到其中之一。

这是一个 `optional` 特性。因此,如果未指定,则使用编译器默认行为。
请查阅工具集特定文档,以了解支持哪些 cxxstd
cxxstd-dialect

cxxstd 的子特性

允许的值: iso, gnu, ms

指示是否应使用非标准方言。这些方言通常具有“或此或彼”扩展或平台特定功能。不指定方言将默认为 “iso”,这将尝试在编译器能力范围内尽可能使用 ISO C++ 标准一致性。

c++abi

如果编译器支持多个 C++ ABI 变体,则选择一个特定的变体。

c++-template-depth

允许的值: 任何正整数。

允许使用最大模板实例化深度参数配置 C++ 编译器。特定工具集可能支持或不支持此特性,具体取决于其编译器是否提供相应的命令行选项。

由于当前 B2 实现中的一些内部细节,无法拥有其有效值均为正整数的特性。作为一种变通方法,已为此特性定义了大量允许的值,如果需要不同的值,用户可以通过调用 feature.extend 规则轻松添加它。
debug-symbols

允许的值: on, off

指定生成的对象文件、可执行文件和库是否应包含调试信息。通常,此特性的值由 `variant` 特性隐式设置,但用户可以显式指定它。最常见的用法是构建带有调试信息的发布变体。

define

指定应在命令行上定义的预处理器符号。您可以仅指定符号(将定义但不带任何值),也可以同时指定符号和值,并用等号分隔。

def-file

提供了一种为 Windows DLL 指定 def 文件的方法。

dependency

引入对此特性的值命名的目标的依赖项(因此,每当声明的目标是最新的时,它也会是最新的)。该依赖项不以任何其他方式使用。

dll-path (仅限 Unix)

允许的值: 共享库的目录路径。

指定在运行目标时系统应查找共享库的附加目录。

请注意,相对路径将在相关 jam 文件的目录(如 b2 命令行上提供的)之前添加前缀,从而 *严重* 限制了它的实际用途!

请参阅关于 dll-pathhardcode-dll-paths 的 FAQ 条目 以了解详细信息,并参阅 hardcode-dll-paths 特性以自动为开发添加路径。

embed-manifest

允许的值: on, off

此特性特定于 msvc 工具集(请参阅 Microsoft Visual C++),并控制清单文件应嵌入到可执行文件和共享库中,还是与它们放在一起。此特性对应于项目设置对话框中“配置属性”→“清单工具”→“输入和输出”→“嵌入清单”下的 IDE 选项。

embed-manifest-file

此特性特定于 msvc 工具集(请参阅 Microsoft Visual C++),并控制哪些清单文件应嵌入到可执行文件和共享库中。此特性对应于项目设置对话框中“配置属性”→“清单工具”→“输入和输出”→“附加清单文件”下的 IDE 选项。

embed-manifest-via

此特性特定于 msvc 工具集(请参阅 Microsoft Visual C++),并控制是否应通过链接器或清单工具嵌入清单。

exception-handling

允许的值: on, off

禁用异常。

extern-c-nothrow

允许的值: off, on

选择是否默认将所有 extern "C" 函数都视为 nothrow

fflags

编译 Fortran 源代码时,此特性的值未经修改地传递给工具。

file

当在预构建库目标的 requirements 中使用时,此特性指定库文件的路径。有关示例,请参阅 预构建目标

find-shared-library

添加要链接的共享库。通常,应优先使用 lib 目标而不是使用此特性。

find-static-library

添加要链接的静态库。通常,应优先使用 lib 目标而不是使用此特性。

flags

此特性用于工具的通用标志,即非语言特定的标志。此特性的值未经修改地传递给将构建目标的工具。

hardcode-dll-paths (仅限 Unix)

允许的值: true, false
默认值: true(exe), false(install)
忽略用于: lib

当使用 `<hardcode-dll-paths>true`(默认值)构建 executable 时,目标二进制文件将与包含已用共享库目录的所有路径的 *rpath 列表* 链接。

当使用 `<hardcode-dll-paths>true` 安装目标时,这些相同的路径以及任何使用 <dll-path> 添加的路径都将传播到结果二进制文件。

此特性的目的是帮助开发;默认情况下,可以运行生成的可执行文件(exe - 但不是 install 目标),而无需更改共享库的系统路径或将库安装到系统路径。
请参阅 FAQ 条目 以了解详细信息。

implicit-dependency

指示由此特性的值命名的目标可能会生成声明的目标的源文件包含的文件。有关更多信息,请参阅 生成的头文件 部分。

force-include

指定必须包含的包含路径,其方式类似于在每个目标源文件的第一行出现 #include "file"

如果在单个目标上多次使用,则不保证包含顺序。

include

指定要传递给 C 和 C++ 编译器的附加包含路径。

inlining

允许的值: off, on, full

启用内联。

install-package

指定已安装文件所属的软件包的名称。这用于某些平台上的默认安装前缀。

install-<name>

指定 install 目标的安装前缀。这些命名的安装前缀默认注册

  • prefix: 如果属性集中包含 <target-os>windows,则为 C:\<package name>,否则为 /usr/local

  • exec-prefix: (prefix)

  • bindir: (exec-prefix)/bin

  • sbindir: (exec-prefix)/sbin

  • libexecdir: (exec-prefix)/libexec

  • libdir: (exec-prefix)/lib

  • datarootdir: (prefix)/share

  • datadir: (datarootdir)

  • sysconfdir: (prefix)/etc

  • sharedstatedir: (prefix)/com

  • localstatedir: (prefix)/var

  • runstatedir: (localstatedir)/run

  • includedir: (prefix)/include

  • oldincludedir: /usr/include

  • docdir: (datarootdir)/doc/<package name>

  • infodir: (datarootdir)/info

  • htmldir: (docdir)

  • dvidir : (docdir)

  • pdfdir : (docdir)

  • psdir : (docdir)

  • lispdir: (datarootdir)/emacs/site-lisp

  • localedir: (datarootdir)/locale

  • mandir: (datarootdir)/man

如果需要更多,可以使用 stage.add-install-dir 添加它们。

instruction-set

允许的值: 取决于使用的工具集。

指定应为其生成代码的特定指令集。一般来说,该代码可能无法在具有较旧/不同指令集的处理器上运行。

虽然 B2 允许此特性使用大量可能的值,但给定值是否有效取决于您使用的编译器。请参阅 C++ 编译器 部分了解详细信息。

library

此特性几乎等同于 <source> 特性,但它仅在链接时生效。当您想将 Jamfile 中的所有目标链接到特定库时,<library> 特性优先于 <source>X——后者会将库添加到所有目标,即使是那些与库无关的目标。

library-path

添加到链接器将用于搜索库的目录列表中。

leak-sanitizer

允许的值: on, norecover

启用泄漏清理器。值 norecover 禁用清理器的恢复功能。此特性是可选的,因此默认情况下不启用任何清理器。

linemarkers

允许的值: off

在预处理目标上,更改行为以发出/省略行指令,如 #line#linenum

注意: 该值不传播。

link

允许的值: shared, static

控制库的构建方式。

linkflags

请参阅 <cflags>

local-visibility

允许的值: global, protected, hidden

此特性与 visibility 特性具有相同的效果,但旨在供需要特定符号可见性的目标使用。与 visibility 特性不同,local-visibility 不会被目标依赖项继承,并且仅影响应用它的目标。

local-visibility 特性支持与 visibility 特性相同的值,并且含义相同。默认情况下,如果未为目标指定 local-visibility,则使用 visibility 特性的值。

location

指定目标的构建目录。此特性主要与 <install> 规则一起使用。

location-prefix

将目标的构建目录设置为项目的构建目录,并以该特性的值作为前缀。有关示例,请参阅 目标路径 部分。

mflags

编译 Objective C 源代码时,此特性的值未经修改地传递给工具。

mmflags

编译 Objective C++ 源代码时,此特性的值未经修改地传递给工具。

name

当在预构建库目标的 requirements 中使用时,此特性指定库的名称(不带任何平台特定后缀或前缀的库文件名)。有关示例,请参阅 预构建目标

当在 <install> 目标的 requirements 中使用时,它指定目标文件的名称。

optimization

允许的值: off, speed, space, 'minimal', 'debug'。

启用优化。speed 优化以获得更快的代码,space 优化以获得更小的二进制文件。

profiling

允许的值: off, on

启用生成额外的代码以写入性能分析信息。

relevant

允许的值: 任何特性的名称。

指示哪些其他特性与给定目标相关。通常不需要显式管理它,因为 B2 在大多数情况下可以推断出来。不相关的特性不会影响目标路径,也不会导致冲突。

  • 如果满足以下任一条件,则特性将被视为相关

    • 它被 toolset.flagstoolset.uses-features 引用

    • 它被生成器的 requirements 使用

    • 它是相关特性的子特性

    • 它具有相关的子特性

    • 它是复合特性,并且任何组成的特性都是相关的

    • 它影响主目标的目标备选项选择

    • 它是传播的特性,并且与任何依赖项相关

    • 它与同一主目标创建的任何依赖项相关

    • 它用于条件属性的条件中,并且相应的值是相关的

    • 它被显式命名为相关

  • 在以下情况下,无法自动推断相关特性

    • 间接条件。解决方案:返回 <relevant>result-feature:<relevant>condition-feature 形式的属性

      这实际上不是条件,尽管在大多数情况下它的功能类似于条件。特别是,它不支持条件中以逗号分隔的多个元素,并且即使在不允许条件属性的上下文中也能正常工作
    • 读取属性的操作规则。解决方案:添加 toolset.uses-features 以告知 B2 该特性实际上已使用。

    • 直接操作属性集的生成器和目标。解决方案:手动设置 <relevant>。

rtti

允许的值: on, off

禁用运行时类型信息。

runtime-debugging

允许的值: on, off

指定生成的对象文件、可执行文件和库是否应包含仅对调试有用的行为,例如断言。通常,此特性的值由 `variant` 特性隐式设置,但用户可以显式指定它。最常见的用法是构建带有调试输出的发布变体。

runtime-link

允许的值: shared, static

控制应使用静态还是共享 C/C++ 运行时。此特性的使用方式存在一些限制,例如在某些编译器上,使用静态运行时的应用程序根本不应使用共享库,而在某些编译器上,混合使用静态和共享运行时需要格外小心。有关更多详细信息,请查看您的编译器文档。

search

当在预构建库目标的 requirements 中使用时,此特性添加到要搜索库文件的目录列表中。有关示例,请参阅 预构建目标

source

<source>X 属性对构建目标具有与将 X 放在源文件列表相同的效果。当您想将相同的源文件添加到项目中的所有目标(可以将 <source> 放在 requirements 中)或有条件地包含源文件(使用条件 requirements,请参阅 条件和备选项 部分)时,此属性很有用。另请参阅 <library> 特性。

staging-prefix

指定 install 目标的暂存前缀。如果存在,它将代替命名目录 prefix 的路径使用。示例

project : requirements <install-prefix>x/y/z ;
install a1 : a : <location>(bindir) ; # installs into x/y/z/bin
install a2 : a : <location>(bindir) <staging-prefix>q ; # installs into q/bin

当您在构建期间(例如交叉编译时)无法(或不想)将构建产物放入其预期位置,但仍需要将这些预期位置传达给构建系统(例如,生成配置文件)时,此特性很有用。

stdlib

允许的值: native, gnu, gnu11, libc++, sun-stlport, apache

指定要链接到的 C++ 标准库,以及在某些情况下要使用的库 ABI

native

使用编译器默认值。

gnu

使用带有旧 ABI 的 GNU 标准库(又名 libstdc++)。

gnu11

使用带有新 ABI 的 GNU 标准库。

libc++

使用 LLVM libc++。

sun-stlport

使用 Solaris Studio 编译器提供的标准库的 STLport 实现。

apache

使用 Solaris Studio 编译器提供的 Apache stdcxx 版本 4 C++ 标准库。

strip

允许的值: off, on

控制是否应剥离二进制文件——即删除运行不需要的所有内容。

此特性将显示在所有内容的目标路径中,而不仅仅是二进制文件。
suppress-import-lib

禁止链接器创建导入库。

tag

用于自定义生成文件的名称。该值应具有以下形式

@rulename

其中 *rulename* 应该是具有以下签名的规则的名称

rule tag ( name : type ? : property-set )

将为每个目标调用该规则,并提供 B2 计算的默认名称、目标的类型和属性集。该规则可以返回一个字符串,该字符串必须用作目标的名称,也可以返回一个空字符串,在这种情况下将使用默认名称。

tag 特性最典型的用途是在库目标名称中编码构建属性或库版本。您应该注意仅为您关心的类型从 tag 规则返回非空字符串——否则,您最终可能会修改对象文件、生成的头文件以及其他更改名称没有意义的目标的名称。

target-os

允许的值: aix, android, appletv, bsd, cygwin, darwin, freebsd, haiku, hpux, iphone, linux, netbsd, openbsd, osf, qnx, qnxnto, sgi, solaris, unix, unixware, windows, vms, vxworks, freertos

指定要为其生成代码的操作系统。您使用的编译器应该是该操作系统的编译器。此选项使 B2 使用适合该操作系统的命名约定,并相应地调整构建过程。例如,对于 gcc,它控制是否为共享库生成导入库。

有关交叉编译的详细信息,请参阅 交叉编译 部分。

threadapi

允许的值: pthread, win32

选择线程实现。如果 <target-os>windows,则默认值为 win32,否则为 pthread

threading

允许的值: single, multi

控制项目是否应在多线程模式下构建。此特性不一定更改编译器中的代码生成,但它会导致编译器链接到附加的或不同的运行时库,并定义附加的预处理器符号(例如,Windows 上的 _MT 和 Linux 上的 _REENTRANT)。这些符号如何影响编译后的代码取决于代码本身。

thread-sanitizer

允许的值: on, norecover

启用线程清理器。值 norecover 禁用清理器的恢复功能。此特性是可选的,因此默认情况下不启用任何清理器。

toolset

允许的值: 任何工具集模块。

选择将用于构建二进制目标的工具集。工具集模块的完整列表在 内建工具 部分中。

undef

指定要取消定义的预处理器符号。

undefined-sanitizer

允许的值: on, norecover

启用未定义行为清理器。值 norecover 禁用清理器的恢复功能。此特性是可选的,因此默认情况下不启用任何清理器。

use

引入对此特性的值命名的目标的依赖项(因此,每当声明的目标是最新的时,它也会是最新的),并将其使用要求添加到声明的目标的构建属性中。该依赖项不以任何其他方式使用。主要用例是当您希望应用某些库的使用要求(例如 #include 路径),但不希望链接到它时。

user-interface

允许的值: console, gui, wince, native, auto

指定可执行文件的环境,该环境会影响链接器将选择的入口点符号(或入口点函数)。此特性特定于 Windows。

console

控制台应用程序。

gui

应用程序不需要控制台(它应该创建自己的窗口)。

wince

应用程序旨在在具有 Windows CE 内核版本的设备上运行。

native

application runs without a subsystem environment.

auto

应用程序在 Windows 的 POSIX 子系统中运行。

variant

允许的值: debug, release, profile

一个结合了多个低级特性的特性,使其易于请求常见的构建配置。

debug 展开为

<optimization>off <debug-symbols>on <inlining>off <runtime-debugging>on

release 展开为

<optimization>speed <debug-symbols>off <inlining>full <runtime-debugging>off

profile 展开为与 release 相同,外加

<profiling>on <debug-symbols>on

用户可以使用 common 模块中的 variant 规则定义自己的构建变体。

在调试构建中,运行时调试处于启用状态,以适应习惯于各种 IDE 的人们的期望。
vectorize

允许的值: off, on, full

启用向量化。

version

任何内建工具都不使用此特性,但例如,可以使用它通过 <tag> 特性调整目标的名称。

visibility

允许的值: global, protected, hidden

指定编译后的二进制文件中的默认符号可见性。并非所有平台都支持所有值,并且在某些平台(例如 Windows)上,根本不支持符号可见性。

支持的值具有以下含义

global

也称为 gcc 文档中的 “default”。全局符号被认为是公共的,它们从共享库中导出,并且可以被另一个共享库或可执行文件重新定义。

protected

也称为 “symbolic”。受保护的符号从共享库中导出,但不能被另一个共享库或可执行文件重新定义。某些平台(例如 OS X)不支持此模式。

hidden

隐藏符号不从共享库导出,并且不能被进程中加载的不同共享库或可执行文件重新定义。在此模式下,必须在源代码中显式标记公共符号才能从共享库导出。这是推荐模式。

默认情况下,使用编译器默认可见性模式(不添加任何编译器标志)。

在 Boost 超级项目 Jamroot 文件中,此属性设置为默认值 hidden。这意味着 Boost 库默认以隐藏可见性构建,除非用户使用不同的 visibility 覆盖它,或者库设置了不同的 local-visibility(见下文)。
warnings

允许值: on, all, extra, pedantic, off

控制编译器的警告级别。

on

启用默认/"合理"的警告级别。

all

启用大多数警告。

extra

启用额外的,可能冲突的警告。

pedantic

启用可能不重要,以及冲突的警告。

off

禁用所有警告。

默认值是 all

warnings-as-errors

允许的值: off, on

使将警告视为错误并在出现警告时中止编译成为可能。

translate-path

用于引入自定义路径特性转换。该值应具有以下形式

@rulename

其中 *rulename* 应该是具有以下签名的规则的名称

rule rulename ( feature value : properties * : project-id : project-location )

该规则针对每个具有路径属性的 feature、路径属性值、目标属性、目标项目 ID 和目标项目位置的目标调用。它应该返回转换后的路径值。或者,如果它不进行路径转换,则不返回任何内容。保留它进行默认路径转换。

lto

允许值: on

启用链接时优化(也称为过程间优化或全程序优化)。当前支持的工具集是 GNU C++、clang 和 Microsoft Visual C++。此功能是可选的。

lto-mode

lto子特性

允许值: full, thin, fat

指定要使用的 LTO 类型。

full

使用单体 LTO:在链接时,所有输入都合并到单个模块中。

thin

使用 clang 的 ThinLTO:每个编译文件都包含模块的摘要,这些摘要合并到单个索引中。这允许避免将所有模块合并在一起,从而大大减少链接时间。

fat

生成 gcc 的 fat LTO 对象:编译后的文件包含适用于 LTO 的中间语言和适用于常规链接的目标代码。

response-file

允许值: auto, file, contents

控制在适用目标的构建期间是否使用响应文件。对于 file,将创建响应文件,并在操作中替换文件名。对于 contents,内容 (:E=) 将在操作中替换,并且不创建响应文件。对于 auto,将创建响应文件或替换内容,具体取决于内容的长度,以便如果内容在命令执行行长度限制范围内,则替换内容。否则,将创建响应文件,并在操作中替换文件名。

clang-linuxmsvc 工具集支持。

6.4. 内置工具

B2 附带对大量 C++ 编译器和其他工具的支持。本节介绍如何使用这些工具。

在使用任何工具之前,您必须声明您的意图,并可能指定有关工具配置的其他信息。这是通过调用 using 规则来完成的,通常在您的 user-config.jam 中,例如

using gcc ;

可以像其他规则一样传递额外的参数,例如

using gcc : 4.0 : g++-4.0 ;

可以在后续章节中找到可以传递给每个工具的选项的文档。

6.4.1. C++ 编译器

本节列出了所有支持 C++ 编译器的 B2 模块,并记录了如何初始化每个模块。编译器的支持模块的名称也是 toolset 特性的值,该特性可用于显式请求该编译器。

HP aC++ 编译器

acc 模块支持用于 HP-UX 操作系统的 HP aC++ 编译器

该模块使用以下语法初始化

using acc : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,则将在 PATH 中搜索 aCC 二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

Borland C++ 编译器

borland 模块支持在 Microsoft Windows 上运行的 32 位命令行 C 编译器。这是所有版本的 Borland C 和 C Builder 的 bcc32 可执行文件,以及更高版本的 C Builder 上的命令行兼容编译器 bcc32c。

该模块使用以下语法初始化

using borland : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,Boost.Build 将在 PATH 中搜索名为 bcc32 的二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

user-interface

指定应用程序的用户界面。有效选择包括用于控制台应用程序的 console 和用于 Windows 应用程序的 gui

Comeau C/C++ 编译器

como-linuxcomo-win 模块分别支持 Linux 和 Windows 上的 Comeau C/C++ 编译器

该模块使用以下语法初始化

using como : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,B2 将在 PATH 中搜索名为 como 的二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

在使用 Windows 版本的编译器之前,您需要根据编译器的文档设置必要的环境变量。特别是,应设置 COMO_XXX_INCLUDE 变量,其中 XXX 对应于使用的后端 C 编译器。

Code Warrior

cw 模块支持 CodeWarrior 编译器,最初由 Metrowerks 生产,目前由 Freescale 开发。B2 仅支持以 x86 处理器为目标的编译器版本。所有此类版本都在收购前由 Metrowerks 发布,并且不再销售。已知可用的最后一个版本是 9.4。

该模块使用以下语法初始化

using cw : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,B2 将在默认安装路径和 PATH 中搜索名为 mwcc 的二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

setup

在调用编译器之前设置环境变量的命令。如果未指定,将使用编译器二进制文件旁边的 cwenv.bat

compiler

编译 C 和 C++ 源代码的命令。如果未指定,将使用 mwcc。该命令将在执行设置脚本并调整 PATH 变量后调用。

linker

链接可执行文件和动态库的命令。如果未指定,将使用 mwld。该命令将在执行设置脚本并调整 PATH 变量后调用。

Digital Mars C/C++ 编译器

该模块使用以下语法初始化

using dmc : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,B2 将在 PATH 中搜索名为 dmc 的二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

Embarcadero C++ 编译器

embarcadero 模块支持在 Microsoft Windows 上运行的 32 位命令行 C 编译器 bcc32x 和 64 位命令行 C 编译器 bcc64,两者均基于 clang。这些是所有版本的 Embarcadero C++ 的基于 clang 的 Windows 编译器。

该模块使用以下语法初始化

using embarcadero : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

version:

如果指定,版本应为编译器版本。如果未指定版本,Boost Build 将查找 Embarcadero C 的最新安装版本并将其用作版本。如果指定了版本,Boost Build 不会检查这是否与 Embarcadero C 的任何特定版本匹配,因此您可以将该版本用作助记符来配置单独的“版本”。

c++-compile-command:

如果未指定 c-compile-command,Boost.Build 将默认使用 bcc64 编译器。如果您指定了 <address-model>32 的编译器选项,则默认编译器将为 bcc32x。在任何一种情况下,当未给出命令时,Boost Build 将假定编译器在 PATH 中。因此,如果您接受默认编译器并且 Embarcadero C 二进制目录在 PATH 中,则无需指定命令。

如果指定了命令,它将按原样用于调用编译器。如果命令中包含“bcc32x(.exe)”或“bcc64(.exe)”,则 Boost Build 将使用相应的编译器来配置工具集。如果命令中不包含“bcc32x(.exe)”或“bcc64(.exe)”,则 Boost Build 将使用默认编译器来配置工具集。如果您有自己的命令,该命令不包含“bcc32x(.exe)”,但调用了“bcc32x(.exe)”编译器,请指定 <address-model>32 编译器选项。

compiler options:

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

asmflags

指定将传递给汇编器的其他命令行选项。

archiveflags

指定将传递给归档程序的其他命令行选项,归档程序用于创建静态库。

address-model

此选项可用于指定默认编译器,如上面 c++-compile-command 的讨论中所述。否则,地址模型不用于初始化工具集。

user-interface

指定应用程序的用户界面。有效选择包括用于控制台应用程序的 console 和用于 Windows 应用程序的 gui

root

通常,Boost Build 将能够自动确定 Embarcadero C 安装的根目录。它通过多种方式执行此操作,但主要是通过检查注册表项。如果您指定了根目录,它将使用该路径,并且您指定的根目录应为计算机上 Embarcadero C 安装的完整路径(不带尾部的 \ 或 / )。除非 Boost Build 无法找到 Embarcadero C++ 根目录,否则您无需指定此选项。

示例

using embarcadero ;

配置工具集以使用最新版本,并将 bcc64 用作编译器。bcc64 编译器必须在 PATH 中。

using embarcadero : 7.40 ;

配置工具集以使用 7.40 版本,并将 bcc64 用作编译器。bcc64 编译器必须在 PATH 中。

using embarcadero : 7.40 : bcc32x ; using embarcadero : 7.40 : : <address-model>32 ;

配置工具集以使用 7.40 版本,并将 bcc32x 用作编译器。bcc32x 编译器必须在 PATH 中。

using embarcadero : : c:/some_path/bcc64 ;

配置工具集以使用最新版本,并指定完整命令。

using embarcadero : : full_command : <address-model>32 ;

配置工具集以使用最新版本,并指定完整命令,并将 bcc32x 用作编译器。

using embarcadero : : : <root>c:/root_path ;

配置工具集以使用最新版本,并将 bcc64 用作编译器,并指定安装的根目录。bcc64 编译器必须在 PATH 中。

GNU C++

gcc 模块支持 Linux、许多类 Unix 系统(包括 SunOS)以及 Windows(CygwinMinGW)上的 GNU C++ 编译器

gcc 模块使用以下语法初始化

using gcc : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未显式指定版本,则将通过使用 -v 选项运行编译器来自动检测版本。如果未指定命令,则将在 PATH 中搜索 g++ 二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

asmflags

指定编译汇编程序源代码时将使用的其他编译器标志。

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

fflags

指定编译 Fortran 源代码时将使用的其他编译器标志。

mflags

指定编译 Objective-C 源代码时将使用的其他编译器标志。

mmflags

指定编译 Objective-C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译任何语言源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

root

指定编译器安装的根目录。仅当无法从编译器命令检测到此信息时,此选项才是必需的——例如,如果指定的编译器命令是用户脚本。

archiver

指定用于生成静态库的归档程序命令。通常,它是使用 gcc -print-prog-name 选项自动检测的,或者默认为 ar,但在某些情况下,您可能希望覆盖它,例如显式使用系统版本而不是 gcc 附带的版本。

rc

指定将与正在配置的 gcc 版本一起使用的资源编译器命令。此设置仅对 Windows 有意义,并且仅当您计划使用资源文件时才有意义。默认情况下,将使用 windres

rc-type

指定资源编译器的类型。该值可以是用于 msvc 资源编译器的 windres,也可以是用于 borland 资源编译器的 rc

为了编译 64 位应用程序,您必须指定 address-model=64,并且 instruction-set 特性应引用 64 位处理器。目前,这些处理器包括 noconaopteronathlon64athlon-fx

HP C++ Compiler for Tru64 Unix

hp_cxx 模块支持用于 Tru64 Unix 的 HP C++ Compiler

该模块使用以下语法初始化

using hp_cxx : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,B2 将在 PATH 中搜索名为 hp_cxx 的二进制文件。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

Intel C++

intel-* 模块支持 Intel C++ 命令行编译器。

该模块使用以下语法初始化

using intel : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定编译器命令,则 B2 将在 PATH 中查找可执行文件 icpc(在 Linux 上)或 icl.exe(在 Windows 上)。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

root

对于 Linux 版本,指定编译器安装的根目录。仅当无法从编译器命令检测到此信息时,此选项才是必需的——例如,如果指定的编译器命令是用户脚本。对于 Windows 版本,指定 21(或 2021)之前的版本的 iclvars.bat 文件或 21(或 2021)及更高版本的 setvars.bat 文件的目录,用于配置编译器。指定 root 选项而不指定编译器命令允许最终用户不必担心他们正在编译 32 位还是 64 位代码,因为工具集将使用 iclvars.batsetvars.bat 批处理文件自动为适当的地址模型和编译器命令配置编译器。

Microsoft Visual C++

msvc 模块支持 Microsoft Windows 上的 Microsoft Visual C++ 命令行工具。下面列出了受支持的产品和命令行工具版本

  • Visual Studio 2022-14.3

  • Visual Studio 2019-14.2

  • Visual Studio 2017—14.1

  • Visual Studio 2015—14.0

  • Visual Studio 2013—12.0

  • Visual Studio 2012—11.0

  • Visual Studio 2010—10.0

  • Visual Studio 2008—9.0

  • Visual Studio 2005—8.0

  • Visual Studio .NET 2003—7.1

  • Visual Studio .NET—7.0

  • Visual Studio 6.0,Service Pack 5—​6.5

然后,用户将调用 boost build 可执行文件,并将工具集设置为等于 msvc-[版本号],例如,要使用 Visual Studio 2019 构建,可以运行

.\b2 toolset=msvc-14.2 target

msvc 模块使用以下语法初始化

using msvc : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未显式指定版本,则将使用注册表中找到的最新版本。如果将特殊值 all 作为版本传递,则将配置注册表中找到的所有版本。如果指定了版本,但未指定命令,则将在该版本的标准安装路径中搜索编译器二进制文件,然后在 PATH 中搜索。

编译器命令应使用正斜杠指定,并用引号引起来。

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

assembler

编译汇编程序源代码的命令。如果未指定,将使用 ml。该命令将在执行设置脚本并调整 PATH 变量后调用。

compiler

编译 C 和 C++ 源代码的命令。如果未指定,将使用 cl。该命令将在执行设置脚本并调整 PATH 变量后调用。

compiler-filter

用于通过管道传输编译器运行输出的命令。例如,将输出传递给 STLfilt。

idl-compiler

编译 Microsoft COM 接口定义文件的命令。如果未指定,将使用 midl。该命令将在执行设置脚本并调整 PATH 变量后调用。

linker

linker

链接可执行文件和动态库的命令。如果未指定,将使用 link。该命令将在执行设置脚本并调整 PATH 变量后调用。

mc-compiler

编译 Microsoft 消息目录文件的命令。如果未指定,将使用 mc。该命令将在执行设置脚本并调整 PATH 变量后调用。

resource-compiler

setup

在调用此工具集中定义的任何工具之前要运行的全局环境设置脚本的文件名。如果已为当前目标平台显式指定了特定于目标平台的脚本,则不会使用。使用的设置脚本将传递目标平台标识符(x86、x86_amd64、x86_ia64、amd64 或 ia64)作为参数。如果未指定,则会根据使用的编译器二进制文件选择默认脚本,例如 vcvars32.batvsvars32.bat

setup-amd64; setup-i386; setup-ia64

在调用此工具集中定义的任何工具之前要运行的特定于目标平台的脚本的文件名。如果未指定,则使用全局环境设置脚本。

64 位支持

从 8.0 版本开始,Microsoft Visual Studio 可以为 64 位处理器生成二进制文件,包括 x86 的 64 位风格(代号 AMD64/EM64T)和 Itanium(代号 IA64)。此外,还提供了以 64 位模式自身运行的编译器,以获得更好的性能。编译器配置的完整列表如下(我们将 AMD64/EM64T 缩写为 AMD64)

  • 32 位 x86 主机,32 位 x86 目标

  • 32 位 x86 主机,64 位 AMD64 目标

  • 32 位 x86 主机,64 位 IA64 目标

  • 64 位 AMD64 主机,64 位 AMD64 目标

  • 64 位 IA64 主机,64 位 IA64 目标

32 位主机编译器始终可以使用,即使在 64 位 Windows 上也是如此。相反,64 位主机编译器需要 64 位主机处理器和 64 位 Windows,但速度可能更快。默认情况下,仅安装 32 位主机、32 位目标编译器,并且需要显式安装其他编译器。

要使用 64 位编译,您应该

  1. 像往常一样配置您的编译器。如果您显式提供编译器的路径,请提供 32 位编译器的路径。如果您尝试指定任何 64 位编译器的路径,配置将不起作用。

  2. 编译时,使用 address-model=64 生成 AMD64 代码。

  3. 要生成 IA64 代码,请使用 architecture=ia64

当您生成 AMD64 代码并在 AMD64 上运行 64 位 Windows 时,将自动使用(AMD64 主机,AMD64 目标)编译器。(IA64 主机,IA64 目标)编译器将永远不会使用,因为没有人有 IA64 机器进行测试。

据信 AMD64 和 EM64T 目标基本上是兼容的。编译器选项 /favor:AMD64/favor:EM64T 仅被以 AMD64 为目标的编译器接受,它们会导致生成的代码针对 64 位 x86 的特定风格进行调整。B2 将根据 instruction-set 特性的值来使用这些选项。

从 14.0 版本开始,Microsoft Visual Studio 可以使用本机 arm64 工具生成二进制文件,以编译 x86、x86_64 和 arm64。

Windows 运行时支持

从 11.0 版本开始,Microsoft Visual Studio 除了传统的 Win32 桌面应用程序外,还可以为 Windows 应用商店和手机生成二进制文件。要指定要以哪个 Windows API 集为目标,请使用 windows-api 特性。可用选项为 desktopstorephone。如果未指定,将使用 desktop

当使用 storephone 时,指定的工具集确定了目标 Windows 版本。以下选项可用

  • Windows 8.0:toolset=msvc-11.0 windows-api=store

  • Windows 8.1:toolset=msvc-12.0 windows-api=store

  • Windows Phone 8.0:toolset=msvc-11.0 windows-api=phone

  • Windows Phone 8.1:toolset=msvc-12.0 windows-api=phone

例如,使用以下命令为具有 ARM 架构的 Windows 应用商店 8.1 构建

.\b2 toolset=msvc-12.0 windows-api=store architecture=arm

请注意,当以 Windows Phone 8.1 为目标时,12.0 版本不包含 vcvars phone 设置脚本。可以从 此处单独下载它们。

Sun Studio

sun 模块支持用于 Solaris OS 的 Sun Studio C++ 编译器。

该模块使用以下语法初始化

using sun : [version] : [c++-compile-command] : [compiler options] ;

如果要配置编译器的多个版本,则可以多次重复此语句。

如果未指定命令,B2 将在 /opt/SUNWspro/bin 和 PATH 中搜索名为 CC 的二进制文件。

当在复杂的 C 代码(例如 https://boost.ac.cn[Boost C 库])上使用此编译器时,建议在初始化 sun 模块时指定以下选项

-library=stlport4 -features=tmplife -features=tmplrefstatic

有关详细信息,请参阅 Sun C++ Frontend Tales

可以使用以下选项,使用`<option-name>option-value 语法`

cflags

指定编译 C 源代码时将使用的其他编译器标志。

cxxflags

指定编译 C++ 源代码时将使用的其他编译器标志。

compileflags

指定编译 C 和 C++ 源代码时将使用的其他编译器标志。

linkflags

指定将传递给链接器的其他命令行选项。

从 Sun Studio 12 开始,您可以使用 address-model=64 属性创建 64 位应用程序。

IBM Visual Age

vacpp 模块支持用于 AIX 操作系统的 IBM Visual Age C++ 编译器。已知版本 7.1 和 8.0 可用。

该模块使用以下语法初始化

using vacpp ;

该模块不接受任何初始化选项。编译器应安装在 /usr/vacpp/bin 目录中。

Visual Age 的更高版本称为 XL C/C++。它们未经过 vacpp 模块的测试。

6.4.2. 第三方库

B2 为一些第三方 C++ 库提供了特殊支持,如下所述。

STLport 库

STLport 库是 C++ 运行时库的替代实现。B2 支持在 Windows 平台上使用该库。Linux 受每个 STLport 版本中库的不同命名方式的阻碍,并且未获得官方支持。

在使用 STLport 之前,您需要在 user-config.jam 中使用以下语法配置它

using stlport : version : header-path : library-path ;

其中 version 是 STLport 的版本,例如 5.1.4,headers 是可以找到 STLport 标头的位置,而 libraries 是可以找到 STLport 库的位置。应始终提供版本,如果您使用的是 STLport 的 iostreams 实现,则应提供库路径。请注意,STLport 5.* 始终使用其自己的 iostream 实现,因此需要库路径。

配置 STLport 后,您可以通过在命令行上请求 stdlib=stlport 来使用 STLport 进行构建。

zlib

提供对 zlib 库的支持。zlib 可以配置为使用预编译的二进制文件,也可以从源代码构建库。

zlib 可以使用以下语法初始化

using zlib : version : options : condition : is-default ;

使用预构建库的选项

search

包含 zlib 二进制文件的目录。

name

覆盖默认库名称。

include

包含 zlib 标头的目录。

如果未指定这些选项,则将改为使用环境变量 ZLIB_LIBRARY_PATH、ZLIB_NAME 和 ZLIB_INCLUDE。

从源代码构建 zlib 的选项

source

zlib 源代码目录。默认为环境变量 ZLIB_SOURCE。

tag

设置 tag 属性以调整库的文件名。使用预编译的二进制文件时忽略。

build-name

用于编译库的基本名称。使用预编译的二进制文件时忽略。

示例

# Find zlib in the default system location
using zlib ;
# Build zlib from source
using zlib : 1.2.7 : <source>/home/steven/zlib-1.2.7 ;
# Find zlib in /usr/local
using zlib : 1.2.7 : <include>/usr/local/include <search>/usr/local/lib ;
# Build zlib from source for msvc and find
# prebuilt binaries for gcc.
using zlib : 1.2.7 : <source>C:/Devel/src/zlib-1.2.7 : <toolset>msvc ;
using zlib : 1.2.7 : : <toolset>gcc ;
bzip2

提供对 bzip2 库的支持。bzip2 可以配置为使用预编译的二进制文件,也可以从源代码构建库。

bzip2 可以使用以下语法初始化

using bzip2 : version : options : condition : is-default ;

使用预构建库的选项

search

包含 bzip2 二进制文件的目录。

name

覆盖默认库名称。

include

包含 bzip2 标头的目录。

如果未指定这些选项,则将改为使用环境变量 BZIP2_LIBRARY_PATH、BZIP2_NAME 和 BZIP2_INCLUDE。

从源代码构建 bzip2 的选项

source

bzip2 源代码目录。默认为环境变量 BZIP2_SOURCE。

tag

设置 tag 属性以调整库的文件名。使用预编译的二进制文件时忽略。

build-name

用于编译库的基本名称。使用预编译的二进制文件时忽略。

示例

# Find bzip in the default system location
using bzip2 ;
# Build bzip from source
using bzip2 : 1.0.6 : <source>/home/sergey/src/bzip2-1.0.6 ;
# Find bzip in /usr/local
using bzip2 : 1.0.6 : <include>/usr/local/include <search>/usr/local/lib ;
# Build bzip from source for msvc and find
# prebuilt binaries for gcc.
using bzip2 : 1.0.6 : <source>C:/Devel/src/bzip2-1.0.6 : <toolset>msvc ;
using bzip2 : 1.0.6 : : <toolset>gcc ;
Python

提供对 python 语言环境的支持,以作为库链接到其中。

python 可以使用以下语法初始化

using python : [version] : [command-or-prefix] : [includes] : [libraries] : [conditions] : [extension-suffix] ;

使用 python 的选项

version

要使用的 Python 版本。应采用 Major.Minor 格式,例如 2.3。不要包含子版本号。

command-or-prefix

最好是调用 Python 解释器的命令。或者,Python 库和包含文件的安装前缀。如果为空,将根据版本、平台的安装模式以及可以在 PATH 中找到的 python 可执行文件进行猜测。

includes

Python 标头的包含路径。如果为空,将被猜测。

libraries

Python 库二进制文件的路径。如果为空,将被猜测。在 MacOS/Darwin 上,您还可以传递 Python 框架的路径。

conditions

如果指定,则应是一组属性,当 B2 选择要使用的 Python 配置时,这些属性将与构建配置进行匹配。

extension-suffix

在真实文件名扩展名之前附加到扩展模块名称的字符串。通常,我们只会根据 <python-debugging> 特性的值来计算此值。但是,ubuntu 的 python-dbg 包使用 windows 的约定,将 _d 附加到调试构建扩展模块。我们无法检测 ubuntu,也无法探测 python 的“_d”要求,如果您使用 --with-pydebug 配置和构建 python,您将使用标准的 *nix 约定。默认为 ""(或在以 windows 为目标且设置了 <python-debugging> 时为 "_d")。

示例

# Find python in the default system location
using python ;
# 2.7
using python : 2.7 ;
# 3.5
using python : 3.5 ;

# On ubuntu 16.04
using python
: 2.7 # version
: # Interpreter/path to dir
: /usr/include/python2.7 # includes
: /usr/lib/x86_64-linux-gnu # libs
: # conditions
;

using python
: 3.5 # version
: # Interpreter/path to dir
: /usr/include/python3.5 # includes
: /usr/lib/x86_64-linux-gnu # libs
: # conditions
;

# On windows
using python
: 2.7 # version
: C:\\Python27-32\\python.exe # Interperter/path to dir
: C:\\Python27-32\\include # includes
: C:\\Python27-32\\libs # libs
: <address-model>32 <address-model> # conditions - both 32 and unspecified
;

using python
: 2.7 # version
: C:\\Python27-64\\python.exe # Interperter/path to dir
: C:\\Python27-64\\include # includes
: C:\\Python27-64\\libs # libs
: <address-model>64 # conditions
;

6.4.3. 文档工具

下面记录了 B2 对 Boost 文档工具的支持。

xsltproc

要使用 xsltproc,您首先需要使用以下语法配置它

using xsltproc : xsltproc ;

其中 xsltproc 是 xsltproc 可执行文件。如果未指定 xsltproc,并且设置了变量 XSLTPROC,则将使用 XSLTPROC 的值。否则,将在 PATH 中搜索 xsltproc。

可以使用以下选项,使用`<option-name>option-value 语法`

xsl:param

值应具有 name=value 的形式

xsl:path

为 xi:include 元素设置额外的搜索路径。

catalog

用于将远程 URL 重写为本地副本的目录文件。

xsltproc 模块提供以下规则。请注意,这些规则作用于 jam 目标,旨在供 boostbook 等其他工具集使用,而不是直接由用户使用。

xslt
rule xslt ( target : source stylesheet : properties * )

运行 xsltproc 以创建单个输出文件。

xslt-dir
rule xslt-dir ( target : source stylesheet : properties * : dirname )

运行 xsltproc 以在目录中创建多个输出。dirname 未使用,但因历史原因而存在。输出目录由目标确定。

boostbook

要使用 boostbook,您首先需要使用以下语法进行配置

using boostbook : docbook-xsl-dir : docbook-dtd-dir : boostbook-dir ;

docbook-xsl-dir 是 DocBook XSL 样式表目录。如果未提供,我们将使用环境变量中的 DOCBOOK_XSL_DIR(如果可用)或在标准位置查找。否则,我们让 XML 处理器远程加载样式表。

docbook-dtd-dir 是 DocBook DTD 目录。如果未提供,我们将使用环境变量中的 DOCBOOK_DTD_DIR(如果可用)或在标准位置查找。否则,我们让 XML 处理器远程加载 DTD。

boostbook-dir 是包含 DTD 和 XSL 子目录的 BoostBook 目录。

boostbook 模块依赖于 xsltproc。对于 pdf 或 ps 输出,它还依赖于 fop。

可以使用以下选项,使用`<option-name>option-value 语法`

format

允许的值: htmlxhtmlhtmlhelponehtmlmanpdfpsdocbookfotests

format 功能确定 boostbook 规则生成的输出类型。

boostbook 模块定义了一个规则,用于按照通用语法创建目标。

boostbook
rule boostbook ( target-name : sources * : requirements * : default-build * )

创建 boostbook 目标。

doxygen

要使用 doxygen,您首先需要使用以下语法进行配置

using doxygen : name ;

name 是 doxygen 命令。如果未指定,则将在 PATH 中查找。

当生成 BoostBook XML 时,doxygen 模块依赖于 boostbook 模块。

可以使用以下选项,使用`<option-name>option-value 语法`

doxygen:param

doxygen:param 的所有值都将添加到 doxyfile 中。

prefix

指定生成 BoostBook XML 时所有标头的通用前缀。此之前的所有内容都将被剥离。

reftitle

指定生成 BoostBook XML 时库参考部分的标题。

doxygen:xml-imagedir

生成 BoostBook XML 时,指定放置从 LaTex 公式生成的图像的目录。

路径是相对于当前工作目录解释的,而不是相对于 Jamfile。这对于匹配 BoostBook 的行为是必要的。

doxygen 模块定义了一个规则,用于按照通用语法创建目标。

doxygen
rule doxygen ( target : sources * : requirements * : default-build * : usage-requirements * )

创建 doxygen 目标。如果目标名称以 .html 结尾,则这将生成一个 html 目录。否则,它将生成 BoostBook XML。

quickbook

quickbook 模块提供了一个生成器,用于将 Quickbook 转换为 BoostBook XML。

要使用 quickbook,您首先需要使用以下语法进行配置

using quickbook : command ;

command 是 quickbook 可执行文件。如果未指定,B2 将从源代码编译它。如果它无法找到源代码,它将在 PATH 中搜索 quickbook 可执行文件。

fop

fop 模块提供了生成器,用于将 XSL 格式化对象转换为 Postscript 和 PDF。

要使用 fop,您首先需要使用以下语法进行配置

using fop : fop-command : java-home : java ;

fop-command 是运行 fop 的命令。如果未指定,B2 将在 PATH 和 FOP_HOME 中搜索它。

可以使用 java-homejava 来指定在何处查找 java。

6.5. 内置模块

本节介绍 B2 提供的模块。import 规则允许在一个模块或 Jamfile 中使用另一个模块的规则。

6.5.1. modules 模块。

modules 模块定义了处理模块的基本功能。

一个模块定义了许多可以在其他模块中使用的规则。模块可以在顶层包含代码来初始化模块。此代码在首次加载模块时执行。

Jamfile 是一种特殊的模块,由构建系统管理。尽管用户无法直接加载它们,但模块的其他功能对于 Jamfile 仍然有用。

每个模块都有自己的变量和规则命名空间。如果两个模块 A 和 B 都使用名为 X 的变量,则每个模块都获得自己的 X 副本。它们不会以任何方式相互干扰。同样,将规则导入到一个模块对任何其他模块都没有影响。

每个模块都有两个特殊变量。$(file) 包含模块加载的文件名,$(name) 包含模块的名称。

$(file) 不包含文件的完整路径。如果您需要此信息,请使用 modules.binding
b2::modules::binding
Jam

rule binding ( module_name )

C++

value_ref binding(std::string module_name);

返回给定模块的文件系统绑定。

例如,模块可以使用以下方式获取其自身的位置

me = [ modules.binding $(__name__) ] ;
b2::modules::record_binding
Jam

rule record-binding ( module_name : binding )

C++

void record_binding(std::string module_name, value_ref value);

此助手由 load 使用,以记录每个加载模块的绑定(路径)。

module_name 被忽略。而是使用内部跟踪的当前加载模块来记录绑定。
b2::modules::poke
Jam

rule poke ( module_name ? : variables + : value * )

C++

void poke(std::string module_name, list_cref variables, list_cref value);

设置变量的模块本地值。这是在不同模块中设置模块本地变量的最可靠方法;它消除了由于动态作用域引起的名字遮蔽问题。

例如,要在全局模块中设置一个变量

modules.poke : ZLIB_INCLUDE : /usr/local/include ;
b2::modules::peek
Jam

rule peek ( module_name ? : variables + )

C++

list_ref peek(std::string module_name, list_cref variables);

返回变量的模块本地值。这是在不同模块中检查模块本地变量的最可靠方法;它消除了由于动态作用域引起的名字遮蔽问题。

例如,要从全局模块读取一个变量

local ZLIB_INCLUDE = [ modules.peek : ZLIB_INCLUDE ] ;
b2::modules::clone_rules
Jam

rule clone-rules ( source_module target_module )

C++

void clone_rules(std::tuple<std::string, std::string> source_target_modules);

target_module 中定义从 source_module 导出的所有规则的导出副本。还要使它们在全局模块中可用,并带有限定符,这样就好像这些规则最初是在 target_module 中定义的一样。

b2::modules::call_in
Jam

rule call-in ( module-name ? : rule-name args * : * )

C++

list_ref call_in(value_ref module_name, std::tuple<value_ref, list_ref> const lists & rest, bind::context_ref_ context_ref);

在给定模块中本地调用给定规则。对于接受规则名称作为参数的规则,请使用此方法,以便可以在规则调用者的上下文中调用传递的规则(例如,如果该规则访问模块全局变量或是一个本地规则)。请注意,以这种方式调用的规则最多可以接受 18 个参数。

示例

rule filter ( f : values * )
{
	local m = [ CALLER_MODULE ] ;
	local result ;
	for v in $(values)
	{
		if [ modules.call-in $(m) : $(f) $(v) ]
		{
			result += $(v) ;
		}
	}
	return result ;
}
b2::modules::call_locally
Jam

rule call-locally ( qualified-rule-name args * : * )

C++

list_ref call_locally(std::tuple<value_ref, list_ref> rule_name_a1, const lists & rest, const bind::context_ref_ & context_ref);

给定一个可能限定的规则名称和参数,从规则中删除任何初始模块限定,并在该模块中调用它。如果没有模块限定,则在全局模块中调用该规则。请注意,以这种方式调用的规则最多可以接受 18 个参数。

b2::modules::run_tests
Jam

rule run-tests ( m )

C++

void run_tests(value_ref m, bind::context_ref_ context_ref);

为指定的模块运行内部 B2 单元测试。模块的 test 规则在其自身的模块中执行,以消除测试模块依赖项(例如 assert)对模块本身产生的任何意外影响。

b2::modules::load
Jam

rule load ( module-name : filename ? : search * )

C++

void load(value_ref module_name, value_ref filename, list_cref search, bind::context_ref_ context_ref);

如果指示的模块尚未加载,则加载它。

module-name

要加载的模块名称。

filename

(部分)文件路径;默认为 $(module-name).jam

search

在其中搜索文件名的目录。默认为 $(BOOST_BUILD_PATH)

b2::modules::import
Jam

rule import ( module-names + : rules-opt * : rename-opt * )

C++

void import(list_cref module_names, list_cref rules_opt, list_cref rename_opt, bind::context_ref_ context_ref);

加载指示的模块并将规则名称导入到当前模块中。rules-opt 的任何成员都将在调用者的模块中无需限定即可使用。rename-opt 的任何成员都将作为调用者模块中规则的名称,以代替它们在导入模块中拥有的名称。如果 rules-opt = *,则指示模块中的所有规则都将导入到调用者的模块中。如果提供了 rename-opt,则它必须具有与 rules-opt 相同数量的元素。

import 规则在所有模块中都无需限定即可使用。

示例

import path ;
import path : * ;
import path : join ;
import path : native make : native-path make-path ;

6.5.2. class 模块。

b2::class::make
Jam

rule new ( class args * : * )

C++

std::string make(std::tuple<value_ref, list_ref> name_arg1, const lists & rest, bind::context_ref_ context_ref)

实例化给定类的新实例,并使用给定的参数调用 init。返回实例 ID。

b2::class::bases
Jam

rule bases ( class )

C++

list_ref bases(std::string class_name);

返回给定类的基类。

b2::class::is_derived
Jam

rule is-derived ( class : bases + )

C++

bool is_derived(value_ref class_name, list_cref class_bases);

当给定类派生自给定基类时,返回 true。

b2::class::is_instance
Jam

rule is-instance ( value )

C++

bool is_instance(std::string value);

如果给定值是任何类的实例,则返回 true。

b2::class::is_a
Jam

rule is-a ( instance : type )

C++

bool is_a(std::string instance, value_ref type);

如果给定实例是给定类型,则返回 true。

6.5.3. errors 模块。

b2::jam::errors::backtrace
Jam

rule backtrace ( skip-frames prefix messages * : * )

C++

void backtrace(std::tuple<int, std::string, list_ref> skip_prefix_messages, const lists & rest, bind::context_ref_ context_ref);

打印导致此规则调用者的堆栈回溯。每个参数代表回溯的第一行之后要打印的输出行。

b2::jam::errors::error_skip_frames
Jam

rule error-skip-frames ( skip-frames messages * : * )

C++

void error_skip_frames(std::tuple<int, list_ref> skip_messages, const lists & rest, bind::context_ref_ context_ref);

b2::jam::errors::try-catch
Jam

rule error-skip-frames ( skip-frames messages * : * )

C++

void error_skip_frames(std::tuple<int, list_ref> skip_messages, const lists & rest, bind::context_ref_ context_ref);

这不是真正的异常处理机制,但它确实允许我们对错误检查执行一些错误检查。在 try 之后会抑制错误,并记录第一个错误。使用 catch 来检查错误消息是否与预期匹配。

Jam

rule try ( )

C++

void error_try();

开始查找错误消息。

Jam

rule catch ( messages * : * )

C++

void error_catch(const lists & rest, bind::context_ref_ context_ref);

停止查找错误消息;如果错误调用中没有找到消息的参数,则生成错误。

b2::jam::errors::error
Jam

rule error ( messages * : * )

C++

void error(const lists & rest, bind::context_ref_ context_ref);

打印错误消息,其中包含堆栈回溯并退出。

b2::jam::errors::user_error
Jam

rule user-error ( messages * : * )

C++

void user_error(const lists & rest, bind::context_ref_ context_ref);

与 'error' 相同,但生成的堆栈回溯将仅包含用户文件。

b2::jam::errors::warning
Jam

rule warning ( messages * : * )

C++

void warning(const lists & rest, bind::context_ref_ context_ref);

打印警告消息,其中包含堆栈回溯并退出。

b2::jam::errors::lol_to_list
Jam

rule lol→list ( * )

C++

list_ref lol_to_list(const lists & rest);

将任意参数列表转换为列表,其中包含 ":" 分隔符和带引号的元素,表示相同的信息。这主要用于格式化报告错误时规则调用的参数描述。

b2::jam::errors::nearest_user_location
Jam

rule nearest-user-location ( )

C++

list_ref nearest_user_location(bind::context_ref_ context_ref);

返回回溯中最接近用户模块的条目的 file:line。

6.5.4. regex 模块。

包含使用正则表达式进行字符串处理的规则。

  • "x*" 匹配模式 "x" 零次或多次。

  • "x+" 匹配 "x" 一次或多次。

  • "x?" 匹配 "x" 零次或一次。

  • "[abcd]" 匹配字符 "a""b""c""d" 中的任何一个。字符范围(例如 "[a-z]")匹配 "a""z" 之间的任何字符。"[^abc]" 匹配不是 "a""b""c" 的任何字符。

  • "x|y" 匹配模式 "x" 或模式 "y"

  • (x) 匹配 "x" 并捕获它。

  • "^" 匹配字符串的开头。

  • "$" 匹配字符串的结尾。

  • "<" 匹配单词的开头。

  • ">" 匹配单词的结尾。

另请参阅: MATCH

b2::regex_split
Jam

rule split ( string separator )

C++

b2::list_ref rb2::egex_split(const std::tuple<b2::value_ref, b2::value_ref> & string_separator);

返回以下子字符串的列表

  1. 从开头到第一次出现 'separator' 或到结尾,

  2. 在每次出现 'separator' 和下一次出现之间,

  3. 从最后一次出现 'separator' 到结尾。

如果不存在分隔符,则结果将仅包含一个元素。

b2::regex_split_each
Jam

rule split-list ( list * : separator )

C++

b2::list_ref b2::regex_split_each(b2::list_cref to_split, b2::value_ref separator);

返回将 regex.split 应用于列表中每个元素并使用分隔符模式的串联结果。

b2::regex_match
Jam

rule match ( pattern : string : indices * )

C++

b2::list_ref regex_match(b2::value_ref pattern, b2::value_ref string, const std::vector<int_t> & indices);

stringpattern 匹配,并返回由 indices 指示的元素。

b2::regex_transform
Jam

rule transform ( list * : pattern : indices * )

C++

b2::list_ref regex_transform(b2::list_cref list, b2::value_ref pattern, const std::vector<int_t> & indices);

list 的所有元素与 pattern 匹配,并返回由所有成功匹配的索引指示的元素列表。如果省略 indices,则返回所有成功匹配的第一个带括号组的列表。

b2::regex_escape
Jam

rule escape ( string : symbols : escape-symbol )

C++

b2::value_ref regex_escape(b2::value_ref string,b2:: value_ref symbols, b2::value_ref escape_symbol);

使用转义符号 escape-symbol 转义给定字符串中 symbols 中的所有字符,并返回转义后的字符串。

b2::regex_replace
Jam

rule replace ( string match replacement )

C++

b2::value_ref regex_replace(const std::tuple<b2::value_ref, b2::value_ref, b2::value_ref> & string_match_replacement);

替换给定字符串中匹配字符串的出现位置,并返回新字符串。匹配字符串可以是正则表达式。

  • string — 要修改的字符串。

  • match — 要替换的字符。

  • replacement — 用于替换的字符串。

b2::regex_replace_each
Jam

rule replace-list ( list * : match : replacement )

C++

b2::list_ref regex_replace_each(b2::list_cref list, b2::value_ref match, b2::value_ref replacement);

替换给定字符串列表中的匹配字符串的出现位置,并返回新字符串列表。匹配字符串可以是正则表达式。

  • list — 要修改的字符串列表。

  • match — 搜索表达式。

  • replacement — 用于替换的字符串。

b2::regex_grep
Jam

rule grep ( directories + : files + : patterns + : result_expressions * : options * )

C++

b2::list_ref regex_grep(b2::list_cref directories, b2::list_cref files, b2::list_cref patterns, list_cref result_expressions, list_cref options);

将任何 patternsdirectories 中 globbed 的 files 匹配,并返回文件和指示的 result_expressions 列表(file1、re1、re..、…​)。result_expressions 是从 010 的索引。其中 0 是完整匹配。

6.5.5. set 模块。

用于操作唯一值集合的类和函数。

b2::set

Set 类包含唯一值。

b2::set::add
Jam

rule add ( elements * )

C++

void b2::set::add(b2::list_cref elements);, void b2::set::add(const b2::set & elements);

elements 添加到集合中。

b2::set::contains
Jam

rule contains ( element )

C++

bool b2::set::contains(b2::value_ref element) const;

集合是否包含给定的 element

b2::set::to_list
Jam

rule list ( )

C++

b2::list_ref b2::set::to_list() const;

返回包含集合中所有元素的列表。

b2::set::difference
Jam

rule difference ( set1 * : set2 * )

C++

static b2::list_ref b2::set::difference(b2::list_cref set1, b2::list_cref set2);

返回 set1 中不在 set2 中的元素。

b2::set::intersection
Jam

rule intersection ( set1 * : set2 * )

C++

static b2::list_ref b2::set::intersection(b2::list_cref set1, b2::list_cref set2);

删除 set1set2 中都出现的所有项。

b2::set::equal
Jam

rule equal ( set1 * : set2 * )

C++

static bool b2::set::equal(b2::list_cref set1, b2::list_cref set2);

返回 set1set2 是否包含相同的元素。请注意,这会忽略任何元素排序差异以及任何元素重复项。

6.5.6. string 模块。

b2::string_whitespace
Jam

rule whitespace ( )

C++

b2::value_ref string_whitespace();

返回空格字符的规范集合,作为一个字符串。

b2::string_chars
Jam

rule chars ( string )

C++

b2::list_ref string_chars(b2::value_ref s);

将给定的 string 分割成一个字符串列表,该列表由 string 中每个字符按顺序组成。

b2::string_abbreviate
Jam

rule abbreviate ( string )

C++

b2::value_ref string_abbreviate(b2::value_ref s);

对字符串应用一组标准转换,以生成不超过 5 个字符的缩写。

b2::string_join
Jam

rule join ( strings * : separator ? )

C++

b2::value_ref string_join(b2::list_cref strings, b2::value_ref separator);

连接给定的 strings,并在每个字符串之间插入给定的 separator

b2::string_words
Jam

rule words ( string : whitespace * )

C++

b2::list_ref string_words(std::string s, b2::list_cref whitespace);

将字符串拆分为空格分隔的单词。

b2::string_is_whitespace
Jam

rule is-whitespace ( string ? )

C++

bool string_is_whitespace(b2::value_ref s);

检查给定的 string 是否完全由空格组成。

6.5.7. version 模块。

b2::version_less
Jam

rule version-less ( lhs + : rhs + )

C++

bool version_less(const std::vector<int> & lhs, const std::vector<int> & rhs);

如果第一个版本 lhs 在语义上小于第二个版本 rhs,则返回 true

6.5.8. db 模块。

用于管理结构化数据的类和函数。

b2::property_db (property-db)

值的容器,结构化为树,其键是值的树路径。支持数组和对象(命名字段)。

b2::property_db::emplace
Jam

rule emplace ( key + : value )

C++

void emplace(list_cref k, value_ref v);

在路径 key 处使用 value 设置或添加元素。路径可以包含两种位置项:数组索引或对象成员。数组索引为 "[]" n。其中 "[]" 表示数组中从零开始的索引,后跟 n。其他任何内容都视为成员字段名称。

b2::property_db::write_file
Jam

rule write-file ( filename : format ? )

C++

void write_file(value_ref filename, value_ref format);

将数据的表示形式以 format 格式写入给定的 filename 文件。支持的格式为:JSON

b2::property_db::dump
Jam

rule dump ( format ? )

C++

std::string dump(value_ref format);

将数据的表示形式作为字符串以 format 格式写入。支持的格式为:JSON

6.5.9. path

执行各种路径操作。路径始终以“规范化”表示形式存在。其中,路径可以是

  • '.',或

  • ['/'] [ ( '..' '/' )* (token '/')* token ]

简而言之,路径可以是根路径,'..' 元素仅允许在开头,并且永远不会以斜杠结尾,除非路径仅由斜杠组成。

  1. rule make ( native )

    将本机路径转换为规范化形式。

  2. rule native ( path )

    构建路径的本机表示形式。

  3. rule is-rooted ( path )

    测试路径是否为根路径。

  4. rule has-parent ( path )

    测试路径是否具有父路径。

  5. rule basename ( path )

    返回不带任何目录组件的路径。

  6. rule parent ( path )

    返回路径的父目录。如果不存在父目录,则会发出错误。

  7. rule reverse ( path )

    返回 path2,使得 [ join path path2 ] = "."。路径可能不包含 ".." 元素或为根路径。

  8. rule join ( elements + )

    连接传递的路径元素。如果除第一个元素之外的任何元素是根路径,则会生成错误。跳过任何空路径元素或未定义的路径元素。

  9. rule root ( path root )

    如果 path 是相对路径,则它以 root 为根。否则,它将保持不变。

  10. rule pwd ( )

    返回当前工作目录。

  11. rule glob ( dirs * : patterns + : exclude-patterns * )

    返回与指定目录中的给定模式匹配的文件列表。目录和模式都作为可移植路径提供。每个模式都应该是非绝对路径,并且不能包含“.”或“..”元素。模式的每个斜杠分隔元素都可以包含以下特殊字符

    • “?” 匹配任何字符

    • “*” 匹配任意数量的字符

      文件 $(d)/e1/e2/e3(其中 'd' 在 $(dirs) 中)匹配模式 p1/p2/p3 当且仅当 e1 匹配 p1,e2 匹配 p2,依此类推。例如

      [ glob . : *.cpp ]
      [ glob . : */build/Jamfile ]
  12. rule glob-tree ( roots * : patterns + : exclude-patterns * )

    glob 的递归版本。构建文件的 glob,同时也在给定根目录的子目录中搜索。可选的排除模式集将从结果中过滤掉匹配的条目。排除项也适用于子目录扫描,因此与排除模式匹配的目录将不会被搜索。

  13. rule exists ( file )

    如果指定的文件存在,则返回 true。

  14. rule all-parents ( path : upper_limit ? : cwd ? )

    找出路径的绝对名称,并返回所有父级的列表,从直接父级开始。父级以相对名称返回。如果指定了 upper_limit,则会修剪高于它的目录。

  15. rule glob-in-parents ( dir : patterns + : upper-limit ? )

    dir 的父目录中搜索 patterns,直到并包括 upper_limit(如果指定),否则直到文件系统根目录。

  16. rule relative ( child parent : no-error ? )

    假设 childparent 的子目录,则返回从 parentchild 的相对路径。

  17. rule relative-to ( path1 path2 )

    返回 path2 的最小路径,该路径相对于 path1。

  18. rule programs-path ( )

    返回操作系统用于查找程序的路径列表。

  19. rule makedirs ( path )

    创建一个目录和所有尚不存在的父目录。

6.5.10. sequence

各种有用的列表函数。请注意,此模块中的算法主要在调用者的模块命名空间中执行,以便可以将本地规则用作函数对象。另请注意,大多数谓词可以是多元素列表。在这种情况下,除了第一个元素之外的所有元素都会添加到传递给由第一个元素命名的规则的第一个参数之前。

  1. rule filter ( predicate + : sequence * )

    返回 $(sequence) 的元素 e,对于这些元素 [ $(predicate) e ] 具有非空值。

  2. rule transform ( function + : sequence * )

    $(sequence) 的每个元素 e 返回一个由 [ $(function) $(e) ] 组成的新序列。

  3. rule reverse ( s * )

    以相反的顺序返回 s 的元素。

  4. rule insertion-sort ( s * : ordered * )

    使用 BinaryPredicate ordereds 进行插入排序。

  5. rule merge ( s1 * : s2 * : ordered * )

    使用 BinaryPredicate ordered 合并两个有序序列。

  6. rule join ( s * : joint ? )

    s 的元素连接成一个长字符串。如果提供了 joint,则将其用作分隔符。

  7. rule length ( s * )

    查找任何序列的长度。

  8. rule unique ( list * : stable ? )

    list 中移除重复项。如果传递了 stable,则元素的顺序将保持不变。

  9. 规则 max-element ( elements + : ordered ? )

    返回 elements 中的最大数字。使用 ordered 进行比较,如果未提供,则使用 numbers.less

  10. 规则 select-highest-ranked ( elements * : ranks * )

    返回 elements 中所有在并行列表 rank 中对应元素等于 rank 中最大值的元素。

6.5.11. stage

此模块定义了 install 规则,用于将一组目标复制到单个位置。

  1. 规则 add-install-dir ( name : suffix ? : parent ? : options * )

    定义一个命名的安装目录。

    例如,`add-install-dir foo : bar : baz ;` 创建了特性 `<install-foo>`,并为 `install` 规则添加了对命名目录 `(foo)` 的支持。该规则将尝试使用 `<install-foo>` 属性的值(如果存在),否则将回退到 `(baz)/bar`。

    参数

    • `name`:目录的名称。

    • `suffix`:附加到父命名目录的路径后缀。

    • `parent`:父命名目录的可选名称。

    • `options`:修改目录处理方式的特殊选项。允许的选项

      • `package-suffix`:将包名称附加到默认值。例如

        add-install-dir foo : bar : baz : package-suffix ;
        install (foo) : a : <install-package>xyz ;

        将 `a` 安装到 `(baz)/bar/xyz`。

  2. 规则 install-dir-names ( )

    返回所有已注册安装目录的名称。

  3. 规则 get-dir ( name : property-set : package-name : flags * )

    返回命名安装目录的路径。对于给定的 `name=xyz`,如果 `property-set` 中存在 `<install-xyz>` 属性,则该规则使用其值。否则,它会尝试递归地构造路径的默认值,获取 `name` 的已注册基本命名目录和相对路径的路径。例如

    stage.add-install-dir foo : bar : baz ;
    
    local ps = [ property-set.create <install-foo>x/y/z ] ;
    echo [ stage.get-dir foo : $(ps) : $(__name__) ] ; # outputs x/y/z
    
    ps = [ property-set.create <install-baz>a/b/c/d ] ;
    echo [ stage.get-dir foo : $(ps) : $(__name__) ] ; # outputs a/b/c/d/bar

    参数 `package-name` 用于构造使用 `package-suffix` 选项注册的命名目录的路径,以及在以 Windows 为目标时构造 `install-prefix`。

    可用的 `flags`

    • `staged`:考虑 `staging-prefix`

    • `relative`:返回相对于其基本目录的 `name` 的路径。

  4. 规则 get-package-name ( property-set : project-module ? )

    返回在构造安装位置时将用于 `install` 目标的包名称。如果 `property-set` 中存在 `<install-package>` 属性,则该规则使用其值。否则,它将使用 `project-module` 的属性推导出包名称。它会遍历项目层次结构直到根目录,搜索第一个具有 id 的项目。如果未找到,则使用根项目位置的基本名称。如果 `project-module` 为空,则使用调用方模块(这允许在项目 jam 文件中仅调用 `[ get-package-name $(ps) ]`)。

6.5.12. type

处理目标类型声明,并定义支持类型化目标的目标类。

  1. 规则 register ( type : suffixes * : base-type ? )

    注册一个目标类型,可能派生自 `base-type`。在此处提供后缀列表是分别使用给定后缀调用 register-suffixes 规则和使用第一个给定后缀调用 set-generated-target-suffix 规则的快捷方式。

  2. 规则 register-suffixes ( suffixes + : type )

    指定后缀来自 `suffixes` 的文件应被识别为 `type` 类型的目标。如果已为任何后缀指定了不同的类型,则会发出错误。

  3. 规则 registered ( type )

    如果类型已注册,则返回 true。

  4. 规则 validate ( type )

    如果 `type` 未知,则发出错误。

  5. 规则 set-scanner ( type : scanner )

    设置将用于此类型的扫描器类。

  6. 规则 get-scanner ( type : property-set )

    返回适用于 `type` 和 `property-set` 的扫描器实例。

  7. 规则 base ( type )

    返回给定类型的基础类型;如果给定类型不是派生的,则不返回任何内容。

  8. 规则 all-bases ( type )

    按与类型的距离顺序返回给定类型及其所有基础类型。

  9. 规则 all-derived ( type )

    按与类型的距离顺序返回给定类型及其所有派生类型。

  10. 规则 is-derived ( type base )

    如果 `type` 等于 `base` 或以 `base` 作为其直接或间接基础,则返回 true。

  11. 规则 set-generated-target-suffix ( type : properties * : suffix )

    设置在生成具有指定属性的 `type` 目标时要使用的文件后缀。如果尚未为 `type` 指定后缀,则可以不带属性调用。`suffix` 参数可以是一个空字符串 (""),表示不应使用后缀。

    请注意,这不会导致后缀为 `suffix` 的文件被自动识别为 `type` 类型。两种不同的类型可以为其生成的文件使用相同的后缀,但对于具有该后缀的文件,只能自动检测到一种类型。用户应使用 register-suffixes 规则显式指定使用哪种类型。

  12. 规则 change-generated-target-suffix ( type : properties * : suffix )

    更改先前为此类型/属性组合注册的后缀。如果尚未指定后缀,则设置它。

  13. 规则 generated-target-suffix ( type : property-set )

    返回在生成具有给定属性的 `type` 文件时使用的后缀。

  14. 规则 set-generated-target-prefix ( type : properties * : prefix )

    设置在生成具有指定属性的 `type` 目标时应使用的目标前缀。如果尚未为 `type` 指定前缀,则可以不带属性调用。

    `prefix` 参数可以是一个空字符串 (""),表示不应使用前缀。

    用法示例:库名称在 unix 上使用 "lib" 前缀。

  15. 规则 change-generated-target-prefix ( type : properties * : prefix )

    更改先前为此类型/属性组合注册的前缀。如果尚未指定前缀,则设置它。

  16. 规则 generated-target-prefix ( type : property-set )

    返回在生成具有给定属性的 `type` 文件时使用的前缀。

  17. 规则 type ( filename )

    返回给定文件名的文件类型。如果文件名中有多个点,则尝试每个后缀。例如,对于名称 "file.so.1.2",将尝试后缀 "2"、"1" 和 "so"。

6.6. 内建类

6.6.1. 类 abstract-target

所有抽象目标的基础类。

class abstract-target {
    rule __init__ ( name : project )
    rule name ( )
    rule project ( )
    rule location ( )
    rule full-name ( )
    rule generate ( property-set )
}

派生自 abstract-target 的类

  • project-target

  • main-target

  • basic-target

  1. 规则 init ( name : project )

    name

    Jamfile 中目标的名称。

    project

    此目标所属的 project

  2. 规则 name ( )

    返回此目标的名称。

  3. 规则 project ( )

    返回此目标的 project

  4. 规则 location ( )

    返回声明目标的位置。

  5. 规则 full-name ( )

    返回此目标的用户可读名称。

  6. 规则 generate ( property-set )

    使用指定的属性为此抽象目标生成虚拟目标,除非目标需要某些特性的不同值。这是一个抽象方法,必须由派生类重写。

    成功时,返回

    • 一个 property-set,其中包含要应用于依赖项的用法要求

    • 生成的虚拟目标列表,可能为空。

      如果 `property-set` 为空,则以派生类特定的方式执行此目标的默认构建。

6.6.2. 类 project-target

class project-target : abstract-target {
    rule generate ( property-set )
    rule build-dir ( )
    rule main-target ( name )
    rule has-main-target ( name )
    rule find ( id : no-error ? )

    # Methods inherited from abstract-target
    rule name ( )
    rule project ( )
    rule location ( )
    rule full-name ( )
}

此类具有以下职责

  • 维护此项目中主目标的列表并构建它们。

  1. 规则 generate ( property-set )

    覆盖 abstract-target.generate。为此项目中包含的所有目标生成虚拟目标。

    成功时,返回

    • 一个 property-set,其中包含要应用于依赖项的用法要求

    • 生成的虚拟目标列表,可能为空。

  2. 规则 build-dir ( )

    返回项目的根构建目录。

  3. 规则 main-target ( name )

    返回与 `name` 对应的 main-target 类实例。只能在项目完全加载后调用。

  4. 规则 has-main-target ( name )

    返回是否具有指定名称的 main-target 存在。只能在项目完全加载后调用。

  5. 规则 find ( id : no-error ? )

    查找并返回具有指定 id 的目标,相对于自身处理。Id 可以指定目标或文件名,目标优先。如果未找到目标,则可能会报告错误或不返回任何内容,具体取决于 `no-error` 参数。

6.6.3. 类 main-target

class main-target : abstract-target {
    rule generate ( property-set )

    # Methods inherited from abstract-target
    rule name ( )
    rule project ( )
    rule location ( )
    rule full-name ( )
}

main-target 表示 Jamfile 中命名的顶层目标。

  1. 规则 generate ( property-set )

    覆盖 abstract-target.generate。为此主目标选择一个替代方案,方法是查找所有需求由 `property-set` 满足的替代方案,并选择需求集最长的那个。返回在该替代方案上调用 generate 的结果。

    成功时,返回

    • 一个 property-set,其中包含要应用于依赖项的用法要求

    • 生成的虚拟目标列表,可能为空。

6.6.4. 类 basic-target

class basic-target : abstract-target {
    rule __init__ ( name : project : sources * : requirements * : default-build * : usage-requirements * )
    rule generate ( property-set )
    rule construct ( name : source-targets * : property-set )

    # Methods inherited from abstract-target
    rule name ( )
    rule project ( )
    rule location ( )
    rule full-name ( )
}

实现了从源构造主目标替代方案的最标准方法。允许源是文件或其他主目标,并处理这些依赖目标的生成。

  1. 规则 init ( name : project : sources * : requirements * : default-build * : usage-requirements * )

    name

    目标的名称

    project

    声明目标的 project

  2. 规则 generate ( property-set )

    覆盖 abstract-target.generate。确定最终构建属性,生成源,并调用 construct。不应覆盖此方法。

    成功时,返回

    • 一个 property-set,其中包含要应用于依赖项的用法要求

    • 生成的虚拟目标列表,可能为空。

  3. 规则 construct ( name : source-targets * : property-set )

    为此抽象目标构造虚拟目标。返回用法要求 property-set 和虚拟目标列表。应在派生类中重写。

6.6.5. 类 typed-target

class typed-target : basic-target {
    rule __init__ ( name : project : type : sources * : requirements * : default-build * : usage-requirements * )
    rule type ( )
    rule construct ( name : source-targets * : property-set )

    # Methods inherited from abstract-target
    rule name ( )
    rule project ( )
    rule location ( )
    rule full-name ( )

    # Methods inherited from basic-target
    rule generate ( property-set )
  }

typed-target 是最常见的目标替代方案类型。创建类型化目标的规则是为每种类型自动定义的。

  1. 规则 init ( name : project : type : sources * : requirements * : default-build * : usage-requirements * )

    name

    目标的名称

    project

    声明目标的 project

    type

    目标的 type

  2. 规则 type ( )

    返回目标的 type

  3. 规则 construct ( name : source-targets * : property-set )

    实现 basic-target.construct。尝试使用适用于给定 property-set 的生成器创建正确类型的目标。返回一个 property-set,其中包含用法要求和虚拟目标列表。

    此函数由 basic-target.generate 自动调用,不应由用户直接调用。

6.6.6. 类 property-set

用于存储一组属性的类。

class property-set {
    rule raw ( )
    rule str ( )
    rule propagated ( )
    rule add ( ps )
    rule add-raw ( properties * )
    rule refine ( ps )
    rule get ( feature )
}

标识和值之间存在 1<→1 的对应关系。该类的任何两个实例都不相等。为了保持此属性,应使用 'property-set.create' 规则创建新实例。实例是不可变的。

  1. 规则 raw ( )

    返回存储属性的 Jam 列表。

  2. 规则 str ( )

    返回存储属性的字符串表示形式。

  3. 规则 propagated ( )

    返回一个 property-set,其中包含此 property-set 中的所有 propagated 属性。

  4. 规则 add ( ps )

    返回一个新的 property-set,其中包含此 property-setps 中的属性的并集。

    如果 ps 包含应覆盖此对象中值的非 free 属性,请改用 refine
  5. 规则 add-raw ( properties * )

    类似于 add,但它接受属性列表而不是 property-set

  6. 规则 refine ( ps )

    通过覆盖在 ps 中指定了不同值的任何非 free 和非条件属性来细化属性。返回结果 property-set

  7. 规则 get ( feature )

    返回 `feature` 的所有值。

6.7. 构建过程

构建过程的总体概述在 用户文档 中给出。本节提供更多详细信息和一些特定规则。

概括来说,使用特定属性构建目标包括以下步骤

  1. 应用默认构建,

  2. 选择要使用的主目标替代方案,

  3. 确定“通用”属性,

  4. 构建源列表和依赖属性引用的目标,

  5. 将构建依赖项时生成的用法要求添加到“通用”属性,

  6. 使用生成器构建目标,

  7. 计算要返回的用法要求。

6.7.1. 替代方案选择

当目标有多个替代方案时,必须选择其中一个。过程如下

  1. 对于每个替代方案,其条件定义为其需求中 基础属性 的集合。 条件属性 被排除在外。

  2. 只有当其条件中的所有属性都存在于构建请求中时,替代方案才是可行的。

  3. 如果只有一个可行的替代方案,则选择它。否则,尝试找到最佳替代方案。如果替代方案 b 的条件中的属性集是替代方案 a 的条件中的属性集的严格子集,则替代方案 a 比另一个替代方案 b 更好。如果一个可行的替代方案优于所有其他替代方案,则选择它。否则,报告错误。

6.7.2. 确定通用属性

“通用”属性是一个有点人为的术语。这是中间属性集,从中派生出依赖项的构建请求和用于构建目标的属性。

由于默认构建和替代方案已处理,我们只有两个输入:构建请求和需求。以下是关于通用属性的规则。

  1. 非 free 特性只能有一个值

  2. 需求中的非条件属性始终存在于通用属性中。

  3. 构建请求中的属性存在于通用属性中,除非它被需求中的属性覆盖。

  4. 如果构建请求或需求(非条件或条件)包含可扩展属性(复合属性或具有指定的子特性值),则行为等同于分别将所有扩展属性显式添加到构建请求或需求。

  5. 如果需求包括 条件属性,并且此属性的条件在通用属性的上下文中为真,则条件属性也应在通用属性中。

  6. 如果此处其他规则未给出特性的值,则它在通用属性中具有默认值。

这些规则是声明性的。它们没有指定如何计算通用属性。但是,它们为用户提供了足够的信息。重要的一点是条件要求的处理。条件可以通过构建请求中的属性、非条件要求,甚至另一个条件属性来满足。例如,以下示例按预期工作

exe a : a.cpp
      : <toolset>gcc:<variant>release
        <variant>release:<define>FOO ;

6.7.3. 目标路径

有几个因素决定了具体文件目标的位置。项目中的所有文件都构建在 bin 目录下,除非这被 build-dir 项目属性覆盖。bin 下的路径取决于用于构建每个目标的属性。此路径由所有非 free、非 incidental 属性唯一确定。例如,给定一个包含以下内容的属性集:<toolset>gcc <toolset-gcc:version>4.6.1 <variant>debug <warnings>all <define>_DEBUG <include>/usr/local/include <link>static,路径将为 gcc-4.6.1/debug/link-static<warnings> 是 incidental 特性,<define><include> 是 free 特性,因此它们不影响路径。

有时 B2 生成的路径可能会变得过长。有一些命令行选项可以帮助解决此问题。--abbreviate-paths 将每个元素缩减为不超过五个字符。例如,link-static 变为 lnk-sttc--hash 选项使用 MD5 哈希将路径缩减为单个目录。

有两个特性会影响构建目录。<location> 特性完全覆盖默认构建目录。例如,

exe a : a.cpp : <location>. ;

在 Jamfile 的目录中构建 a 生成的所有文件。通常不建议这样做,因为它排除了变体构建。

<location-prefix> 特性在项目构建目录下向路径添加前缀。例如,

exe a : a.cpp : <location-prefix>subdir ;

将在 bin/subdir/gcc-4.6.1/debug 中为 a 创建文件

6.8. 定义

6.8.1. 特性和属性

特性是构建配置的规范化(工具集无关)方面,例如是否启用内联。特性名称不得包含 ‘>’ 字符。

构建配置中的每个特性都有一个或多个关联的。非 free 特性的特性值不得包含尖括号(‘<’)、冒号(‘:’)、等号(‘=’)和短划线(‘-’)等标点符号。Free 特性的特性值不得包含尖括号(‘<’)字符。

属性是(特性,值)对,表示为 <feature>value。

子特性是一种仅在其父特性存在时才存在的特性,并且其标识可以(在其父特性的上下文中)从其值派生出来。子特性的父特性永远不能是另一个子特性。因此,特性及其子特性形成两级层次结构。

特性 F值字符串value-subvalue1-subvalue2…​-subvalueN 形式的字符串,其中 valueF 的合法值,而 subvalue1…​subvalueNF 的某些子特性的合法值,用短划线(‘-’)分隔。例如,属性 <toolset>gcc <toolset-version>3.0.1 可以使用值字符串更简洁地表示为 <toolset>gcc-3.0.1

属性集是一组属性(即,没有重复项的集合),例如:<toolset>gcc <runtime-link>static

属性路径是一个属性集,其元素已连接成一个由斜杠分隔的字符串。前一个示例的属性路径表示形式为 <toolset>gcc/<runtime-link>static

构建规范是一个属性集,它完整地描述了用于构建目标的特性集。

6.8.2. 属性有效性

对于 free 特性,所有值都有效。对于所有其他特性,有效值是显式指定的,并且构建系统将报告使用无效特性值的错误。子属性有效性可能受到限制,因此某些值仅在存在某些其他子属性时才有效。例如,可以指定 <gcc-target>mingw 属性仅在存在 <gcc-version>2.95.2 时才有效。

6.8.3. 特性属性

每个特性都有一组零个或多个以下属性。特性属性是对构建系统应如何在构建请求中解释特性值的低级描述。我们还引用属性的属性,因此例如,incidental 属性是其特性具有 incidental 属性的属性。

  • incidental

    Incidental 特性被认为根本不影响构建产品。因此,对于构建规范仅在 incidental 特性方面不同的目标,构建系统可以使用相同的文件。控制编译器警告级别的特性是可能的 incidental 特性的一个示例。

    Non-incidental 特性被认为会影响构建产品,因此,构建规范在 non-incidental 特性方面不同的目标的 files 被放置在不同的目录中,如 目标路径 中所述。

  • propagated

    此类特性会传播到依赖项。也就是说,如果使用 propagated 属性构建 主目标,则构建系统会尝试在构建其任何依赖项作为该主目标的一部分时使用相同的属性。例如,当请求优化的可执行文件时,通常希望将其与优化的库链接。因此,<optimization> 特性被传播。

  • free

    大多数特性都有一组有限的允许值,并且在给定的构建规范中只能从该集合中取单个值。另一方面,Free 特性可以一次有多个值,并且每个值都可以是任意字符串。例如,可以同时定义多个预处理器符号

    <define>NDEBUG=1 <define>HAS_CONFIG_H=1
  • optional

    Optional 特性是不需要在构建规范中出现的特性。每个非 optional 的非 free 特性都有一个默认值,当未在目标的 requirements 或用户的构建请求中另行指定特性的值时,将使用该默认值。[特性的默认值由特性声明中列出的第一个值给出。 — 将其移到别处 - dwa]

  • symmetric

    通常,特性仅当其值与其默认值不同时才生成子变体目录,从而导致特性的某些值出现不对称的子变体目录结构。Symmetric 特性始终生成相应的子变体目录。

  • path

    path 特性的值指定路径。当从不同的目录调用构建时,该路径被视为相对于使用 path 特性的 Jamfile 的目录,并由构建系统适当地转换

  • implicit

    Implicit 特性的值本身就标识了该特性。例如,用户不需要编写 "<toolset>gcc",而只需编写 "gcc" 即可。Implicit 特性名称也不会出现在变体路径中,尽管值会。因此:bin/gcc/…​ 而不是 bin/toolset-gcc/…​。通常应该只有少数此类特性,以避免可能的名称冲突。

  • composite

    Composite 特性实际上对应于属性组。例如,构建变体是一个 composite 特性。当从一组构建属性生成目标时,composite 特性会递归展开并添加到构建属性集中,以便规则可以在必要时找到它们。Non-composite 的非 free 特性会覆盖构建属性集中 composite 特性的组件。

  • dependency

    dependency 特性的值是目标引用。当用于构建主目标时,dependency 特性的值被视为附加依赖项。

    例如,dependency 特性允许声明库 A 依赖于库 B。结果,每当应用程序链接到 A 时,它也会链接到 B。将 B 指定为 A 的依赖项与将 B 添加到 A 的源不同。

既不是 free 也不是 incidental 的特性称为基础特性。

6.8.4. 特性声明

低级特性声明接口是 feature 模块中的 feature 规则

rule feature ( name : allowed-values * : attributes * )

特性的 allowed-values 可以使用 feature.extend 规则扩展。

6.8.5. 属性细化

当请求具有某些属性的目标,并且该目标需要一组属性时,需要找到用于构建的属性集。此过程称为属性细化,由以下规则执行

  1. 所需集合中的每个属性都添加到原始属性集中

  2. 如果原始属性集包含具有不同非 free 特性值的属性,则该属性将被删除。

6.8.6. 条件属性

有时,希望仅对其他属性的特定组合应用某些要求。例如,您使用的编译器之一发出一个无意义的警告,您希望通过向其传递命令行选项来抑制该警告。您不会希望将该选项传递给其他编译器。条件属性允许您这样做。它们的语法是

property ( "," property ) * ":" property

例如,上述问题可以通过以下方式解决

exe hello : hello.cpp : <toolset>yfc:<cxxflags>-disable-pointless-warning ;

该语法还允许条件中包含多个属性,例如

exe hello : hello.cpp : <os>NT,<toolset>gcc:<link>static ;

6.8.7. 目标标识符和引用

目标标识符用于表示目标。语法是

target-id -> (target-name | file-name | project-id | directory-name)
              | (project-id | directory-name) "//" target-name
project-id -> path
target-name -> path
file-name -> path
directory-name -> path

此语法允许将某些元素识别为以下任一类型

  • 在当前 Jamfile 中声明的目标的名称(请注意,目标名称可能包含斜杠)。

  • 常规文件,由绝对名称或相对于项目源位置的名称表示。

  • 项目 id(此时,所有项目 id 都以斜杠开头)。

  • 另一个项目的目录,由绝对名称或相对于当前项目位置的名称表示。

为了确定真实含义,将按此顺序检查可能的解释。例如,有效的目标 id 可能为

a

当前项目中的目标

lib/b.cpp

常规文件

/boost/thread

项目 "/boost/thread"

/home/ghost/build/lr_library//parser

特定项目中的目标

../boost_1_61_0

特定目录中的项目

理由:目标与项目通过特殊分隔符(不仅仅是斜杠)分隔,因为

  • 它强调项目和目标是不同的事物。

  • 它允许主目标名称带有斜杠。

目标引用用于指定源目标,并且可以另外指定该目标的所需属性。它具有以下语法

target-reference -> target-id [ "/" requested-properties ]
requested-properties -> property-path

例如,

exe compiler : compiler.cpp libs/cmdline/<optimization>space ;

即使 compiler 可执行文件以速度优化构建,也会导致链接为空间优化的 cmdline 库的版本。

7. 实用工具

7.1. 调试器

7.1.1. 概述

B2 自带 Jamfile 的调试器。要运行调试器,请使用 b2 -dconsole 启动 B2。

$ b2 -dconsole
(b2db) break gcc.init
Breakpoint 1 set at gcc.init
(b2db) run
Starting program: /usr/bin/b2
Breakpoint 1, gcc.init ( ) at /usr/share/b2/tools/gcc.jam:74
74      local tool-command = ;
(b2db) quit

7.1.2. 运行程序

run 命令用于启动新的 b2 子进程进行调试。run 的参数在命令行上传递。如果子进程已在运行,则会在启动新子进程之前将其终止。

当程序暂停时,continue 将恢复执行。step 命令将使程序前进一个语句,在进入另一个函数或从当前函数返回时停止。next 类似于 step,但它会跳过函数调用。finish 执行直到当前函数返回。

kill 命令立即终止当前子进程。

7.1.3. 断点

断点使用 break 命令设置。断点的位置可以指定为函数名称(包括模块名称),或 file:line 形式的文件名和行号。创建断点时,会为其指定一个唯一 id,该 id 用于在其他命令中标识它。

(b2db) break Jamfile:10
Breakpoint 1 set at Jamfile:10
(b2db) break msvc.init
Breakpoint 2 set at msvc.init

可以使用 disable 命令临时禁用断点。当断点被禁用时,子进程在命中该断点时不会停止。可以使用 enable 再次激活禁用的断点。

(b2db) disable 1
(b2db) enable 1

可以使用 deleteclear 永久删除断点。它们之间的区别在于 delete 接受断点 id,而 clear 接受断点的位置,即最初指定断点的位置。

(b2db) clear Jamfile:10
Deleted breakpoint 1
(b2db) delete 2

7.1.4. 检查堆栈

backtrace 命令将打印堆栈上每个帧的摘要。

print 命令可用于显示表达式的值。

(b2db) print [ modules.peek : ARGV ]
/usr/bin/b2 toolset=msvc install
(b2db) print $(__file__)
Jamfile.jam

7.1.5. 杂项命令

quit 退出调试器。help 描述可用命令。

8. Extender 手册

8.1. 简介

本节介绍如何扩展 B2 以适应您的本地需求,主要是为了添加对您拥有的非标准工具的支持。在开始之前,请务必阅读并理解元目标的概念,概念,这对于理解其余内容至关重要。

当前版本的 B2 具有三个级别的目标,如下所示。

元目标 (metatarget)

从 Jamfile 中的声明创建的对象。可以使用一组属性调用,以生成具体目标。

具体目标 (concrete target)

对应于文件或操作的对象。

Jam 目标 (jam target)

特定于 Boost.Jam 构建引擎的底层具体目标。本质上是一个字符串,通常是文件名。

在大多数情况下,您只需要处理具体目标以及从元目标创建具体目标的过程。扩展元目标级别很少需要。Jam 目标通常仅在命令行模式中使用。

所有 Boost.Jam 与目标相关的内置函数,例如 DEPENDSALWAYS,都在 Jam 目标上运行。将它们应用于元目标或具体目标无效。

8.1.1. 元目标

元目标是一个对象,它记录 Jamfile 中指定的信息,例如元目标类型、名称、源和属性,并且可以使用特定属性调用以生成具体目标。在代码级别,它由从 abstract-target 派生的类的实例表示。[4]

generate 方法接受构建属性(作为 property-set 类的实例),并返回包含以下内容的列表

  • 作为首个元素 — 来自此调用的 Usage-requirements(property-set 的实例)

  • 作为后续元素 — 创建的具体目标(virtual-target 类的实例。)

可以使用 targets.resolve-reference 函数按 target-id 查找元目标,targets.generate-from-reference 函数可以查找和生成元目标。

abstract-target 类有三个直接派生类

  • project-target 对应于项目,不打算进一步子类化。此类的 generate 方法构建项目中所有未标记为显式目标的目标。

  • main-target 对应于项目中的目标,包含一个或多个目标备选项。此类也不应被子类化。此类的 generate 方法选择要构建的备选项,并调用该备选项的 generate 方法。

  • basic-target 对应于特定的目标备选项。这是基类,有许多派生类。 generate 方法处理目标需求和请求的构建属性,以确定目标的最终属性,构建所有源,最后使用源虚拟目标列表和最终属性调用抽象 construct 方法。

project-targetmain-target 类的实例是隐式创建的 — 当加载新的 Jamfile 时,或者当创建具有尚未知名称的新目标备选项时。从 basic-target 派生的类的实例通常在 Jamfile 调用元目标规则时创建,例如 exe

允许创建从 basic-target 派生的自定义类,并创建新的元目标规则来创建此类目标的实例。但是,在大多数情况下,使用 basic-target 的特定子类 — typed-target。该类与类型关联,并中继到生成器以构造该类型的具体目标。此过程将在下面解释。当声明新类型时,会自动定义新的元目标规则。该规则创建与该类型关联的 type-target 的新实例。

8.1.2. 具体目标

具体目标由从 virtual-target 派生的类的实例表示。最常用的子类是 file-target。文件目标与创建它的操作相关联 — action 类的实例。操作反过来又包含源目标列表。它还包含 property-set 实例,其中包含应用于该操作的构建属性。

以下是从另一个目标 source 创建目标的示例

local a = [ new action $(source) : common.copy : $(property-set) ] ;
local t = [ new file-target $(name) : CPP : $(project) : $(a) ] ;

第一行创建 action 类的实例。第一个参数是源列表。第二个参数是 jam 级别的 action 的名称。第三个参数是应用于此操作的 property-set。第二行创建一个目标。我们指定名称、类型和项目。我们还传递之前创建的 action 对象。如果 action 创建了多个目标,我们可以重复第二行多次。

在某些情况下,创建具体目标的代码可能会使用相同的属性多次调用。返回对应于同一文件的两个不同的 file-target 实例显然会导致问题。因此,每当返回目标时,您都应该通过 virtual-target.register 函数传递它们,除了允许 B2 跟踪为每个元目标创建了哪些虚拟目标之外,这还将根据需要用先前创建的相同目标替换目标。[5] 以下是几个示例

return [ virtual-target.register $(t) ] ;
return [ sequence.transform virtual-target.register : $(targets) ] ;

8.1.3. 生成器

理论上,B2 中的每种元目标(例如 exelibobj)都可以通过编写一个新的元目标类来实现,该类独立于其他代码,确定要生成哪些文件以及要使用哪些命令。但是,这将相当不灵活。例如,添加对新编译器的支持将需要编辑多个元目标。

实际上,大多数文件都有特定的类型,并且大多数工具使用和生成特定类型的文件。为了利用这一事实,B2 定义了目标类型和生成器生成器的概念,并具有特殊的元目标类 typed-target。目标类型仅仅是一个标识符。它与一组对应于该类型的文件扩展名相关联。生成器是工具的抽象。它声明它生成的类型,并且如果使用一组输入目标调用,则尝试构造声明类型的输出目标。最后,typed-target 与特定目标类型相关联,并中继该类型的生成器(或多个生成器)。

生成器是从 generator 派生的类的实例。 generator 类本身适用于常见情况。您可以为自定义场景定义派生类。

8.2. 示例:1 对 1 生成器

假设您正在编写一个生成 C++ 代码的应用程序。如果您曾经这样做过,您就会知道这并不好。在字符串文字中嵌入大量 C++ 代码非常笨拙。更好的解决方案是

  1. 编写要生成的代码的模板,在将更改的点留下占位符

  2. 在您的应用程序中访问模板,并将占位符替换为适当的文本。

  3. 写入结果。

这很容易实现。您编写特殊的 verbatim 文件,这些文件只是 C++,只是文件的第一行包含应生成的变量的名称。创建一个简单的工具,该工具接受 verbatim 文件并创建一个 cpp 文件,其中包含一个 char* 变量,该变量的名称取自 verbatim 文件的第一行,其值是文件正确引用的内容。

让我们看看 B2 可以做什么。

首先,B2 不知道“verbatim 文件”。因此,您必须注册一个新的目标类型。以下代码执行此操作

import type ;
type.register VERBATIM : verbatim ;

type.register 的第一个参数给出了声明类型的名称。按照惯例,它是大写的。第二个参数是此类型文件的后缀。因此,如果 B2 在源列表中看到 code.verbatim,它就知道它是 VERBATIM 类型。

接下来,您告诉 B2 verbatim 文件可以在一个构建步骤中转换为 C++ 文件。生成器是将一种类型(或一组类型)的目标转换为另一种类型的构建步骤的模板。我们的生成器将被称为 verbatim.inline-file;它将 VERBATIM 文件转换为 CPP 文件

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;

最后,您必须告知 B2 用于进行转换的 shell 命令。这是通过 actions 声明完成的。

actions inline-file
{
    "./inline-file.py" $(<) $(>)
}

现在,我们准备将所有内容联系在一起。将上面的所有代码放在文件 verbatim.jam 中,将 import verbatim ; 添加到 Jamroot.jam,并且可以在您的 Jamfile 中编写以下内容

exe codegen : codegen.cpp class_template.verbatim usage.verbatim ;

列出的 verbatim 文件将自动转换为 C++ 源文件,编译,然后链接到 codegen 可执行文件。

在后续章节中,我们将扩展此示例,并详细回顾所有机制。完整代码可在 example/customization 目录中找到。

8.3. 目标类型

我们在简介中所做的第一件事是声明一个新的目标类型

import type ;
type.register VERBATIM : verbatim ;

类型是目标最重要的属性。B2 可以自动生成必要的构建操作,仅仅是因为您指定了所需的类型(使用不同的主要目标规则),并且因为 B2 可以从源的扩展名猜测源的类型。

type.register 规则的前两个参数是新类型的名称和与之关联的扩展名列表。具有列表中扩展名的文件将具有给定的目标类型。在从其他源生成声明类型的目标的情况下,将使用第一个指定的扩展名。

有时您希望根据构建属性(例如工具集)更改用于生成目标的后缀。例如,某些编译器使用扩展名 elf 作为可执行文件。您可以使用 type.set-generated-target-suffix 规则

type.set-generated-target-suffix EXE : <toolset>elf : elf ;

新的目标类型可以从现有类型继承。

type.register PLUGIN : : SHARED_LIB ;

上面的代码定义了一个从 SHARED_LIB 派生的新类型。最初,新类型继承基类型的所有属性 — 特别是生成器和后缀。通常,您会以某种方式更改新类型。例如,使用 type.set-generated-target-suffix,您可以为新类型设置后缀。或者您可以为新类型编写特殊的生成器。例如,它可以为插件生成额外的元信息。无论哪种方式,PLUGIN 类型都可以像 SHARED_LIB 一样使用。例如,您可以将插件直接链接到应用程序。

类型可以定义为“main”,在这种情况下,B2 将自动声明一个主要目标规则来构建该类型的目标。更多详细信息可以在后面找到。

8.4. 扫描器

有时,文件可以通过某些包含系统引用其他文件。要使 B2 跟踪包含文件之间的依赖关系,您需要提供扫描器。主要的限制是只能为一个目标类型分配一个扫描器。

首先,我们需要为扫描器声明一个新类

class verbatim-scanner : common-scanner
{
    rule pattern ( )
    {
        return "//###include[ ]*\"([^\"]*)\"" ;
    }
}

所有复杂的逻辑都在 common-scanner 类中,您只需要覆盖返回用于扫描的正则表达式的方法。正则表达式中的括号指示字符串的哪一部分是包含文件的名称。只会识别正则表达式中的第一个带括号的组;如果您无法以这种方式表达您想要的一切,您可以返回多个正则表达式,每个正则表达式都包含一个要匹配的带括号的组。

之后,我们需要注册我们的扫描器类

scanner.register verbatim-scanner : include ;

第二个参数的值,在本例中为 include,指定包含应搜索包含文件的路径列表的属性。

最后,我们将新的扫描器分配给 VERBATIM 目标类型

type.set-scanner VERBATIM : verbatim-scanner ;

这足以扫描包含依赖项。

8.5. 工具和生成器

本节将介绍如何扩展 B2 以支持新工具。

对于每个附加工具,都必须创建一个名为生成器的 B2 对象。该对象具有它接受和生成的特定类型的目标。使用该信息,B2 能够自动调用生成器。例如,如果您声明一个生成器,该生成器接受类型为 D 的目标并生成类型为 OBJ 的目标,则当将扩展名为 .d 的文件放在源列表中时,将导致 B2 调用您的生成器,然后将生成的对象文件链接到应用程序中。(当然,这要求您指定 .d 扩展名对应于 D 类型。)

每个生成器都应该是从 generator 类派生的类的实例。在最简单的情况下,您不需要创建派生类,只需创建 generator 类的实例即可。让我们回顾一下我们在简介中看到的示例。

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
actions inline-file
{
    "./inline-file.py" $(<) $(>)
}

我们声明一个标准生成器,指定其 id、源类型和目标类型。调用时,生成器将创建一个类型为 CPP 的目标,并将类型为 VERBATIM 的源目标作为唯一源。但是,将使用什么命令来实际生成文件?在 B2 中,操作使用命名的“actions”块指定,并且在创建目标时应指定操作块的名称。按照惯例,生成器使用与其自身 id 相同的操作块名称。因此,在上面的示例中,“inline-file”操作块将用于将源转换为目标。

生成器主要有两种类型:标准生成器和组合生成器,分别使用 generators.register-standardgenerators.register-composing 规则注册。例如

generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
generators.register-composing mex.mex : CPP LIB : MEX ;

第一个(标准)生成器采用类型为 VERBATIM单个源并生成结果。第二个(组合)生成器接受任意数量的源,这些源可以具有 CPPLIB 类型。组合生成器通常用于生成顶级目标类型。例如,构建 exe 目标时调用的第一个生成器是对应于适当链接器的组合生成器。

您还应该了解用于注册生成器的两个特定函数:generators.register-c-compilergenerators.register-linker。第一个函数为 C 文件设置标头依赖项扫描,第二个函数处理各种复杂性,例如搜索到的库。因此,在添加对编译器和链接器的支持时,应始终使用这些函数。

(需要关于 UNIX 的注释)

自定义生成器类

标准生成器允许您指定源类型和目标类型、操作以及一组标志。如果您需要更复杂的功能,则需要创建一个新的生成器类,其中包含您自己的逻辑。然后,您必须创建该类的实例并注册它。以下是如何创建您自己的生成器类的示例

class custom-generator : generator
{
    rule __init__ ( * : * )
    {
        generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
    }

}

generators.register
  [ new custom-generator verbatim.inline-file : VERBATIM : CPP ] ;

此生成器的工作方式与我们上面定义的 verbatim.inline-file 生成器完全相同,但是可以通过覆盖 generator 类的方法来自定义行为。

有两个方法值得关注。 run 方法负责整个过程 — 它接受多个源目标,将它们转换为正确的类型,并创建结果。当所有源都转换为正确的类型以实际创建结果时,将调用 generated-targets 方法。

当您要向生成的目标添加其他属性或使用其他源时,可以覆盖 generated-targets 方法。对于一个真实的示例,假设您有一个程序分析工具,应为其提供可执行文件的名称和所有源的列表。当然,您不想手动列出所有源文件。以下是 generated-targets 方法如何自动查找源列表

class itrace-generator : generator {
...
    rule generated-targets ( sources + : property-set : project name ? )
    {
        local leaves ;
        local temp = [ virtual-target.traverse $(sources[1]) : : include-sources ] ;
        for local t in $(temp)
        {
            if ! [ $(t).action ]
            {
                leaves += $(t) ;
            }
        }
        return [ generator.generated-targets $(sources) $(leafs)
          : $(property-set) : $(project) $(name) ] ;
    }
}
generators.register [ new itrace-generator nm.itrace : EXE : ITRACE ] ;

将使用类型为 EXE 的单个源目标调用 generated-targets 方法。对 virtual-target.traverse 的调用将返回可执行文件依赖的所有目标,我们进一步查找不是从任何事物生成的文件。找到的目标将添加到源中。

可以覆盖 run 方法以完全自定义生成器的工作方式。特别是,可以将源转换为所需类型的方式完全自定义。这是另一个真实的示例。Boost Python 库的测试通常由两部分组成:Python 程序和 C++ 文件。C++ 文件被编译为 Python 扩展,Python 程序会加载该扩展。但是,在很可能两个文件具有相同名称的情况下,必须重命名创建的 Python 扩展。否则,Python 程序将导入自身,而不是扩展。以下是如何完成此操作

rule run ( project name ? : property-set : sources * )
{
    local python ;
    for local s in $(sources)
    {
        if [ $(s).type ] = PY
        {
            python = $(s) ;
        }
    }

    local libs ;
    for local s in $(sources)
    {
        if [ type.is-derived [ $(s).type ] LIB ]
        {
            libs += $(s) ;
        }
    }

    local new-sources ;
    for local s in $(sources)
    {
        if [ type.is-derived [ $(s).type ] CPP ]
        {
            local name = [ $(s).name ] ;    # get the target's basename
            if $(name) = [ $(python).name ]
            {
                name = $(name)_ext ;        # rename the target
            }
            new-sources += [ generators.construct $(project) $(name) :
              PYTHON_EXTENSION : $(property-set) : $(s) $(libs) ] ;
        }
    }

    result = [ construct-result $(python) $(new-sources) : $(project) $(name)
                 : $(property-set) ] ;
}

首先,我们将所有源分离为 Python 文件、库和 C++ 源。对于每个 C++ 源,我们通过调用 generators.construct 并传递 C++ 源和库来创建一个单独的 Python 扩展。此时,如有必要,我们还会更改扩展名的名称。

8.6. 功能特性

通常,我们需要控制传递给调用工具的选项。这是通过功能特性完成的。考虑一个例子

# Declare a new free feature
import feature : feature ;
feature verbatim-options : : free ;

# Cause the value of the 'verbatim-options' feature to be
# available as 'OPTIONS' variable inside verbatim.inline-file
import toolset : flags ;
flags verbatim.inline-file OPTIONS <verbatim-options> ;

# Use the "OPTIONS" variable
actions inline-file
{
    "./inline-file.py" $(OPTIONS) $(<) $(>)
}

我们首先定义一个新的功能特性。然后,flags 调用表示,每当运行 verbatim.inline-file 操作时,verbatim-options 功能特性的值将添加到 OPTIONS 变量中,并且可以在操作主体内部使用。您需要查阅在线帮助 (--help) 以查找 toolset.flags 规则的所有功能特性。

尽管您可以定义任何功能特性集并以任何方式解释其值,但 B2 建议以下编码标准用于设计功能特性。

大多数功能特性应具有固定的值集,该值集在它们旨在使用的工具类中是可移植的(工具中立的)。用户不必为精确的工具调整值。例如,<optimization>speed 对于所有 C++ 编译器都具有相同的含义,并且用户不必担心传递给编译器命令行的确切选项。

除了此类可移植功能特性外,还有特殊的“raw”功能特性,允许用户根据需要将任何值传递给特定工具的命令行参数。例如,<cxxflags> 功能特性允许您将任何命令行选项传递给 C++ 编译器。 <include> 功能特性允许您传递任何以 -I 开头的字符串,并且解释是特定于工具的。(有关非常智能地使用该功能特性的示例,请参阅我可以使用 Boost.Jam 变量捕获外部程序输出吗?)。当然,应该始终努力使用可移植功能特性,但这些功能特性仍然作为后门提供,只是为了确保 B2 不会剥夺用户的任何控制权。

使用可移植功能特性是一个好主意,因为

  • 当为可移植功能特性赋予固定的值集时,您可以使用该功能特性的两种不同设置构建项目,并且 B2 将自动为生成的文件使用两个不同的目录。 B2 不会尝试分离使用不同原始选项构建的目标。

  • 与“raw”功能特性不同,您无需在 Jamfile 中使用特定的命令行标志,并且它更有可能与其他工具一起使用。

添加功能特性的步骤

添加功能特性需要三个步骤

  1. 声明功能特性。为此,使用“feature.feature”规则。您必须确定一组功能特性属性

    • 如果您希望为一个目标设置的功能特性值自动传播到其依赖目标,则使其“propagated”。

    • 如果功能特性没有固定的值列表,则它必须是“free”。例如,include 功能特性是一个 free 功能特性。

    • 如果功能特性用于引用相对于 Jamfile 的路径,则它必须是“path”功能特性。此类功能特性也会将其值自动转换为 B2 的内部路径表示形式。例如,include 是一个 path 功能特性。

    • 如果功能特性用于引用某个目标,则它必须是“dependency”功能特性。

  2. 在特定于目标的变量中表示功能特性值。构建操作是由 Boost.Jam 变量扩展修改的命令模板。 toolset.flags 规则将特定于目标的变量设置为功能特性的值。

  3. 使用变量。步骤 2 中设置的变量可以在构建操作中用于形成命令参数或文件。

另一个例子

这是另一个例子。让我们看看如何创建一个引用目标的功能特性。例如,在 Windows 上链接动态库时,有时需要指定一个“DEF 文件”,告诉应该导出哪些函数。最好这样使用此文件

lib a : a.cpp : <def-file>a.def ;

实际上,此功能特性已受支持,但无论如何……

  1. 由于该功能特性引用目标,因此它必须是“dependency”。

    feature def-file : : free dependency ;
  2. 关心 DEF 文件的工具集之一是 msvc。应向其添加以下行。

    flags msvc.link DEF_FILE <def-file> ;
  3. 由于 msvc.link 操作未使用 DEF_FILE 变量,因此我们需要将其修改为

    actions link bind DEF_FILE
    {
        $(.LD) .... /DEF:$(DEF_FILE) ....
    }

    请注意 bind DEF_FILE 部分。它告诉 B2 将 DEF_FILE 中的内部目标名称转换为 link 操作中的相应文件名。如果没有它,$(DEF_FILE) 的扩展将是一个奇怪的符号,链接器不太可能理解。

    我们几乎完成了,除了将以下代码添加到 msvc.jam

    rule link
    {
        DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;
    }

    这是 B2 引擎中一个 bug 的解决方法,希望有一天能得到修复。

变体和复合功能特性。

有时您想为某些功能特性集创建快捷方式。例如,release<variant> 的一个值,是功能特性集的快捷方式。

可以定义您自己的构建变体。例如

variant crazy : <optimization>speed <inlining>off
                <debug-symbols>on <profiling>on ;

将定义具有指定属性集的新变体。您还可以扩展现有变体

variant super_release : release : <define>USE_ASM ;

在这种情况下,super_release 将扩展到 release 指定的所有属性,以及您指定的其他属性。

您不仅限于仅使用 variant 功能特性。以下示例定义了一个全新的功能特性

feature parallelism : mpi fake none : composite link-incompatible ;
feature.compose <parallelism>mpi : <library>/mpi//mpi/<parallelism>none ;
feature.compose <parallelism>fake : <library>/mpi//fake/<parallelism>none ;

这将允许您指定功能特性 parallelism 的值,该值将扩展为链接到必要的库。

8.7. 主要目标规则

主要目标规则(例如“exe”或“lib”)创建一个顶级目标。您很可能想要声明自己的规则,并且有两种方法可以做到这一点。

第一种方法适用于您的目标规则应仅生成特定类型的目标的情况。在这种情况下,已经为您定义了一个规则!当您定义新类型时,B2 会自动定义相应的规则。规则的名称从类型的名称中获得,方法是将所有字母转换为小写并将下划线替换为破折号。例如,如果您创建一个包含以下内容的模块 obfuscate.jam

import type ;
type.register OBFUSCATED_CPP  : ocpp ;

import generators ;
generators.register-standard obfuscate.file : CPP : OBFUSCATED_CPP ;

并导入该模块,您将能够在 Jamfile 中使用规则“obfuscated-cpp”,它会将源转换为 OBFUSCATED_CPP 类型。

第二种方法是编写一个包装器规则,该规则调用任何现有规则。例如,假设每个目录只有一个库,并且希望将目录中的所有 cpp 文件编译到该库中。您可以使用以下方法实现此效果

lib codegen : [ glob *.cpp ] ;

如果您想使其更简单,可以将以下定义添加到 Jamroot.jam 文件

rule glib ( name : extra-sources * : requirements * )
{
    lib $(name) : [ glob *.cpp ] $(extra-sources) : $(requirements) ;
}

允许您将 Jamfile 简化为

glib codegen ;

请注意,由于您可以将自定义生成器与目标类型关联,因此构建逻辑可能相当复杂。例如,boostbook 模块声明了目标类型 BOOSTBOOK_MAIN 和该类型的自定义生成器。如果您的主要目标规则是非平凡的,则可以使用它作为示例。

8.8. 工具集模块

如果您的扩展程序仅在一个项目中使用,则可以将它们放在单独的 .jam 文件中,并由您的 Jamroot.jam 导入。如果扩展程序将在多个项目中使用,用户会感谢您的画龙点睛之笔。

using 规则提供了一种加载和配置扩展程序的标准机制。为了使其工作,您的模块应提供 init 规则。将使用传递给 using 规则的相同参数调用该规则。允许的参数集由您确定。例如,您可以允许用户指定路径、工具版本和其他选项。

以下是一些有助于使 B2 更一致的指南

  • init 规则永远不应失败。即使用户提供了不正确的路径,您也应发出警告并继续。配置可能在不同的机器之间共享,并且一台机器上的错误值在另一台机器上可能是可以的。

  • 最好指定要执行的命令,而不是指定工具的安装路径。首先,这提供了更多控制权:可以指定

    /usr/bin/g++-snapshot
    time g++

    作为命令。其次,虽然某些工具具有逻辑“安装根目录”,但如果用户不必记住特定工具是需要完整命令还是路径,那会更好。

  • 检查多次初始化。用户可以尝试多次初始化模块。您需要检查这一点并决定该怎么做。通常,除非您支持工具的多个版本,否则重复初始化是用户错误。如果在初始化期间可以指定工具的版本,请确保始终指定版本,或者从不指定版本(在这种情况下,工具仅初始化一次)。例如,如果您允许

    using yfc ;
    using yfc : 3.3 ;
    using yfc : 3.4 ;

    那么不清楚第一次初始化对应于工具的 3.3 版、工具的 3.4 版还是其他版本。这可能导致使用相同版本构建两次。

  • 如果可能,init 必须可以无参数调用。在这种情况下,它应该尝试自动检测所有必要的信息,例如,通过在 PATH 或常见安装位置中查找工具。通常,这是可能的,并允许用户简单地编写

    using yfc ;
  • 考虑使用 tools/common 模块中的工具。您可以查看 tools/gcc.jam 如何在 init 规则中使用该模块。

9. 常见问题解答

9.1. 如何在 Jamfile 中获取功能特性的当前值?

由于 Jamfile 不具备任何特性的“当前”值(无论是工具集、构建变体还是其他任何特性),因此这是不可能的。对于 B2 的单次运行,任何给定的主目标都可以使用多个属性集进行构建。例如,用户可以在命令行中请求两个构建变体。或者,一个库在从一个应用程序中使用时构建为共享库,而在从另一个应用程序中使用时构建为静态库。每个 Jamfile 只读取一次,因此通常在 Jamfile 中无法访问特性的单个值。

特性仅在构建目标时才具有特定值,并且有两种方法可以使用该值

  • 使用条件需求或间接条件需求。请参阅名为“需求”的章节

  • 定义自定义生成器和自定义主目标类型。自定义生成器可以执行任意处理或属性操作。请参阅扩展器手册

9.2. 我收到“实际目标的名称重复”错误。这是什么意思?

最可能的情况是您尝试使用几乎相同但属性不同的属性编译同一个文件两次。例如

exe a : a.cpp : <include>/usr/local/include ;
exe b : a.cpp ;

上面的代码片段需要对 a.cpp 进行两次不同的编译,它们仅在其 include 属性上有所不同。由于 include 特性被声明为 free,B2 不会为其每个值创建单独的构建目录,并且这两个构建都将在同一个构建目录中生成目标文件。忽略这一点并仅编译文件一次将是危险的,因为不同的 include 可能会导致编译完全不同的代码。

要解决此问题,您需要决定文件应编译一次还是两次。

  1. 要仅编译文件一次,请确保两个目标请求的属性相同

    exe a : a.cpp : <include>/usr/local/include ;
    exe b : a.cpp : <include>/usr/local/include ;

    或者

    alias a-with-include : a.cpp : <include>/usr/local/include ;
    exe a : a-with-include ;
    exe b : a-with-include ;

    或者,如果您希望 includes 属性不影响为构建 ab 可执行文件添加的任何其他源文件的编译方式

    obj a-obj : a.cpp : <include>/usr/local/include ;
    exe a : a-obj ;
    exe b : a-obj ;

    请注意,在上述两种情况下,include 属性都将仅应用于构建这些目标文件,而不应用于可能为目标 ab 添加的任何其他源文件。

  2. 要将文件编译两次,您可以告诉 B2 将其编译为两个单独的目标文件,如下所示

    obj a_obj : a.cpp : <include>/usr/local/include ;
    obj b_obj : a.cpp ;
    exe a : a_obj ;
    exe b : b_obj ;

    或者,您可以使目标文件目标成为主目标的本地目标

    exe a : [ obj a_obj : a.cpp : <include>/usr/local/include ] ;
    exe b : [ obj a_obj : a.cpp ] ;

    这将导致 B2 实际为您稍微更改生成的目标文件名,从而避免任何冲突。

    请注意,在上述两种情况下,include 属性都将仅应用于构建这些目标文件,而不应用于可能为目标 ab 添加的任何其他源文件。

一个很好的问题是为什么 B2 不能自动使用上述某些方法。问题在于,这种魔术仅在一半情况下有效,而在另一半情况下,它会默默地做错事。在这些情况下,要求用户澄清其意图更简单也更安全。

9.3. 访问环境变量

许多用户希望在 Jamfile 中使用环境变量,例如,控制外部库的位置。在许多情况下,最好在 site-config.jam 文件中声明这些外部库,如配方部分中所述。但是,如果用户已经设置了环境变量,那么他们设置 site-config.jam 文件可能不方便,并且使用环境变量可能是合理的。

Boost.Jam 自动将所有环境变量导入其内置的 .ENVIRON 模块,因此用户可以直接从那里读取它们,或者通过使用辅助规则 os.environ 来读取。例如

import os ;
local unga-unga = [ os.environ UNGA_UNGA ] ;
ECHO $(unga-unga) ;

或者更实际一点

import os ;
local SOME_LIBRARY_PATH = [ os.environ SOME_LIBRARY_PATH ] ;
exe a : a.cpp : <include>$(SOME_LIBRARY_PATH) ;

9.4. 如何控制属性顺序?

由于内部原因,B2 按字母顺序对所有属性进行排序。这意味着如果您编写

exe a : a.cpp : <include>b <include>a ;

那么命令行将首先提及 a include 目录,然后是 b,即使它们是以相反的顺序指定的。在大多数情况下,用户并不在意。但有时 include 或其他属性的顺序很重要。对于这种情况,提供了特殊的语法

exe a : a.cpp : <include>a&&b ;

&& 符号分隔属性值,并指定应保留其顺序。建议您仅在属性顺序确实重要时才使用此功能,而不是作为方便的快捷方式。在任何地方都使用它可能会对性能产生负面影响。

9.5. 如何控制 Unix 上的库链接顺序?

在类 Unix 操作系统上,调用链接器时指定静态库的顺序非常重要,因为默认情况下,链接器会单次遍历库列表。以不正确的顺序传递库将导致链接错误。此外,此行为通常用于使一个库覆盖另一个库中的符号。因此,有时需要强制特定的库链接顺序。

B2 尝试自动计算正确的顺序。主要规则是,如果库 a “使用”库 b,则库 a 将在命令行中出现在库 b 之前。如果 b 出现在 a 库的源文件中,或者其用法列在其需求中,则认为库 a 使用 b。要显式指定 use 关系,可以使用 <use> 特性。例如,以下两行都将导致 a 在命令行中出现在 b 之前

lib a : a.cpp b ;
lib a : a.cpp : <use>b ;

相同的方法也适用于搜索到的库

lib z ;
lib png : : <use>z ;
exe viewer : viewer png z ;

9.6. 我可以使用 Boost.Jam 变量捕获外部程序输出吗?

SHELL 内置规则可以用于此目的

local gtk_includes = [ SHELL "gtk-config --cflags" ] ;

9.7. 如何获取项目根目录(又名 Jamroot)位置?

您可能希望在 Jamfile 中使用项目的根目录位置。要访问它,只需在 Jamroot.jam 文件中使用以下代码声明路径常量

path-constant TOP : . ;

之后,TOP 变量可以在每个 Jamfile 中使用。

9.8. 如何更改单个文件的编译标志?

如果一个文件必须使用特殊选项进行编译,则需要为该文件显式声明一个 obj 目标,然后在您的 exelib 目标中使用该目标

exe a : a.cpp b ;
obj b : b.cpp : <optimization>off ;

当然,您可以使用其他属性,例如指定特定的 C/C++ 编译器选项

exe a : a.cpp b ;
obj b : b.cpp : <cflags>-g ;

您还可以使用条件属性进行更精细的控制

exe a : a.cpp b ;
obj b : b.cpp : <variant>release:<optimization>off ;

9.9. 为什么 dll-pathhardcode-dll-paths 属性有用?

此条目特定于 Unix 系统。

在回答问题之前,让我们回顾一下关于共享库的几个要点。共享库可以被多个应用程序(或其他库)使用,而无需将库物理包含在链接的二进制文件中。这可以大大减小应用程序的总大小。在应用程序安装后升级共享库也是可能的。

但是,为了启动依赖于共享库的应用程序,操作系统需要找到共享库。动态链接器将在系统定义的路径列表中搜索,加载库并解析符号。这意味着您应该更改系统定义的列表(由 LD_LIBRARY_PATH 环境变量给出),或将库安装到系统位置。这在开发时可能不方便,因为库尚未准备好安装,并且弄乱系统路径可能是不希望的。幸运的是,在 Unix 上还有另一种方法。

使用 hardcode-dll-pathsdll-path 特性,可以将目标与附加的库目录路径列表链接,这些路径将在系统路径之前搜索——这些路径称为“运行时库搜索路径”或“运行路径”,或“运行路径列表”,具体取决于您正在阅读哪个平台的文档——请参阅您平台的动态链接器 man pageWikipedia 了解更多信息。

为了简洁起见,我们将在下面仅使用 rpath list

9.9.1. hardcode-dll-paths

对于 exe 目标,hardcode-dll-paths 特性尤其有助于开发;由于构建系统已经知道所有使用的共享库的路径,因此默认情况下,它会自动将它们添加到可执行文件的 rpath list 中。

但是,当可执行文件安装后,情况就不同了;显然,已安装的可执行文件不应包含指向您的开发树的硬编码路径。install 规则因此隐式地(即默认情况下)否定 hardcode-dll-paths 特性,如果必要,通过重新链接不带自动路径的可执行文件。

  • 对于 exe 规则

    • 使用 <hardcode-dll-paths>true(默认),所有包含已使用共享库的目录的路径都将自动添加到目标的 rpath list 中。

    • 需要显式的 <hardcode-dll-paths>false 属性来禁用自动将目录路径添加到共享库。

  • 对于 install 规则

    • 如果需要,需要显式的 <hardcode-dll-paths>true 来将 rpath list(添加到源目标)传播到 install 目标。(这包括添加到源目标的显式 dll-path 条目。)

    • 默认情况下,隐式的 <hardcode-dll-paths>false 属性将确保源目标的 rpath list 不会 传播到 install 目标。

  • 对于 lib 规则,<hardcode-dll-paths> 特性被忽略。

9.9.2. dll-path

作为替代方案(或补充),您可以使用 dll-path 特性手动将显式目录路径添加到 rpath list 中。
例如

install installed : application : <dll-path>/usr/lib/snake
                                  <location>/usr/bin ;

将允许应用程序找到放置在 /usr/lib/snake 目录中的库。

9.9.3. 结论

如果您将库安装到非标准位置并添加显式路径,您将可以更好地控制将要使用的库。系统位置中同名的库将不会被意外使用。如果您将库安装到系统位置并且不添加任何路径,则系统管理员将拥有更多控制权。每个库都可以单独升级,并且所有应用程序都将使用新库。

哪种方法最好取决于您的情况。如果库相对独立并且可以被第三方应用程序使用,则应将其安装在系统位置。如果您有很多库只能供您的应用程序使用,则将它们安装到非标准目录并添加显式路径(如上面的示例所示)是有意义的。另请注意,不同系统的指南在这方面有所不同。例如,Debian GNU 指南禁止任何额外的搜索路径,而 Solaris 指南建议应始终使用它们。

共享库搜索路径摘要

(适用于客户端目标——即使用共享库的目标。)

规则 功能 Rpath List 添加

exe

hardcode-dll-paths

true(默认)

所有共享库目录的绝对路径
如果这些是目标本身,则会添加其构建目录路径。

exe

hardcode-dll-paths

false

(无)

install

hardcode-dll-paths

true

将来自源(exelib 目标)的 rpath list 传播到已安装的二进制文件。(这包括添加到源目标的显式 dll-path 条目。)

install

hardcode-dll-paths

false(默认)

(无)

lib

hardcode-dll-paths

已禁用
(无效)

(无)

exe, lib, install

dll-path

(绝对路径)

指定的绝对路径

exe, lib, install

dll-path

(相对路径)
⚠️

由指定的相对路径组成的路径,前缀为命令行中指定的 jam 目录的路径。

⚠️ 警告:生成的路径将取决于特定的命令行调用,因此在实际使用中受到严重限制。

9.10. site-config.jam 中的目标

最好声明给定系统上可用的标准库。将目标声明放在特定项目的 Jamfile 中不是很好,因为库的位置可能因不同的开发机器而异,因此需要在不同的项目中复制此类声明。解决方案是在 B2 的 site-config.jam 配置文件中声明目标

project site-config ;
lib zlib : : <name>z ;

回想一下,site-config.jamuser-config.jam 都是项目,您可以在 Jamfile 中执行的所有操作也可以在这些文件中执行。因此,您声明一个项目 ID 和一个目标。现在,可以编写

exe hello : hello.cpp /site-config//zlib ;

在任何 Jamfile 中。

9.11. 仅标头库

在现代 C++ 中,库通常仅由头文件组成,没有任何源文件需要编译。要使用此类库,您需要添加正确的 include 并可能为您的项目定义。但是,对于大量的外部库,记住哪些库是仅标头库,哪些库必须链接到就变得有问题。但是,使用 B2,可以将仅标头库声明为 B2 目标,所有依赖项都可以使用此类库,而无需记住它是仅标头库还是非仅标头库。

可以使用 alias 规则声明仅标头库,将其 include 路径指定为其用法需求的一部分,例如

alias my-lib
    : # no sources
    : # no build requirements
    : # no default build
    : <include>whatever ;

my-lib 的用法需求中指定的 include 会自动添加到其所有依赖项的构建属性中。依赖项无需关心 my-lib 是否为仅标头库,并且以后可以将 my-lib 变成常规编译库,而无需将 include 添加到其依赖项声明中。

如果您已经为定义仅标头库的项目声明了正确的用法需求,则无需为 alias 目标复制它们

project my : usage-requirements <include>whatever ;
alias mylib ;

9.12. B2、b2bjam 和 Perforce Jam 之间有什么区别?

B2 是完整构建系统的名称。运行它的可执行文件是 b2。该可执行文件是用 C 编写的,实现了性能关键型算法,例如依赖关系图的遍历和执行命令。它还实现了一种解释型语言,用于实现 B2 的其余部分。此可执行文件正式称为“B2 引擎”。

B2 引擎源自早期名为 Perforce Jam 的构建工具。最初,只有少量更改,文件名是 bjam。后来,随着越来越多的更改,名称的相似性对用户造成了困扰,并且从 Boost 1.47.0 开始,可执行文件的正式名称更改为 b2。为了兼容性,仍然创建了一个名为 bjam 的副本,但我们鼓励您在所有情况下都使用新名称。

Perforce Jam 是一个重要的基础,我们衷心感谢其影响,但对于今天的用户而言,这些工具仅共享解释型语言的一些基本知识。

10. 额外工具

10.1. 文档工具

10.1.1. Asciidoctor

asciidoctor 工具将 ascidoc 文档格式转换为各种后端格式,以便查看或供文档工具进一步处理。此工具支持基线 asciidoctor 发行版(即基于 Ruby 的工具)。

特性:asciidoctor-attribute

定义任意 asciidoctor 属性。特性的值应使用属性的 CLI 语法指定。例如,用作目标需求

html example : example.adoc :
    <asciidoctor-attribute>idprefix=ex ;

这是一个 free 特性,并且不 propagated。即,它仅适用于指定它的目标。

特性:asciidoctor-doctype

指定用于生成输出格式的 doctype。允许的 doctype 值包括:articlebookmanpageinline

特性:asciidoctor-backend

指定用于从源 asciidoc 生成输出的 backend。此特性会自动应用以适应构建目标类型。例如,当为 asciidoc 源指定 html 目标时

html example : example.adoc ;

目标默认将获取 <asciidoctor-backend>html5 需求。每个目标类型的默认值是

  • html: <asciidoctor-backend>html5

  • docbook: <asciidoctor-backend>docbook45

  • man: <asciidoctor-backend>manpage

  • pdf: <asciidoctor-backend>pdf

要覆盖默认值,您可以将其指定为目标上的需求

docbook example : example.adoc :
    <asciidoctor-backend>docbook5 ;

允许的 backend 值包括:html5docbook45docbook5pdf

初始化

要使用 asciidoctor 工具,您需要在配置文件中使用 using 规则声明它。初始化采用以下参数

  • command:要执行的命令,以及任何额外的参数。

例如,您可以将以下内容插入到您的 user-config.jam

using asciidoctor : "/usr/local/bin/asciidoctor" ;

如果未给出 command,则默认仅为 asciidoctor,并假设 asciidoctor 在搜索 PATH 中可用。

10.2. 杂项工具

10.2.1. pkg-config

pkg-config 程序用于检索系统中已安装库的信息。它从特殊的元数据文件中检索有关包的信息。这些文件以包命名,并具有 .pc 扩展名。指定给 pkg-config 的包名称定义为元数据文件的名称,减去 .pc 扩展名。

特性:pkg-config

选择已初始化的 pkg-config 配置之一。此特性 propagated 到依赖项。其用法在初始化部分中讨论。

特性:pkg-config-define

free 特性向 pkg-config 调用添加变量赋值。例如,

pkg-config.import mypackage : requirements <pkg-config-define>key=value ;

等效于在命令行上调用

pkg-config --define-variable=key=value mypackage ;
规则:import

导入 pkg-config 包的主目标规则。构建其使用者目标时,将使用依赖于当前属性集的参数调用 pkg-config 命令。具有影响的特性是

  • <pkg-config-define>:添加 --define-variable 参数;

  • <link>:当 <link>static 时添加 --static 参数;

  • <link>:当 <link>static 时添加 --static 参数;

  • <name>:指定包名称(如果属性不存在,则使用目标名称代替);

  • <version>:指定包版本范围,可以多次使用,并且应该是由点分隔的数字序列,可以选择以 =<>>= 为前缀。

示例

pkg-config.import my-package
    : requirements <name>my_package <version><4 <version>>=3.1 ;
初始化

要使用 pkg-config 工具,您需要在配置文件中使用 using 规则声明它

using pkg-config : [config] : [command] ... : [ options ] ... ;
  • config:已初始化配置的名称。可以省略名称,在这种情况下,配置将成为默认配置。

  • command:要执行的命令,以及任何额外的参数。如果未给出命令,则首先检查 PKG_CONFIG 环境变量,如果为空,则使用字符串 pkg-config

  • options:修改 pkg-config 行为的选项。允许的选项包括

  • <path>:设置 PKG_CONFIG_PATH 环境变量;允许多次出现。

  • <libdir>:设置 PKG_CONFIG_LIBDIR 环境变量;允许多次出现。

  • <allow-system-cflags>:设置 PKG_CONFIG_ALLOW_SYSTEM_CFLAGS 环境变量;允许多次出现。

  • <allow-system-libs>:设置 PKG_CONFIG_ALLOW_SYSTEM_LIBS 环境变量;允许多次出现。

  • <sysroot>:设置 PKG_CONFIG_SYSROOT_DIR 环境变量;允许多次出现。

  • <variable>:向命令调用添加变量定义参数;允许多次出现。

pkg-config-target
class pkg-config-target : alias-target-class {
    rule construct ( name : sources * : property-set )
    rule version ( property-set )
    rule variable ( name : property-set )
}

import 规则返回的对象类。对象本身在需要更复杂逻辑来使用包的情况下可能很有用。有关示例,请参阅提示

  1. rule construct ( name : sources * : property-set ) 覆盖 alias-target.construct

  2. rule version ( property-set )property-set 的上下文中返回包的版本。

  3. rule variable ( name : property-set )property-set 的上下文中返回包中变量 name 的值。

提示
使用多个配置

假设您有 2 个 .pc 文件集合:一个用于平台 A,另一个用于平台 B。您可以初始化 2 个 pkg-config 工具配置,每个配置对应于特定的集合

using pkg-config : A : : <libdir>path/to/collection/A ;
using pkg-config : B : : <libdir>path/to/collection/B ;

然后,您可以指定平台 A 的构建应使用配置 A,而平台 B 的构建应使用配置 B

project
    : requirements
      <target-os>A-os,<architecture>A-arch:<pkg-config>A
      <target-os>B-os,<architecture>B-arch:<pkg-config>B
    ;

由于 project-configuser-configsite-config 模块是 jamroot 模块的父模块,因此您可以将其放在这些文件中的任何一个中。

根据属性集选择包名称

由于包的文件应以包命名,并附加 .pc 后缀,因此一些项目提出了命名方案,以便允许同时安装多个主要版本或构建变体。为了选择与构建请求对应的特定名称,您可以在需求中使用 <conditional> 属性

pkg-config.import mypackage : requirements <conditional>@infer-name ;

rule infer-name ( properties * )
{
    local name = mypackage ;
    local variant = [ property.select <variant> : $(properties) ] ;
    if $(variant) = debug
    {
      name += -d ;
    }
    return <name>$(name) ;
}

common.format-name 规则在这种情况下非常有用。

根据包版本或变量修改用法需求

有时,您需要根据包的版本或其定义的变量应用一些逻辑。为此,您可以在用法需求中使用 <conditional> 属性

mypackage =
  [ pkg-config.import mypackage : usage-requirements <conditional>@define_ns
  ] ;

rule extra-props ( properties * )
{
    local ps = [ property-set.create $(properties) ] ;
    local prefix = [ $(mypackage).variable name_prefix : $(ps) ] ;
    prefix += [ $(mypackage).version $(ps) ] ;
    return <define>$(prefix:J=_) ;
}

10.2.2. Sass

此工具将 SASS 和 SCSS 文件转换为 CSS。此工具显式支持用 C (sassc) 编写的版本和原始 Ruby 实现 (scss),但其他变体也可能有效。除了本节中描述的工具特定特性外,该工具还识别特性 <flags><include>

特性:sass-style

设置输出样式。可用值包括

  • nested:每个属性都放在自己的行上,规则根据它们的嵌套深度进行缩进;

  • expanded:每个属性都放在自己的行上,规则不缩进;

  • compact:每个规则都放在一行上,嵌套规则占用相邻行,而不相关的规则组之间用换行符分隔;

  • compressed:占用最小的空间量:所有不必要的空格都被删除,属性值被压缩以具有最小的表示形式。

该特性是 optional 的,并且不 propagated 到依赖目标。如果未指定样式,则如果属性集包含属性 <optimization>on,则选择 compressed 样式。否则,选择 nested 样式。

特性:sass-line-numbers

启用发出注释,显示规则的原始行号。这对于调试样式表很有用。可用值包括 onoff。该特性是 optional 的,并且不 propagated 到依赖目标。如果未为此特性指定值,则从特性 debug-symbols 复制一个值。

初始化

要使用 sass 工具,您需要在配置文件中使用 using 规则声明它。初始化采用以下参数

  • command:要执行的命令,以及任何额外的参数。

例如,您可以将以下内容插入到您的 user-config.jam

using sass : /usr/local/bin/psass -p2 ; # Perl libsass-based version

如果未给出 command,则尝试 sassc,然后再尝试 scss

11. 示例

11.1. 简介

在这里,我们收录了一系列从简单到复杂的、完全可用的示例,说明如何将 Boost Build v2 用于各种任务。它们展示了从简单到高级功能的整个过程。如果您发现自己在查看示例,但没有找到您想要看到的工作方式,请发布到我们的支持列表,我们将尝试提出解决方案并在此处添加它,供其他人学习。

11.2. Hello

此示例展示了一个非常基本的 Boost Build 项目设置,因此它可以从单个源文件编译单个可执行文件

hello.cpp
#include <iostream>

int main()
{
    std::cout << "Hello!\n";
}

我们的 jamroot.jam 非常简单,仅为程序指定了一个 exe 目标

jamroot.jam
exe hello : hello.cpp ;

构建示例会产生

> cd /example/hello
> b2
...found 8 targets...
...updating 4 targets...
common.mkdir bin/clang-darwin-4.2.1
common.mkdir bin/clang-darwin-4.2.1/debug
clang-darwin.compile.c++ bin/clang-darwin-4.2.1/debug/hello.o
clang-darwin.link bin/clang-darwin-4.2.1/debug/hello
...updated 4 targets...
> bin/clang-darwin-4.2.1/debug/hello
Hello!
bin 子目录中的实际路径将取决于您的工具集。

11.3. Sanitizers

此示例展示了在使用 clang 或 gcc 工具集时如何启用 sanitizers

main.cpp
int main()
{
    char* c = nullptr;
    std::cout << "Hello sanitizers\n " << *c;
}

我们的 jamroot.jam 非常简单,仅为程序指定了一个 exe 目标

jamroot.jam
exe main : main.cpp ;

可以通过将 onnorecover 传递给适当的 sanitizer 特性(例如,thread-sanitizer=on)来启用 Sanitizers。norecover 选项会导致程序在检测到第一个 sanitizer 问题后终止。以下示例展示了如何在简单程序中启用 addressundefined sanitizers

> cd /example/sanitizers
> b2 toolset=gcc address-sanitizer=norecover undefined-sanitizer=on
...found 10 targets...
...updating 7 targets...
gcc.compile.c++ bin/gcc-7.3.0/debug/address-sanitizer-norecover/undefined-sanitizer-on/main.o
gcc.link bin/gcc-7.3.0/debug/address-sanitizer-norecover/undefined-sanitizer-on/main
...updated 7 targets...

运行生成的程序可能会产生类似于以下内容的输出

> ./bin/gcc-7.3.0/debug/address-sanitizer-norecover/undefined-sanitizer-on/main
Hello sanitizers
main.cpp:6:43: runtime error: load of null pointer of type 'char'
ASAN:DEADLYSIGNAL
=================================================================
==29767==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55ba7988af1b bp 0x7ffdf3d76560 sp 0x7ffdf3d76530 T0)
==29767==The signal is caused by a READ memory access.
==29767==Hint: address points to the zero page.
    #0 0x55ba7988af1a in main /home/damian/projects/boost/tools/build/example/sanitizers/main.cpp:6
    #1 0x7f42f2ba1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #2 0x55ba7988adb9 in _start (/home/damian/projects/boost/tools/build/example/sanitizers/bin/gcc-7.3.0/debug/address-sanitizer-norecover/undefined-sanitizer-on/main+0xdb9)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/damian/projects/boost/tools/build/example/sanitizers/main.cpp:6 in main
==29767==ABORTING
bin 子目录中的实际路径将取决于您的工具集和配置。呈现的输出可能会因您的编译器版本而异。

12. Boost.Jam 文档

Jam 是 make(1) 的替代品,它使构建简单的事情变得简单,并使构建复杂的事情变得可管理。

12.1. 构建 B2

在构建 B2 之后安装它只是将生成的可执行文件复制到 PATH 中的某个位置。为了构建可执行文件,有一组 build 引导脚本来适应特定的环境。这些脚本接受一个可选参数,即要构建的工具集的名称。当未给出工具集时,会尝试检测可用的工具集并使用它。构建脚本接受以下参数

build [toolset]

不带参数运行脚本将为您提供最大的成功机会。在 Windows 平台上,从命令控制台执行

cd jam source location
.\build.bat

在 Unix 类型平台上执行

cd jam source location
sh ./build.sh

对于 Boost 发行版中包含的 Boost.Jam 源代码,jam 源代码位置BOOST_ROOT/tools/build/src/engine

如果脚本未能检测到合适的工具集,则您的特定工具集可能无法自动检测。 在这种情况下,您可以将工具集指定为第一个参数,前提是该工具集在 PATH 中随时可用。

用于构建 Boost.Jam 的工具集与用于 B2 的工具集无关。 只需要一个版本的 Boost.Jam 即可使用 B2。

支持的工具集以及它们是否自动检测到,如下所示:

表 2. 支持的工具集
脚本 平台 工具集 检测和说明

build.bat

Windows

vc142

Microsoft Visual Studio C++ 2019

  • 使用 vswhere 实用程序。

vc141

Microsoft Visual Studio C++ 2017

  • 使用 vswhere 实用程序。

  • 常用安装位置:%ProgramFiles%\Microsoft Visual Studio\2017\Enterprise\VC\

  • 常用安装位置:%ProgramFiles%\Microsoft Visual Studio\2017\Professional\VC\

  • 常用安装位置:%ProgramFiles%\Microsoft Visual Studio\2017\Community\VC\

vc14

Microsoft Visual Studio C++ 2015

  • 环境变量 %VS140COMNTOOLS%

  • 常用安装位置:%ProgramFiles%\Microsoft Visual Studio 14.0\VC\

vc12

Microsoft Visual Studio C++ 2013

  • 环境变量 %VS120COMNTOOLS%

  • 常用安装位置:%ProgramFiles%\Microsoft Visual Studio 12.0\VC\

borland

Embarcadero C++Builder

  • PATH 中的 bcc32c.exe

intel-win32

Intel C++ Compiler for Windows

  • PATH 中的 icl.exe

mingw

作为 MinGW 配置的 GNU GCC

  • 常用安装位置:C:\MinGW

como

Comeau Computing C/C++

gcc,

GNU GCC

clang

Clang LLVM

gcc-nocygwin

GNU GCC

build.sh

Unix、Linux、Cygwin、Windows Bash 等。

gcc

GNU GCC

  • PATH 中的 g++

clang

Clang LLVM

  • PATH 中的 clang++

intel-linux

Intel C++ (oneAPI) for Linux

  • PATH 中的 icpx

  • PATH 中的 icc

  • PATH 中的 icpc

  • 常用安装位置中的 setvars.sh$HOME/intel/oneapi/opt/intel/oneapi/opt/intel/inteloneapi

  • 常用安装位置中的 iccvars.sh/opt/intel/cc/9.0/bin/opt/intel_cc_80/bin

mipspro

SGI MIPSpro C++

  • uname 为 “IRIX” 或 “IRIX64”,且 PATH 中有 CC

true64cxx

Compaq C++ Compiler for True64 UNIX

  • uname 为 “OSF1”,且 PATH 中有 cc

qcc

QNX Neutrino

  • uname 为 “QNX”,且 PATH 中有 QCC

xlcppvacpp

IBM VisualAge C++

  • uname 为 “Linux”,且 PATH 中有 xlC_r (xlcppvacpp 取决于机器字节序)

  • uname 为 “AIX”,且 PATH 中有 xlC_r (vacpp)

pgi

PGI Compilers

  • PATH 中的 pgc++

pathscale

Pathscale C++

  • PATH 中的 pathCC

como

Comeau Computing C/C++

  • PATH 中的 como

kylix

Borland C++

  • PATH 中的 bc++ (kylix)

acc

HP-UX aCC

  • PATH 中的 aCC

sunpro

Sun Workshop 6 C++

  • 标准安装位置:/opt/SUNWspro/bin/CC

构建的可执行文件放置在 src/engine 目录中。

build.sh 脚本支持用于控制构建和自定义编译器的其他调用选项

build.sh [--option|--option=x] [toolset]
--help

显示一些帮助信息,包括这些选项。

--verbose

显示有关此脚本正在执行的操作的消息。

--debug

构建可执行文件的调试版本。 默认设置为构建优化的可执行文件。

--guess-toolset

打印我们可以检测到的用于构建的工具集。 这供外部脚本使用,例如 Boost Libraries 主引导脚本。

--cxx=CXX

要使用的编译器可执行文件,而不是检测到的编译器可执行文件。

--cxxflags=CXXFLAGS

除了检测到的编译器的标志外,还要使用的编译器标志。

12.2. 语言

B2 具有解释型的过程式语言。 b2 中的语句是规则(过程)定义、规则调用、流程控制结构、变量赋值和各种语言支持。

12.2.1. 词法特征

B2 将其输入文件视为以空格分隔的标记,但有两个例外:双引号 (") 可以包含空格以将其嵌入到标记中,并且规则动作定义中匹配的花括号 (\{}) 之间的所有内容都视为单个字符串。 反斜杠 (\) 可以转义双引号或任何单个空格字符。

B2 要求空格(空格、制表符或换行符)包围所有标记,包括冒号 (:) 和分号 (;) 标记。

B2 关键字(如本文档中所述)是保留的,通常必须用双引号 (") 引起来才能用作任意标记,例如变量名或目标名。

注释以 # 字符开头,并延伸到行尾。 块注释以 #| 开头,并延伸到下一个 |#

12.2.2. 目标

基本的 b2 数据实体是目标。 构建目标是要更新的文件。 源目标是用于更新构建文件的文件。 构建目标和源目标统称为文件目标,并且经常构建的目标是其他构建目标的源目标。 伪目标是表示对其他目标的依赖关系的符号,但它们本身不与任何真实文件关联。

文件目标的标识符通常是文件名,它可以是绝对根路径,相对于 b2 调用的目录,或只是本地路径(无目录)。 最常见的情况是最后一种情况,实际文件路径使用 $(SEARCH)$(LOCATE) 特殊变量绑定。 请参阅下文的 SEARCH 和 LOCATE 变量。 本地文件名可以选择使用 grist 限定,grist 是用于确保唯一性的字符串值。 标识符形式为 file(member) 的文件目标是库成员(通常是 Unix 上的 ar(1) 存档)。

绑定检测

每当目标绑定到文件系统中的位置时,Boost Jam 将查找名为 BINDRULE 的变量(首先在被绑定的目标上,然后在全局模块中)。 如果非空,$(BINDRULE[1]) 命名一个规则,该规则使用目标的名称和它正在绑定的路径调用。 由 $(BINDRULE[1]) 命名的规则的签名应与以下内容匹配

rule bind-rule ( target : path )

此功能对于正确的头文件扫描非常有用,因为许多编译器将首先在包含执行 #include 指令的文件的目录中搜索 #include 文件。 $(BINDRULE) 可用于记录该目录。

12.2.3. 规则

基本的 b2 语言实体称为规则。 规则分为两个部分定义:过程和动作。 过程是在调用规则时要运行的 jam 语句体; 动作是在更新规则的构建目标时要执行的 OS shell 命令。

规则可以返回值,这些值可以使用 "[ rule args …​ ]" 扩展为列表。 规则的值是其最后一个语句的值,但只有以下语句具有值:“if”(所选分支的值)、“switch”(所选 case 的值)、set(结果变量的值)和“return”(其参数的值)。

用于定义和调用规则的 b2 语句如下:

定义规则的过程,替换任何先前的定义。

rule rulename { statements }

定义规则的更新动作,替换任何先前的定义。

actions [ modifiers ] rulename { commands }

调用规则。

rulename field1 : field2 : ... : fieldN ;

在目标特定变量的影响下调用规则。

on target rulename field1 : field2 : ... : fieldN ;

用作参数,扩展为调用规则的返回值。

[ rulename field1 : field2 : ... : fieldN ]
[ on target rulename field1 : field2 : ... : fieldN ]

使用 field1fieldN 中的值调用规则。 它们可以在过程的语句中引用为 $(1)$(N)(最多 9 个),并且只有前两个可以在动作的 commands 中引用为 $(1)$(2)$(<)$(>)$(1)$(2) 同义。

规则分为两类:更新规则(带有动作)和纯过程规则(没有动作)。 更新规则将参数 $(1)$(2) 分别视为构建目标和源,而纯过程规则可以接受任意参数。

当调用更新规则时,其更新动作会在规则的过程运行之前添加到与其构建目标 ($(1)) 关联的动作中。 稍后,为了在更新阶段构建目标,commands 将传递给 OS 命令 shell,其中 $(1)$(2) 被目标名称的绑定版本替换。 请参阅上面的绑定。

规则调用可以通过变量间接完成

$(var) field1 : field2 : ... : fieldN ;

on target $(var) field1 : field2 : ... : fieldN ;

[ $(var) field1 : field2 : ... : fieldN ]
[ on target $(var) field1 : field2 : ... : fieldN ]

变量的值命名要调用的规则(或规则)。 对于 $(var) 值的列表中的每个元素,都会调用一个规则。 字段 field1 : field2 : …​ 作为每个调用的参数传递。 对于 [ …​ ] 形式,返回值是所有调用的返回值的串联。

动作修饰符

理解以下动作修饰符

actions bind vars

$(vars) 将被替换为绑定值。

actions existing

$(>) 仅包含当前存在的源目标。

actions ignore

命令的返回状态将被忽略。

actions piecemeal

命令会重复调用,$(>) 的子集足够小,可以容纳在此 OS 上的命令缓冲区中。

actions quietly

该动作不会回显到标准输出。

actions together

来自同一构建目标上同一动作的多次调用的 $(>) 被合并在一起。

actions updated

$(>) 仅包含标记为更新的源目标本身。

参数列表

您可以描述规则接受的参数,并在规则内按名称引用它们。 例如,以下内容将“I’m sorry, Dave”打印到控制台

rule report ( pronoun index ? : state : names + )
{
    local he.suffix she.suffix it.suffix = s ;
    local I.suffix = m ;
    local they.suffix you.suffix = re ;
    ECHO $(pronoun)'$($(pronoun).suffix) $(state), $(names[$(index)]) ;
}
report I 2 : sorry : Joe Dave Pete ;

形式参数列表中的每个名称(在规则声明中用 : 分隔)都绑定到相应实际参数的单个元素,除非后面跟着以下修饰符之一

符号 前面符号的语义

?

optional

*

绑定到实际参数的零个或多个未绑定元素。 当 * 出现在预期参数名称的位置时,将接受任意数量的附加参数。 此功能可用于实现“varargs”规则。

+

绑定到实际参数的一个或多个未绑定元素。

检查实际参数和形式参数是否不一致,这会导致 b2 以错误代码退出

### argument error
# rule report ( pronoun index ?  : state  : names + )
# called with: ( I 2 foo  : sorry  : Joe Dave Pete )
# extra argument foo
### argument error
# rule report ( pronoun index ?  : state  : names + )
# called with: ( I 2  : sorry )
# missing argument names

如果省略形式参数列表,则会像“经典”Jam 中一样绕过所有检查。 但是,参数列表大大提高了规则的可靠性和可读性,并且强烈建议您为编写的任何新 Jam 代码使用参数列表。

12.2.4. 内置规则

B2 具有不断增长的内置规则集,所有这些规则都是没有更新动作的纯过程规则。 它们分为三组:第一组构建依赖关系图; 第二组修改它; 第三组只是实用程序规则。

依赖关系构建
DEPENDS
rule DEPENDS ( targets1 * : targets2 * )

构建直接依赖关系:使 targets1 中的每个目标都依赖于 targets2 中的每个目标。 通常,如果 targets2 本身被重建或比 targets1 新,则 targets1 将被重建。

INCLUDES
rule INCLUDES ( targets1 * : targets2 * )

构建同级依赖关系:使依赖于 targets1 中任何目标的任何目标也依赖于 targets2 中的每个目标。 这反映了一个源文件包含另一个源文件时产生的依赖关系:从源文件构建的对象既依赖于原始源文件也依赖于包含的源文件,但是两个源文件彼此不依赖。 例如

DEPENDS foo.o : foo.c ;
INCLUDES foo.c : foo.h ;

在此示例中,foo.o 依赖于 foo.cfoo.h

修改绑定

六个规则 ALWAYSLEAVESNOCARENOTFILENOUPDATETEMPORARY 修改依赖关系图,以便 b2 在其目标绑定阶段以不同的方式处理目标。 请参阅上面的绑定。 通常,如果目标丢失、如果其文件系统修改时间比其任何依赖项(递归地)旧,或者如果其任何依赖项正在更新,则 b2 会更新目标。 可以通过调用以下规则来更改此基本行为

ALWAYS
rule ALWAYS ( targets * )

导致 targets 被重建,而不管它们是否是最新的(它们仍然必须在依赖关系图中)。 这用于 clean 和 uninstall 目标,因为它们没有依赖项,否则似乎永远不需要构建。 它最好应用于也是 NOTFILE 目标的目标,但是它也可以用于强制更新真实文件。

LEAVES
rule LEAVES ( targets * )

使 targets 中的每个目标仅依赖于其叶子源,而不依赖于任何中间目标。 这使其免受其依赖项更新的影响,因为“叶子”依赖项是那些没有自己的依赖项和没有更新动作的依赖项。 这允许仅在原始源文件更改时才更新目标。

NOCARE
rule NOCARE ( targets * )

导致 b2 忽略既找不到又没有更新动作来构建它们的 targets。 通常,对于此类目标,b2 会发出警告,然后跳过依赖于这些缺失目标的其他目标。 Jambase 中的 HdrRule 在头文件扫描期间找到的头文件名上使用 NOCARE,以让 b2 知道包含的文件可能不存在。 例如,如果 #include#ifdef 中,则包含的文件可能实际上不存在。

对于具有构建动作的目标:如果它们的构建动作以非零返回代码退出,则仍将构建依赖目标。
NOTFILE
rule NOTFILE ( targets * )

targets 标记为伪目标而不是真实文件。 不会检查时间戳,因此仅当目标的依赖项更新,或者目标也被标记为 ALWAYS 时,才执行此类目标上的动作。 默认的 b2 目标 all 是一个伪目标。 在 Jambase 中,NOTFILE 用于定义几个附加的方便伪目标

NOUPDATE
rule NOUPDATE ( targets * )

导致忽略 targets 上的时间戳。 这有两个影响:首先,一旦创建目标,它将永远不会被更新; 其次,手动更新目标不会导致其他目标被更新。 例如,在 Jambase 中,此规则由 MkDir 规则应用于目录,因为 MkDir 只关心目标目录是否存在,而不关心它上次更新的时间。

TEMPORARY
rule TEMPORARY ( targets * )

targets 标记为临时目标,允许在依赖于它们的其他目标更新后将其删除。 如果 TEMPORARY 目标丢失,则 b2 使用目标父级的时间戳。 Jambase 使用 TEMPORARY 来标记在库中存档后归档的对象文件,以便可以在存档后删除它们。

FAIL_EXPECTED
rule FAIL_EXPECTED ( targets * )

为了处理预期构建动作会失败的目标(例如,在测试断言或编译时类型检查是否正常工作时),Boost Jam 提供了与 NOCARE 等样式相同的 FAIL_EXPECTED 规则。 在目标更新期间,FAIL_EXPECTED 参数的构建动作的返回代码会反转:如果失败,则依赖目标的构建将继续,就像它成功一样。 如果成功,则跳过依赖目标。

RMOLD
rule RMOLD ( targets * )

当用于构建这些目标的规则失败时,B2 会删除可能存在于磁盘上的任何目标文件。 但是,默认情况下不会删除依赖项构建失败的目标。 RMOLD 规则导致删除其依赖项构建失败的参数。

ISFILE
rule ISFILE ( targets * )

ISFILE 将目标标记为必须是文件。 这更改了 b2 搜索目标的方式,使其忽略文件系统中不是文件(如目录)的项目的匹配项。 这使得可以避免 #include "exception" 匹配,如果碰巧在头文件搜索路径中有一个名为 exception 的目录。

目前尚未完全实现。
实用程序

ECHOEXIT 这两个规则是实用程序规则,仅在 b2 的解析阶段使用。

ECHO
rule ECHO ( args * )

将消息 args 打印到 stdout。

EXIT
rule EXIT ( message * : result-value ? )

message 打印到 stdout,然后退出并返回失败状态(如果未给出 result-value),否则退出并返回给定的 result-value

EchoechoExitexit 被接受为 ECHOEXIT 的别名,因为很难分辨出这些是内置规则而不是语言的一部分,例如 include

GLOB

GLOB 规则执行文件名 globbing

rule GLOB ( directories * : patterns * : downcase-opt ? )

使用与 switch 语句中的模式相同的通配符。 它通过用作 "[ ]" 内规则调用的参数来调用。 例如:FILES = [ GLOB dir1 dir2 : *.c *.h ]FILES 设置为 dir1dir2 中 C 源文件和头文件的列表。 结果文件名是完整路径名,包括目录,但模式仅应用于不带目录的文件名。

如果提供了 downcase-opt,则文件名在与模式匹配之前转换为全小写; 您可以使用此方法使用小写模式进行不区分大小写的匹配。 如果操作系统提供路径,则返回的路径仍将具有混合大小写。 在 Windows NT 和 Cygwin 以及 OpenVMS 上,文件名始终在匹配之前转换为小写。

GLOB_ARCHIVE

GLOB_ARCHIVE 规则执行对象存档成员的名称 globbing。

rule GLOB_ARCHIVE ( archives * : member-patterns * : downcase-opt ? : symbol-patterns ? )

GLOB 类似,此规则用于匹配存档(静态对象库)中的成员文件名。 返回成功匹配的成员列表,否则返回 null。 结果成员名称以 archive-path(member-name) 形式限定包含存档的路径名。 成员模式仅用于匹配成员名称; 当未指定通配符时,假定为完全匹配。 成员名称通常对应于对象文件名,因此是特定于平台的,在匹配模式中使用平台定义的对象后缀可以实现可移植性。

如果提供了 downcase-opt,则成员名称在与模式匹配之前转换为全小写; 您可以使用此方法使用小写模式进行不区分大小写的匹配。 如果操作系统提供路径,则返回的路径仍将具有混合大小写。 在 Windows NT、Cygwin 和 OpenVMS 上,文件名始终在匹配之前转换为小写。

此外,可以在支持的平台(目前仅限 OpenVMS)上使用符号/函数模式匹配成员。 在这种情况下,将返回包含匹配符号的成员。 成员和符号模式以 OR 条件应用,成员模式优先。 在不支持的平台上,当指定任何符号模式时,返回 null。

MATCH

MATCH 规则执行模式匹配。

rule MATCH ( regexps + : list * )

egrep(1) 样式的正则表达式 regexpslist 中的字符串进行匹配。 结果是 list 中每个字符串以及 regexps 中每个正则表达式的匹配 () 子表达式的列表。

BACKTRACE
rule BACKTRACE ( )

返回四元组列表:filename line module rulename…​,描述调用堆栈的每个较浅层级。 此规则可用于从 Jam 规则生成有用的诊断消息。

UPDATE
rule UPDATE ( targets * )

经典 jam 将命令行的任何非选项元素视为要更新的目标的名称。 这阻止了更复杂的命令行处理。 现在再次启用此功能,但对 UPDATE 规则进行了额外更改,以允许灵活更改要更新的目标列表。 UPDATE 规则具有两个效果

  1. 它清除要更新的目标列表,并且

  2. 导致更新指定的目标。

如果未使用 UPDATE 规则指定任何目标,则不会更新任何目标。 为了支持以更有用的方式更改更新列表,该规则还会返回先前在更新列表中的目标。 这使得可以添加目标,如下所示

local previous-updates = [ UPDATE ] ;
UPDATE $(previous-updates) a-new-target ;
W32_GETREG
rule W32_GETREG ( path : data ? )

仅针对 win32 平台定义。 它读取 Windows 的注册表。 “path”是信息的位置,“data”是我们要获取的值的名称。 如果省略“data”,则将返回“path”的默认值。 “path”值必须符合 MS 密钥路径格式,并且必须以预定义的根密钥之一为前缀。 像往常一样,

  • HKLM 等效于 HKEY_LOCAL_MACHINE

  • HKCU 等效于 HKEY_CURRENT_USER

  • HKCR 等效于 HKEY_CLASSES_ROOT

不支持其他预定义的根密钥。

当前支持的数据类型:REG_DWORDREG_SZREG_EXPAND_SZREG_MULTI_SZREG_DWORD 类型的数据将转换为字符串,REG_MULTI_SZ 转换为字符串列表,对于 REG_EXPAND_SZ 类型的数据,其中的环境变量将被替换为其定义的值。 REG_SZ 类型和其他不支持类型的数据将放入字符串中,而无需修改。 如果无法接收数据的值,则仅返回空列表。 例如,

local PSDK-location =
  [ W32_GETREG HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MicrosoftSDK\\Directories : "Install Dir" ] ;
W32_GETREGNAMES
rule W32_GETREGNAMES ( path : result-type )

仅针对 win32 平台定义。 它读取 Windows 的注册表。 “path”是信息的位置,“result-type”是 subkeysvalues。 有关“path”格式和约束的更多信息,请参阅 W32_GETREG

根据“result-type”,该规则返回以下内容之一

subkeys

path”的所有直接子键的名称。

values

由“path”给出的注册表项中包含的值的名称。 仅当密钥的“默认”值已在注册表中设置时,它才会出现在返回的列表中。

如果无法识别“result-type”或无法检索请求的数据,则该规则返回一个空列表。 示例

local key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths" ;
local subkeys = [ W32_GETREGNAMES "$(key)" : subkeys ] ;
for local subkey in $(subkeys)
{
    local values = [ W32_GETREGNAMES "$(key)\\$(subkey)" : values ] ;
    for local value in $(values)
    {
        local data = [ W32_GETREG "$(key)\\$(subkey)" : "$(value)" ] ;
        ECHO "Registry path: " $(key)\\$(subkey) ":" $(value) "=" $(data) ;
    }
}
SHELL
rule SHELL ( command : * )

SHELL 执行 command,然后返回 command 的标准输出。 SHELL 仅适用于 C 库中具有 popen() 函数的平台。 在没有正常工作的 popen() 函数的平台上,SHELL 实现为 no-op。 SHELL 适用于 Unix、MacOS X 和大多数 Windows 编译器。 在 Windows 下的 Metrowerks 编译器上,SHELL 是 no-op。 有一组允许的选项作为附加参数

exit-status

除了输出之外,执行命令的结果状态也作为结果的第二个元素返回。

no-output

不捕获命令的输出。 而是返回一个空 ("") 字符串值来代替输出。

strip-eol

从输出中删除尾随行尾字符(如果有)。

由于 Perforce/Jambase 定义了一个隐藏内置规则的 SHELL 规则,因此在这种情况下,可以使用 COMMAND 作为 SHELL 的别名。

MD5
rule MD5 ( string )

MD5 计算作为参数传递的字符串的 MD5 哈希值并返回它。

SPLIT_BY_CHARACTERS
rule SPLIT_BY_CHARACTERS ( string : delimiters )

SPLIT_BY_CHARACTERSdelimiters 中存在的任何分隔符字符上拆分指定的 string,并返回结果列表。

PRECIOUS
rule PRECIOUS ( targets * )

PRECIOUS 规则指定即使更新该目标的命令失败,也不应删除作为参数传递的每个目标。

PAD
rule PAD ( string : width )

如果 string 短于 width 个字符,则在右侧用空格字符填充它,并返回结果。 否则,返回未修改的 string

FILE_OPEN
rule FILE_OPEN ( filename : mode )

如果 mode 参数为 “w” 或 “r”,则 FILE_OPEN 规则打开指定的文件并返回文件描述符。 请注意,目前只有 UPDATE_NOW 规则可以使用结果文件描述符编号。 如果 mode 参数为 “t”,则将文件作为文本文件打开,并将内容作为单个字符串返回。

UPDATE_NOW
rule UPDATE_NOW ( targets * : log ? : ignore-minus-n ? )

UPDATE_NOW 导致立即更新指定的目标。 如果更新成功,则返回非空字符串。 log 参数(如果存在)指定一个文件的描述符,构建的所有输出都将重定向到该文件。 如果指定了 ignore-minus-n 参数,即使在命令行上指定了 -n 参数,也会更新目标。

12.2.5. 流程控制

B2 有几个简单的流程控制语句

for var in list { statements }

list 中的每个元素执行 statements,并将变量 var 设置为元素值。

if cond { statements }
[ else { statements } ]

执行显而易见的操作; else 子句是可选的。 cond 由以下内容构建

a

如果任何 a 元素是非零长度字符串,则为 true

a = b

列表 a 与列表 b 逐字符串匹配

a != b

列表 a 与列表 b 不匹配

a < b

a[i] 字符串小于 b[i] 字符串,其中 i 是列表 ab 中的第一个不匹配元素

a <= b

每个 a 字符串都小于或等于其 b 对应项

a > b

a[i] 字符串大于 b[i] 字符串,其中 i 是第一个不匹配元素

a >= b

每个 a 字符串都大于或等于其 b 对应项

a in b

如果可以在 b 中找到 a 的所有元素,或者如果 a 没有元素,则为 true

! cond

条件不为真

cond && cond

合取

cond || cond

析取

( cond )

优先级分组

include file ;

导致 b2 读取名为 file 的文件。 file 像常规目标一样绑定(请参阅上面的绑定),但不像常规目标,include file 无法构建。

include file 在解析阶段插入到输入流中。 主输入文件和所有包含的文件被视为单个文件; 也就是说,b2 不会从包含的文件中推断出任何作用域边界。

local vars [ = values ] ;

在封闭的 {} 块内创建新的 vars,从而遮蔽它们可能具有的任何先前值。 当前块结束时,将恢复 vars 的先前值。 任何调用的规则或包含的文件都将看到本地值,而不是先前的值(这有时称为动态作用域)。 local 语句可以出现在任何位置,甚至在块外部(在这种情况下,输入结束时将恢复先前的值)。 如果存在,则 vars 初始化为 values,否则保持未初始化。

return values ;

在规则主体中,return 语句设置规则调用的返回值并返回到调用方。

switch value
{
    case pattern1 : statements ;
    case pattern2 : statements ;
    ...
}

switch 语句执行零个或一个封闭的 statements,具体取决于哪个(如果有)是第一个 patternvalue 匹配的 case。 pattern 值未进行变量扩展。 模式值可能包括以下通配符

?

?

*

*

[chars]

匹配 chars 中的任何单个字符

[^chars]

匹配不在 chars 中的任何单个字符

\x

匹配 x(转义其他通配符)

while cond { statements }

只要 cond 在输入时保持为 true,就重复执行 statements。 (请参阅上面 if 下的 cond 表达式语法的描述)。

break ;

立即退出最近的封闭 while 或 for 循环。

continue ;

跳转到最近的封闭 while 或 for 循环的顶部。

12.2.6. 变量

B2 变量是零个或多个元素的列表,每个元素都是字符串值。 未定义的变量与具有空列表的变量无法区分,但是,定义的变量可能有一个或多个为空字符串的元素。 所有变量都引用为 $(variable)

变量可以是全局的,也可以是特定于目标的。 在后一种情况下,变量仅在更新特定目标期间采用给定的值。

变量使用以下方式定义:

variable = elements ;
variable += elements ;
variable on targets = elements ;
variable on targets += elements ;
variable default = elements ;
variable ?= elements ;

前两种形式全局设置 variable。 第三种和第四种形式设置特定于目标的变量。 = 运算符将 variable 的任何先前元素替换为 elements+= 操作将 elements 添加到 variable 的元素列表中。 最后两种形式是同义的:它们全局设置 variable,但仅当它先前未设置时才设置。

在更新命令中引用的变量将被其值替换; 特定于目标的值优先于全局值。 作为参数 ($(1)$(2)) 传递给动作的变量将被其绑定值替换; 可以在动作上使用 bind 修饰符,以使其他变量被绑定值替换。 请参阅上面的动作修饰符。

B2 变量不会重新导出到执行更新动作的 shell 的环境中,但是更新动作可以使用 $(variable) 引用 b2 变量。

变量展开

在解析过程中,b2 对每个不是关键字或规则名称的标记执行变量展开。此类带有嵌入式变量引用的标记将被替换为零个或多个标记。变量引用的形式为 $(v)$(vm),其中 v 是变量名,m 是可选的修饰符。

规则动作中的变量展开与语句中的变量展开类似,不同之处在于,动作字符串在空格处被标记化,而无需引号。

变量展开后,标记的结果是标记组件的乘积,其中每个组件是一个文字子字符串或替换变量引用的列表。例如

$(X) -> a b c
t$(X) -> ta tb tc
$(X)z -> az bz cz
$(X)-$(X) -> a-a a-b a-c b-a b-b b-c c-a c-b c-c

变量名和修饰符本身可以包含变量引用,并且这部分也参与乘积运算

$(X) -> a b c
$(Y) -> 1 2
$(Z) -> X Y
$($(Z)) -> a b c 1 2

由于这种乘积展开,如果标记中的任何变量引用未定义,则展开的结果为空列表。如果任何变量元素为空字符串,则结果会传播非空元素

$(X) -> a ""
$(Y) -> "" 1
$(Z) ->
-$(X)$(Y)- -> -a- -a1- -- -1-
-$(X)$(Z)- ->

变量元素的字符串值可以解析为 grist 和文件名相关组件。变量的修饰符用于选择元素、选择组件和替换组件。修饰符如下:

[n]

选择元素编号 n(从 1 开始)。如果变量包含的元素少于 n 个,则结果为零元素列表。n 可以为负数,在这种情况下,返回从末尾向左数第 n 个元素。

[n-m]

选择元素编号 nmnm 可以为负数,在这种情况下,它们指的是从末尾向左数的元素。

[n-]

选择元素编号 n 到最后一个。n 可以为负数,在这种情况下,它指的是从末尾向左数的元素。

:B

选择文件名基本名 — 不带扩展名的基本名称。

:S

选择文件扩展名 — 一个(最后一个)文件名后缀。

:M

选择归档成员名称。

:D

选择目录路径。

:P

选择父目录。

:G

选择 grist。

:U

将小写字符替换为大写。

:L

将大写字符替换为小写。

:T

将所有反斜杠 (“\”) 转换为正斜杠 (“/”)。例如

x = "C:\\Program Files\\Borland" ; ECHO $(x:T) ;

打印 C:/Program Files/Borland

:W

当从 Cygwin 调用基于 Windows 的工具时,向它们传递真正的 Windows 风格路径可能很重要。:W 修饰符,仅在 Cygwin 下,使用 cygwin_conv_to_win32_path 函数将 Cygwin 路径转换为 Win32 路径。例如

x = "/cygdrive/c/Program Files/Borland" ; ECHO $(x:W) ;

在 Cygwin 上打印 C:\Program Files\Borland

类似地,当在 OpenVMS 上使用时,:W 修饰符使用 decc$to_vms CRTL 函数将 POSIX 风格的路径转换为本机 VMS 风格的格式。此修饰符通常在动作块内部使用,以正确指定 VMS 特定命令中的文件路径。例如

x = "subdir/filename.c" ; ECHO $(x:W) ;

在 OpenVMS 上打印 [.subdir]filename.c

在其他平台上,字符串保持不变。

:chars

选择 chars 中列出的组件。

例如,:BS 选择文件名(基本名和扩展名)。

:G=grist

grist 替换 grist。

:D=path

path 替换目录。

:B=base

base 替换文件名的基本部分。

:S=suf

suf 替换文件名的后缀。

:M=mem

mem 替换归档成员名称。

:R=root

如果尚未根目录化,则将 root 前置到整个文件名。

:E=value

如果变量未设置,则为其分配 value

:J=joinval

将列表元素连接成单个元素,用 joinval 分隔。

:O=value

设置变量评估的语义选项。value 的格式特定于变量或生成的文件扩展。

在 VMS 上,$(var:P)$(var:D) 的父目录。

:⇐value

在评估变量的扩展后,将给定的 value 前缀到展开的表达式值中的元素。

:>=value

在评估变量的扩展后,将给定的 value 后缀到展开的表达式值中的元素。

局部 For 循环变量

Boost Jam 允许您在循环中声明局部 for 循环控制变量

x = 1 2 3 ;
y = 4 5 6 ;
for local y in $(x)
{
    ECHO $(y) ; # prints "1", "2", or "3"
}
ECHO $(y) ;     # prints "4 5 6"
生成文件扩展

在扩展表达式期间,b2 还会查找 @(filename:E=filecontents) 形式的子表达式,并在创建给定文件后,用 filename 替换表达式,文件的内容设置为 filecontents。这对于创建编译器响应文件和其他“内部”文件很有用。扩展在解析和动作执行期间都有效。因此,可以在三个构建阶段中的任何阶段创建文件。此扩展遵循与变量扩展相同的修饰符。生成的文件扩展接受以下 (:O=) 扩展选项值

F

始终用生成的文件名替换 @() 引用。

C

始终用内容替换 @() 引用,即 :E=value 表达式中的 value

FCCF

根据内容长度 (:E=value) 替换为文件或内容。如果命令长度短于允许的命令长度限制,它将在动作中替换为内容。否则,引用将替换为文件名。

内建变量

本节讨论对 b2 具有特殊含义的变量。所有这些都必须在全局模块中定义或使用 — 在命名模块内部使用这些变量将不会产生预期的效果。请参阅 模块

SEARCH 和 LOCATE

这两个变量控制文件目标名称到文件系统中位置的绑定。通常,$(SEARCH) 用于查找现有源文件,而 $(LOCATE) 用于修复构建目标的location。

根目录(绝对路径)文件目标按原样绑定。未根目录的文件目标名称通常也按原样绑定,因此相对于当前目录,但 $(LOCATE)$(SEARCH) 的设置会改变这一点

  • 如果设置了 $(LOCATE),则目标相对于 $(LOCATE) 中的第一个目录绑定。只有第一个元素用于绑定。

  • 如果设置了 $(SEARCH),则目标绑定到 $(SEARCH) 中目标文件已存在的第一个目录。

  • 如果 $(SEARCH) 搜索失败,则目标仍然相对于当前目录绑定。

$(SEARCH)$(LOCATE) 都应设置为特定于目标的,而不是全局的。如果它们是全局设置的,则 b2 将对所有文件绑定使用相同的路径,这不太可能产生合理的结果。在编写您自己的规则时,特别是那些不是基于 Jambase 中的规则构建的规则时,您可能需要直接设置 $(SEARCH)$(LOCATE)。Jambase 中定义的大多数规则都为它们正在查找的源文件和它们创建的目标文件分别设置了明智的 $(SEARCH)$(LOCATE) 值。

HDRSCAN 和 HDRRULE

这两个变量控制头文件扫描。$(HDRSCAN) 是一个 egrep(1) 模式,用 () 括起文件名,用于在源文件中查找文件包含语句。Jambase 使用 $(HDRPATTERN) 作为 $(HDRSCAN) 的模式。$(HDRRULE) 是一个规则的名称,用于调用扫描结果:扫描的文件是目标,找到的文件是源文件。这是 b2 通过变量设置调用规则的唯一位置。

$(HDRSCAN)$(HDRRULE) 都必须设置才能进行头文件扫描,并且它们应该设置为特定于目标的,而不是全局的。如果它们是全局设置的,则所有文件,包括可执行文件和库,都将被扫描以查找头文件包含语句。

头文件包含的扫描并不精确,但至少是动态的,因此无需运行类似 makedepend(GNU) 的工具来创建静态依赖文件。扫描机制在包含方面出错(即,它更有可能返回编译器实际未使用的文件名,而不是遗漏包含文件),因为它无法判断 #include 行是否在 #ifdefs 或其他条件逻辑中。在 Jambase 中,HdrRuleNOCARE 规则应用于扫描期间找到的每个头文件,以便如果文件尚不存在但不会导致编译失败,则 b2 将不会在意。

此外,正则表达式扫描仅在包含的文件名以字面形式位于源文件中时才有效。它无法处理允许使用变量名包含文件的语言(如 Jam 语言本身)。

信号量

有时希望禁止某些动作的并行执行。例如

  • 旧版本的 yacc 使用具有固定名称的文件。因此,运行两个 yacc 动作是危险的。

  • 人们可能希望执行并行编译,但不执行并行链接,因为链接是 i/o 绑定的,只会变得更慢。

Craig McPeeters 扩展了 Perforce Jam 以解决此类问题,并且该扩展已集成到 Boost.Jam 中。

任何目标都可以通过在该目标上设置名为 JAM_SEMAPHORE 的变量来分配信号量。变量的值是信号量名称。它必须与任何声明的目标的名称不同,但在其他方面是任意的。

信号量的语义是,在一组具有相同信号量的目标中,无论 -j 选项如何,在同一时刻只能更新一个目标。

平台标识符

许多 Jam 内建变量可用于标识运行时平台

OS

OS 标识符字符串

OSPLAT

适用的情况下,为底层架构

MAC

在 MAC 平台上为 true

NT

在 NT 平台上为 true

OS2

在 OS2 平台上为 true

UNIX

在 Unix 平台上为 true

VMS

在 VMS 平台上为 true

Jam 版本
JAMDATE

b2 启动时的时间和日期,采用 ISO-8601 UTC 值。

JAMUNAME

uname(1) 命令的输出(仅限 Unix)

JAMVERSION

b2 版本,采用语义三元组 “X.Y.Z”。

JAM_VERSION

具有两个元素的预定义全局变量,指示 Boost Jam 的版本号。Boost Jam 版本从 03 00 开始。早期版本的 Jam 不会自动定义 JAM_VERSION

JAMSHELL

b2 执行规则的动作块时,它会 fork 并 exec 一个 shell,将动作块作为参数传递给 shell。shell 的调用可以通过 $(JAMSHELL) 控制。Unix 上的默认值例如是

JAMSHELL = /bin/sh -c % ;

% 被替换为动作块的文本。

B2 不直接支持跨多个主机并行构建,因为这在很大程度上取决于本地环境。要跨多个主机并行构建,您需要编写自己的 shell,该 shell 提供对多个主机的访问。然后,您重置 $(JAMSHELL) 以引用它。

正如 b2 扩展 % 以作为规则动作块的文本一样,它扩展 ! 以作为多进程槽号。槽号在 1 到命令行上给出的 -j 标志允许的并发作业数之间变化。有了这个,就可以编写一个多主机 shell。例如

#!/bin/sh

# This sample JAMSHELL uses the SunOS on(1) command to execute a
# command string with an identical environment on another host.

# Set JAMSHELL = jamshell ! %
#
# where jamshell is the name of this shell file.
#
# This version handles up to -j6; after that they get executed
# locally.

case $1 in
1|4) on winken sh -c "$2";;
2|5) on blinken sh -c "$2";;
3|6) on nod sh -c "$2";;
*) eval "$2";;
esac
__TIMING_RULE____ACTION_RULE__

可以将 __TIMING_RULE____ACTION_RULE__ 设置为规则的名称,以便 b2 在目标的动作完成后调用。它们都提供有关已完成动作的诊断信息。对于 __TIMING_RULE__,规则调用如下:

rule timing-rule ( args * : target : start end user system )

__ACTION_RULE__ 的调用如下:

rule action-rule ( args * : target : command status start end user system : output ? )

两者的参数都是

args

__TIMING_RULE____ACTION_RULE__ 中规则名称之后的任何值都将在此处传递。

目标

已构建的 b2 目标。

command

在动作主体中执行的命令的文本。

status

执行的命令的整数结果。

start

执行的命令的开始时间戳,采用 ISO-8601 UTC 值。

end

执行的命令的完成时间戳,采用 ISO-8601 UTC 值。

user

执行的命令花费的用户 CPU 秒数,采用浮点值。

system

执行的命令花费的系统 CPU 秒数,采用浮点值。

output

命令的输出,作为单个字符串。输出的内容反映了 -pX 选项的使用。

如果为目标设置了这两个变量,则都会调用,先调用 __TIMING_RULE__,然后调用 __ACTION_RULE__

12.2.7. 模块

Boost Jam 引入了对模块的支持,这为规则和变量提供了一些基本的命名空间保护。还引入了一个新的关键字 module。本节中描述的特性是原语,这意味着它们旨在提供编写 Jam 规则所需的操作,这些规则提供更优雅的模块接口。

声明
module expression { ... }

{ …​ } 中的代码在通过评估表达式命名的模块中执行。可以在模块自身的命名空间中以及全局模块的命名空间中找到规则定义,形式为 模块名称.规则名称,因此在模块内部,始终可以无限定地调用该模块中的其他规则

module my_module
{
    rule salute ( x ) { ECHO $(x), world ; }
    rule greet ( ) { salute hello ; }
    greet ;
}
my_module.salute goodbye ;

当在当前模块的命名空间中找不到调用的规则时,将在全局模块的命名空间中查找它,因此限定调用可以跨模块工作

module your_module
{
    rule bedtime ( ) { my_module.salute goodnight ; }
}
变量作用域

每个模块都有自己的一组动态嵌套的变量作用域。当执行从模块 A 传递到模块 B 时,来自 A 的所有变量绑定都将变为不可用,并被属于 B 的绑定替换。这同样适用于局部变量和全局变量

module A
{
    x = 1 ;
    rule f ( )
    {
        local y = 999 ; # becomes visible again when B.f calls A.g
        B.f ;
    }
    rule g ( )
    {
        ECHO $(y) ;     # prints "999"
    }
}
module B
{
    y = 2 ;
    rule f ( )
    {
        ECHO $(y) ; # always prints "2"
        A.g ;
    }
}

访问另一个模块的变量的唯一方法是进入该模块

rule peek ( module-name ? : variables + )
{
    module $(module-name)
    {
        return $($(>)) ;
    }
}

请注意,由于每当进入新的模块作用域时现有变量绑定都会更改,因此参数绑定将变为不可用。这解释了上面 peek 规则中 $(>) 的使用。

局部规则
local rule rulename...

该规则在当前模块中局部声明。它不会以限定符输入到全局模块中,并且其名称不会出现在以下结果中

[ RULENAMES module-name ]
RULENAMES 规则
rule RULENAMES ( module ? )

返回给定模块中所有非局部规则的名称列表。如果省略 module,则返回全局模块中所有非局部规则的名称。

VARNAMES 规则
rule VARNAMES ( module ? )

返回给定模块中所有变量绑定的名称列表。如果省略 module,则返回全局模块中所有变量绑定的名称。

这包括调用堆栈中尚未返回的规则中的任何局部变量。
IMPORT 规则

IMPORT 允许跨模块的规则名称别名

rule IMPORT ( source_module ? : source_rules *
            : target_module ? : target_rules * )

IMPORT 规则将 source_module 中的规则复制到 target_module 中作为局部规则。如果未提供 source_moduletarget_module,则它指的是全局模块。source_rules 指定要从 source_module 导入哪些规则;target_rules 指定在 target_module 中为这些规则提供的名称。如果 source_rules 包含一个名称,该名称与 source_module 中的规则不对应,或者如果它包含的项目数与 target_rules 不同,则会发出错误。例如,

# import m1.rule1 into m2 as local rule m1-rule1.
IMPORT m1 : rule1 : m2 : m1-rule1 ;
# import all non-local rules from m1 into m2
IMPORT m1 : [ RULENAMES m1 ] : m2 : [ RULENAMES m1 ] ;
EXPORT 规则

EXPORT 允许跨模块的规则名称别名

rule EXPORT ( module ? : rules * )

EXPORT 规则将来自 source_modulerules 标记为非局部(因此可导出)。如果 rules 的元素未命名 module 中的规则,则会发出错误。例如,

module X {
  local rule r { ECHO X.r ; }
}
IMPORT X : r : : r ; # error - r is local in X
EXPORT X : r ;
IMPORT X : r : : r ; # OK.
CALLER_MODULE 规则
rule CALLER_MODULE ( levels ? )

CALLER_MODULE 返回包围对其调用者的调用的模块作用域的名称(如果提供了 levels,则将其解释为要遍历以定位模块的额外调用堆栈层数)。如果作用域属于全局模块,或者如果不存在此类模块,则返回空列表。例如,以下内容打印 “{Y} {X}”

module X {
    rule get-caller { return [ CALLER_MODULE ] ; }
    rule get-caller's-caller { return [ CALLER_MODULE 1 ] ; }
    rule call-Y { return Y.call-X2 ; }
}
module Y {
    rule call-X { return X.get-caller ; }
    rule call-X2 { return X.get-caller's-caller ; }
}
callers = [ X.get-caller ] [ Y.call-X ] [ X.call-Y ] ;
ECHO {$(callers)} ;
DELETE_MODULE 规则
rule DELETE_MODULE ( module ? )

DELETE_MODULE 从给定模块(或全局模块,如果未提供模块)中删除所有变量绑定和未被引用的规则,并将其内存返回给系统。

虽然它不会影响当前正在执行的规则,直到它们完成,但应极其谨慎地使用 DELETE_MODULE,因为它会立即清除任何其他规则和所有变量(包括该模块中的局部变量)。由于动态绑定的工作方式,被局部变量遮蔽的变量不会被销毁,因此结果可能真的无法预测。

12.3. 其他

12.3.1. 诊断

除了通用错误消息外,b2 可能会发出以下消息之一

warning: unknown rule X

调用了未通过 actionsrule 语句定义的规则。

using N temp target(s)

找到了标记为临时的(但仍然存在的)目标。

updating N target(s)

目标已过期,将被更新。

can't find N target(s)

找不到源文件,也没有创建它们的动作。

can't make N target(s)

由于找不到源文件,因此无法创建其他目标。

warning: X depends on itself

目标直接或通过其源文件依赖于自身。

don't know how to make X

目标不存在,并且未定义任何用于创建它的动作。

X skipped for lack of Y

源文件构建失败,因此无法构建目标。

warning: using independent target X

未被任何其他目标依赖的目标正在使用 $(<)$(>) 引用。

X removed

B2 在中断后删除了部分构建的目标。

12.3.2. 错误、限制

为了使并行构建成功,文件之间的依赖关系必须正确地拼写出来,因为目标倾向于以最快优先的顺序构建。此外,请注意会将固定名称的文件放入当前目录的不可并行命令,例如 yacc(1) 的行为。

设置不佳的 $(JAMSHELL) 很可能导致静默失败。

12.3.3. 基本原理

本节源自 Jam 官方文档以及使用它和阅读 Jambase 规则的经验。我们在此重复这些信息主要是因为它对于理解和使用 Jam 至关重要,但并未整合在一个地方。其中一些信息在官方文档中完全缺失。我们希望它对任何希望熟悉 Jam 和 Boost 构建系统的人都有用。

  • Jam 规则 实际上是简单的过程实体。将它们视为函数。参数用冒号分隔。

  • Jam 目标是由任意字符串标识的抽象实体。内建的 DEPENDS 规则在命名目标之间创建依赖关系图中的链接。

  • 请注意,原始 Jam 文档中关于内建 INCLUDES 规则的描述是不正确的:INCLUDES targets1 : targets2 导致依赖于 targets1 成员的所有内容都依赖于 targets2 的所有成员。它以一种奇怪的方式执行此操作,通过将 targets2 附加到 targets1 中所有内容的依赖关系列表的特殊尾部部分。以这种方式创建循环依赖关系似乎是可以的;事实上,当单个构建动作同时生成 targets1targets2 时,这似乎是“正确的事情”。

  • 当调用规则时,如果声明了与规则同名的 actions,则这些动作将添加到由规则的第一个参数标识的目标的更新动作中。如果声明了相应的动作,则实际上可以调用未声明的规则:该规则被视为空规则。

  • 目标(NOTFILE 目标除外)通过称为绑定的过程与文件系统中的路径关联。绑定是基于目标特定 SEARCHLOCATE 变量的设置,搜索与目标同名(不包括 grist)的文件的过程。

  • 除了局部变量和全局变量之外,jam 还允许您在目标上设置变量。特定于目标的变量值通常无法读取,并且仅在以下上下文中生效

    • 在更新动作中,变量值首先在由第一个参数(正在更新的目标)命名的目标上查找。由于 Jam 在执行动作之前构建其整个依赖关系树,因此 Jam 规则将特定于目标的变量设置作为向相应动作提供参数的一种方式。

    • 绑定完全SEARCHLOCATE 变量的特定于目标的设置控制,如此处所述。

    • 在用于头文件扫描的特殊规则中,变量值首先在由规则的第一个参数(正在扫描的源文件)命名的目标上查找。

  • 变量的“绑定值”是与变量命名的目标关联的路径。在构建动作中,前两个参数会自动替换为其绑定值。可以使用 bind 动作修饰符有选择地将特定于目标的变量替换为其绑定值。

  • 请注意,Jam 文档中使用的术语“绑定”表示一个处理阶段,其中包括三个子阶段:绑定(是的!)、更新确定和头文件扫描。“绑定”一词的重复使用可能会导致一些混淆。特别是,Jam 文档中的“修改绑定”部分可能应标题为“修改更新确定”。

  • “Grist”只是 <字符> 形式的字符串前缀。它在 Jam 中用于基于更简单的名称创建唯一的目标名称。例如,文件名 test.exe 可以由单独的子项目中的目标或“相同”抽象目标的调试和发布变体使用。绑定到名为 “test.exe” 的文件的每个不同目标都有其自己唯一的 grist 前缀。Boost 构建系统还充分利用 Jam 将字符串划分为 grist 边界的能力,有时会在字符串的开头连接多个 grist 元素。使用 Grist 而不是用绝对路径标识目标有两个原因

    1. 目标的location并非总是可以仅从用户在 Jamfile 中放置的内容中派生出来,有时还取决于绑定过程。仍然需要某种机制来明确标识具有相同名称的目标。

    2. Grist 允许我们为每个构建目标使用统一的抽象标识符,而无需考虑目标文件location(如 ALL_LOCATE_TARGET 设置所允许的)。

  • 当从名称中提取 grist 时,使用 $(var:G),结果包括前导和尾随尖括号。当使用 $(var:G=expr) 将 grist 添加到名称时,首先剥离现有 grist。然后,如果 expr 为非空,则根据需要添加前导 <s 和尾随 >s 以形成 <expr2> 形式的表达式;然后前置 <expr2>。

  • 当调用 Jam 时,它会将所有环境变量设置导入到相应的 Jam 变量中,然后导入所有命令行 (-s…) 变量设置。名称以 PATH、Path 或 path 结尾的变量在特定于操作系统的路径列表分隔符边界(例如,UNIX 的 “:” 和 Windows 的 “;”)上拆分为字符串列表。所有其他变量都在空格 (“ ”) 边界上拆分。Boost Jam 修改了该行为,允许对变量进行引号。

  • 值为空列表或完全由空字符串组成的变量具有负逻辑值。因此,例如,以下代码允许一个合理的非空默认值,用户可以轻松地覆盖它

    MESSAGE ?\= starting jam... ;
    if $(MESSAGE) { ECHO The message is: $(MESSAGE) ; }

    如果用户想要特定消息,他可以使用 -sMESSAGE=message text 调用 jam。如果他不想显示任何消息,他可以使用 -sMESSAGE= 调用 jam,并且不会打印任何内容。

  • Jam 中命令行选项的解析可能相当违反直觉,关于其他 Unix 程序如何接受选项。作为选项的有效值接受两种变体

    1. -xvalue,以及

    2. -x value.

13. 实现参考

这包括 B2 引擎代码内部结构的参考文档。即使面向 Jam 的构建系统接口也在引擎中实现,它们也记录在通用参考部分中。本节更深入,包括引擎机制和数据结构的部分。它适用于对引擎本身进行更改的人员。

13.1. b2::list_cref

b2 值的容器,即 LIST 的非拥有引用。主要遵循随机访问容器行为。

13.1.1. b2::list_cref 概述

struct list_cref
{
	// types
	struct iterator;
	using size_type = int32_t;
	using value_type = OBJECT *;

	// construct/copy/destroy
	list_cref() = default;
	list_cref(const list_cref &) = default;
	list_cref(list_cref && other);
	explicit list_cref(LIST * l);
	list_cref & operator=(const list_cref &) = default;

	// iterators
	iterator begin() const;
	iterator end() const;

	// capacity
	bool empty() const B2_NOEXCEPT;
	size_type length() const B2_NOEXCEPT;
	size_type size() const B2_NOEXCEPT;

	// element access
	value_type & operator[](size_type i) const;

	// list operations
	bool contains(value_ref a) const;
	list_ref slice(size_type i, size_type j = -1) const;
	bool operator==(const list_cref & b) const;
	bool operator==(const list_ref & b) const;

	// data access
	LIST * data() const B2_NOEXCEPT;
	LIST * operator*() const B2_NOEXCEPT;

	protected:
	friend struct iterator;
	LIST * list_obj = nullptr;
};

13.1.2. b2::list_cref 构造/复制/销毁

b2::list_cref::list_cref
inline list_cref::list_cref(list_cref && other)
inline list_cref::list_cref(LIST * l)

13.1.3. b2::list_cref 迭代器

b2::list_cref::begin
inline list_cref::iterator list_cref::begin() const
b2::list_cref::end
inline list_cref::iterator list_cref::end() const

13.1.4. b2::list_cref 容量

b2::list_cref::empty
inline bool list_cref::empty() const B2_NOEXCEPT
b2::list_cref::length
inline list_cref::size_type list_cref::length() const B2_NOEXCEPT
inline list_cref::size_type list_cref::size() const B2_NOEXCEPT

13.1.5. b2::list_cref 元素访问

b2::list_cref::operator[]
inline list_cref::value_type & list_cref::operator[](
	list_cref::size_type i) const

13.1.6. b2::list_cref 列表操作

b2::list_cref::contains
inline bool list_cref::contains(value_ref a) const
b2::list_cref::slice
inline list_ref list_cref::slice(
	list_cref::size_type i, list_cref::size_type j) const
b2::list_cref::operator==
inline bool list_cref::operator==(const list_cref & b) const
inline bool list_cref::operator==(const list_ref & b) const

13.1.7. b2::list_cref 数据访问

b2::list_cref::operator==
inline LIST * list_cref::data() const B2_NOEXCEPT
inline LIST * list_cref::operator*() const B2_NOEXCEPT

13.2. b2::list_ref

b2 值的容器,即 LIST 的拥有引用。主要遵循随机访问容器行为。并且作为拥有引用,将根据需要分配、复制、移动 LIST 对象。

13.2.1. b2::list_ref 概述

struct list_ref : private list_cref
{
	// types
	using list_cref::iterator;
	using list_cref::size_type;
	using list_cref::value_type;

	using list_cref::begin;
	using list_cref::end;
	using list_cref::empty;
	using list_cref::length;
	using list_cref::size;
	using list_cref::operator[];
	using list_cref::contains;
	using list_cref::operator==;
	using list_cref::data;
	using list_cref::operator*;

	// construct/copy/destroy
	list_ref() = default;
	list_ref(list_ref && other);
	list_ref(const list_cref & other);
	list_ref(const list_ref & other);
	explicit list_ref(value_ref o);
	explicit list_ref(LIST * l, bool own = false);
	list_ref(iterator i, const iterator & e);
	~list_ref();

	// modifiers
	LIST * release();
	void reset(LIST * new_list = nullptr);
	list_ref & append(const list_ref & other);
	list_ref & append(list_cref other);
	list_ref & operator+(const list_ref & other);
	list_ref & operator+(const list_cref & other);
	list_ref & push_back(OBJECT * value);
	template <typename... T>
	list_ref & push_back(T... value);
	template <typename T>
	list_ref & operator+(T value);
	list_ref & pop_front();
	list_ref & operator=(list_ref && other);

	// list operations
	inline list_ref & slice(size_type i, size_type j = -1);
	inline list_cref cref() const;
};

13.2.2. b2::list_ref 构造/复制/销毁

b2::list_ref::list_cref
inline list_ref::list_ref(list_ref && other) // (1)
inline list_ref::list_ref(const list_cref & other) // (2)
inline list_ref::list_ref(const list_ref & other) // (2)
inline list_ref::list_ref(value_ref o) // (3)
inline list_ref::list_ref(LIST * l, bool own) // (4)
inline list_ref::list_ref(iterator i, const iterator & e) // (5)
  1. 列表的数据从 other 移动。

  2. other 复制列表。

  3. 使用初始给定的元素值创建一个新列表。

  4. 如果 own == true,则取得数据 l 的所有权,否则复制它。

  5. 使用 [i,e) 范围内的元素填充新列表。 == b2::list_ref 修饰符 === b2::list_ref::release

inline LIST * list_ref::release()

返回列表数据,放弃其所有权。此列表处于空的有效状态。 === b2::list_ref::reset

inline void list_ref::reset(LIST * new_list)

用给定的 new_list 替换列表数据。当前列表连同列表中的元素一起释放。 === b2::list_ref::append

inline list_ref & list_ref::append(const list_ref & other)
inline list_ref & list_ref::append(list_cref other)
inline list_ref & list_ref::operator+(const list_ref & other)
inline list_ref & list_ref::operator+(const list_cref & other)

将来自 other 列表的元素(进行复制)添加到此列表的末尾。所有函数都返回对列表的引用,以允许链接。例如:list_ref() + "one" + "two"。 === b2::list_ref::push_back

inline list_ref & list_ref::push_back(OBJECT * value)
template <typename... T>
inline list_ref & list_ref::push_back(T... value) // (1)
template <typename... T>
inline list_ref & list_ref::operator+(T value) // (2)
  1. 添加从给定参数构造的值。即通过调用 value::make(value…​)

  2. 添加可转换为 value_typevalue

将单个值添加到列表的末尾。返回列表以允许链接。 === b2::list_ref::pop_front

inline list_ref & list_ref::pop_front()
b2::list_ref::operator=,赋值
inline list_ref & list_ref::operator=(list_ref && other) // (1)
  1. 将数据从 other 移动到此列表。

b2::list_ref::slice
inline list_ref & list_ref::slice(size_type i, size_type j)

就地将列表替换为指示的子范围切片 [i,j]j 的负值索引从 end() 位置开始。

13.3. b2::lists

“列表的列表”的容器,拥有 LOL 对象的实例。该接口允许 LOL 的内联组合。

13.3.1. b2::lists 概述

struct lists
{
	// types
	using size_type = int32_t;

	// construct/copy/destroy
	lists();
	lists(lists && other);
	~lists();

	// capacity
	bool empty() const B2_NOEXCEPT;
	size_type size() const B2_NOEXCEPT;
	size_type length() const B2_NOEXCEPT;
	size_type max_size() const B2_NOEXCEPT;
	size_type capacity() const B2_NOEXCEPT;

	// element access
	list_cref operator[](size_type i) const;

	// modifiers
	void push_back(const list_cref & l);
	void push_back(list_ref && l);
	lists & operator|(const list_cref & l);
	lists & operator|(LIST * l);
	lists & operator|(list_ref && l);
	lists & operator|=(list_ref && l);
	lists & append(const lists & lol);
	lists & operator|(const lists & lol);
	void swap(LOL & other);
	void clear();

	// display
	void print() const;

	// data access
	LOL * data() const B2_NOEXCEPT;
	operator LOL *() const B2_NOEXCEPT;

	private:
	mutable LOL lol;
};

13.3.2. b2::lists 构造/复制/销毁

b2::lists::lists
inline lists::lists()
inline lists::lists(lists && other)
b2::lists::~lists
inline lists::~lists()

13.3.3. b2::lists 容量

13.3.4. b2::lists 元素访问

b2::lists::operator[]
inline list_cref lists::operator[](int32_t i) const

返回给定 i 索引处列表的常量引用,即 list_cref。 == b2::lists 修饰符 === b2::lists::push_back

inline void lists::push_back(const list_cref & l)
inline void lists::push_back(list_ref && l)

将给定的列表添加到 LOL 的末尾。 === b2::lists::operator|

inline lists & lists::operator|(const list_cref & l)
inline lists & lists::operator|(LIST * l)
inline lists & lists::operator|(list_ref && l)
inline lists & lists::operator|=(list_ref && l)

将给定的列表添加到 LOL 的末尾。这将返回此对象,使其可以将添加链接到单个语句中。 === b2::lists::swap

inline void lists::swap(LOL & other)

将来自 other 的数据与此数据交换。 === b2::lists::clear

inline void lists::clear()

释放所有列表项并将长度重置为零。 == b2::lists 元素访问 === b2::lists::print

inline void lists::print() const

将列表输出到 cout,列表以冒号 (:) 分隔,元素以空格分隔。 == b2::lists 数据访问 === b2::lists::data

inline LOL * lists::data() const B2_NOEXCEPT
inline lists::operator LOL *() const B2_NOEXCEPT

返回底层 LOL 对象。

14. 历史

14.1. 5.2.1 版本

此补丁还原了为 Dinkumware std 库定义 _HAS_EXCEPTIONS=0 的更改。它具有更改 ABI 的不良影响。对于用户代码来说,更好地处理关闭异常同时将警告视为错误的情况,并通过让用户自行消除警告来获得 std 库的警告/错误。

此补丁还修复了当已有版本初始化时,请求初始化任何 msvc 工具集版本 (using msvc ;) 的情况。它不再报错说版本已在使用中,而是将已初始化的 msvc 工具集集合视为满足了一般初始化 msvc 的请求。

14.2. 5.2.0 版本

此版本包含来自常规贡献者 Nikita 和 Dmitry 的许多修复。还有一些新功能。首先是在搜索库中具有 dll-path 的能力。这使得更好地支持系统/外部库成为可能。第二个新功能是添加了为 IDE 和工具集成生成 compile_commands.json 的功能。这长期以来一直是一个愿望清单上的项目。

  • 新增:添加了生成 compile_commands.json 命令数据库的支持,用于某些 IDE 集成。— René Ferdinand Rivera Morell

  • 新增:添加了一个用于结构化数据管理的模块 (db)。具有一个 property-db 类,可以以 JSON 数据形式写出。— René Ferdinand Rivera Morell

  • 新增:允许将 dll-path 添加到搜索库的使用要求中。这使得可以在平台上调整动态库的搜索路径。— Dmitry Arkhipov

  • 修复了在递归导入模块时模块的错误递归加载。递归加载会导致堆栈溢出。— René Ferdinand Rivera Morell

  • 默认在 FreeBSD 和 OpenBSD 上使用 Clang 构建。— Nikita Kniazev

  • 修复了引擎中对 OS=SUNOSOS=SOLARIS 的 Solaris/SunOS 检测。— Nikita Kniazev

  • 允许一些工具通过多次 using 进行默认初始化。这些工具是 asciidoctorfopgettextpkg-configpythonsasssaxonhe。— Dmitry Arkhipov

  • 修复了 ac 模块中使用相关功能时会导致不正确的配置检查的问题。— Dmitry Arkhipov

  • 修复了在某些硬件上为地址模型应用 -m31、-m32、-m64 的问题,并避免在不支持时添加它们,通常情况下。— Nikita Kniazev

  • 修复了在引擎引导构建中未针对 Windows 时错误地附加清单的问题。— Nikita Kniazev

  • 修复了 clang-win 工具集的 embed-manifest-via=linker。— Nikita Kniazev

  • 修复了不正确的 unknown 操作系统名称,改为使用正确的 none。这对于正确的 emscripten 工具集构建是必需的。— Nikita Kniazev

  • 修复:对于 msvc 工具集,仅注册已请求初始化的版本。— Nikita Kniazev

  • 修复:在每次测试后抑制虚假的 1 file(s) copied. 消息。— Nikita Kniazev

  • 修复:改进了安装目录的处理。— Dmitry Arkhipov

  • 修复:对于 msvc 工具集,识别 Visual Studio 2022 v17.10.0,它使用工具链版本 14.40.33807。— Dmitry Andric

  • 修复:对于 msvc 工具集,exception-handling=off 应为 Dinkumware/MSSTL 定义 _HAS_EXCEPTIONS=0。— Nikita Kniazev

14.3. 5.1.0 版本

这主要是为了解决影响 Boost 库的问题而发布的错误修复版本。但有一个“重大”更改。在运行大型构建时,可能很难找到构建失败。为了方便找出问题,构建结束时的简要摘要输出现在不再那么简略。它现在包含一个排序的已跳过和失败的目标列表。这些列表的输出反映了一般的跳过/失败项。因此,可以在输出的其余部分快速搜索相同的字符串。

  • 新增:在构建摘要的末尾添加失败和跳过目标的列表,以便更容易找到失败的内容。— René Ferdinand Rivera Morell

  • 新增:向 mpi 工具集添加 mpi.run-flags,允许将任意标志应用于运行 mpi 目标。例如,这允许添加 --oversubscribe 标志,以便可以运行任务多于可用节点的测试。— René Ferdinand Rivera Morell

  • 修复了在标头扫描尝试扫描空文件名时出现的虚假错误。— René Ferdinand Rivera Morell

  • 使 C/C++/ObjC 包含指令扫描模式更严格,以避免尝试扫描空文件名。— Andrey Semashev

  • 修复了 mingw 链接器命令,始终将反斜杠替换为正斜杠。— Christian Seiler

  • 修复了 QCC 调试构建标志。QCC 工具集正在使用旧的、不再支持的调试符号选项。— John McFarlane

14.4. 5.0.1 版本

  • 修复了引擎的旧版本 GCC 和 Clang 工具集的编译错误。我们现在支持使用 GCC 4.7 和 Clang 3.6 及更高版本构建引擎。— René Ferdinand Rivera Morell

  • 修复了 import-search 因不正确的本机与非本机路径处理而无法在 Windows 上查找导入的问题。— René Ferdinand Rivera Morell

  • 支持使用 target-os=xyz 交叉编译安装 B2。— René Ferdinand Rivera Morell

14.5. 5.0.0 版本

这是 B2 的新时代。这个新主要版本的驱动力是将核心构建系统从 Jam 实现转移到 C++。此初始版本仅是此转移的开始,通过使用新的 Jam/C++ 本机绑定系统在 C++ 中实现了一些最基本方面。即使这是一个主要版本,目标仍然是对现有项目构建文件具有向后兼容性。但不保证对其他 Jam 文件的向后兼容性。

  • 新增:支持字符串 (原始 Jam 值类型)、数字 (浮点数) 和对象 (类的实例) 的 Jam 本机变体值。— René Ferdinand Rivera Morell

  • 新增:将模块 classerrorsmodulesregexsetstringsysinfo 移植到 C++。— René Ferdinand Rivera Morell

  • 新增:将 bootstrap.jam 移植到 C++,并改用 build-system.jam 作为查找构建文件的关键文件。— René Ferdinand Rivera Morell

  • 新增:添加 require-b2 内置规则,以验证特定 Jam 文件所需的 B2 版本。— René Ferdinand Rivera Morell

  • 新增:添加 regex.grep 内置函数,该函数使用正则表达式匹配执行并行 (在可用情况下) 文件内容搜索。— René Ferdinand Rivera Morell

  • 新增:使内部部分线程安全,以支持并行内置函数。目前包括 Jam 值、哈希表和文件系统。— René Ferdinand Rivera Morell

  • 新增:添加 import-search 项目规则,以声明 import 的其他搜索路径,这些路径引用搜索到的项目位置或其他目录。— René Ferdinand Rivera Morell

  • 修复了 OPT_SEMAPHORE 的一致使用和 JAM_SEMAPHORE 的文档。— Thomas Brown

  • 修复了 mingw 的存档操作失败。— René Ferdinand Rivera Morell

不再支持或测试使用 VisualStudio 2013(即 MSVC 12)构建 B2。为解决缺少 C++11 功能而进行的工程工作量变得过大。并且占用了其他改进的时间。

14.6. 4.10.1 版本

  • 在 b2 构建中,静默 Apple clang 工具集使用标准弃用函数的警告。— René Ferdinand Rivera Morell

14.7. 4.10.0 版本

此版本包含许多错误修复,但在此过程中,还要感谢 Nikita 对许多工具集进行了清理和重构。

  • 新增:扫描汇编程序文件以查找 C 预处理器包含。— Nikita Kniazev

  • 修复:从基本工具集继承生成器覆盖。— Nikita Kniazev

  • 新增:添加行标记器功能,该功能在预处理目标上更改行为以发出/省略行指令,如 #line#<linenum>。— Nikita Kniazev

  • 修复了 QNX 的编译器名称。— James Choi

  • 修复了 openssl 名称处理。— Dmitry Arkhipov

  • 修复了 clang-win 汇编程序路径推导。— Nikita Kniazev

  • 修复了工具集子功能需求继承。— Nikita Kniazev

  • 统一了 clang-linux 工具集与 gcc 工具集的编译和链接。— Nikita Kniazev

  • 修复了 msvc 工具集的相同目录 pch 标头生成。— Nikita Kniazev

  • 实现了 --durations,它报告执行时间最长的目标。— Nikita Kniazev

  • 将 clang-darwin 更改为从 clang-linux 继承并统一编译命令。— Nikita Kniazev

  • 修复了 clang-linux 不覆盖 RPATH_OPTION 的问题。— Nikita Kniazev

  • 修复了不应运行的意外配置检查 (由 Alexander Grund 报告)。通过将 <build>no 条件评估更改为短路。— Nikita Kniazev

  • 修复了相同的工具集覆盖 (inherit-overrides)。— Nikita Kniazev

  • 新增:添加了使用 C 处理器处理汇编源文件的功能。— Nikita Kniazev

  • 对内部测试进行了许多改进和清理。— Nikita Kniazev

  • 统一了 gcc 和 clang-linux soname 选项处理,并在 Windows 上禁用它。— Nikita Kniazev

  • 统一了 gcc/mingw 共享库和导入库的链接。— Nikita Kniazev

  • 修复了 pdb 生成顺序和命名问题。— Nikita Kniazev

  • 统一了 clang-darwin 与 gcc 的链接。— Nikita Kniazev

  • 修复了 mingw/msys/cygwin、winthreads/pthread 不一致性,以更正编译器标志。— Nikita Kniazev

  • 通过从 clang-linux 继承来统一 clang-vxworks。— Nikita Kniazev

  • 不要存储空的配置缓存和日志。— Nikita Kniazev

  • 修复了生成器自定义规则名称继承。这影响了 cygwin/mingw 链接。— Nikita Kniazev

  • 修复了 testing.execute=off 以更正 run-fail 行为。— Nikita Kniazev

  • 修复了带有本机路径的 use-project。— René Ferdinand Rivera Morell

  • 修复了 msvc 自动配置版本优先级。现在,msvc 工具集以正确的从最新到最旧的顺序配置,无论是否从注册表找到。— René Ferdinand Rivera Morell

  • 新增:添加了对自动搜索外部项目以查找全局目标和项目引用的支持。— René Ferdinand Rivera Morell

14.8. 4.9.6 版本

  • 修复了 clang-win 工具集上 winsdk 的版本检查。— Nikita Kniazev

14.9. 4.9.5 版本

  • 改进了备选匹配错误消息,以包含更多上下文。— René Ferdinand Rivera Morell

  • 修复了在从另一个 use-project 包含的项目内部执行 use-project 时出现的错误。— René Ferdinand Rivera Morell

  • 支持 ARM64 上的本机 msvc 编译器。— Stephen Just

  • PCH 修复:修复了 msvc pch 包含目录;修复了 msvc pch 标头名称;修复了构建 pch 时缺少 gcc -ftemplate-depth。— Nikita Kniazev

  • 新增clang-win 在默认安装位置搜索编译器可执行文件,当在 PATH 中未找到时。— Nikita Kniazev

  • 修复了 clang-win 以支持版本化的 winsdk bin 位置。— Nikita Kniazev

14.10. 4.9.4 版本

  • 修复了某些平台/编译器上因 varargs 结束标记无效垃圾读取为 int 而不是 nullptr 而导致的崩溃。

  • 在 Windows 上时,不要为 GCC 强制使用 Windows 路径分隔符。因为它会混淆 Cygwin GCC 的相对包含路径处理。— René Ferdinand Rivera Morell

  • 为项目声明添加了 common-requirements,作为声明 requirementsusage-requirements 两者的简写。— René Ferdinand Rivera Morell

  • 添加到项目 explicit 规则中传递目标,以减少存在许多 explicit 目标时 explicit 目标的重复。— René Ferdinand Rivera Morell

  • 使覆盖率功能成为非偶然的和链接不兼容的。— Thomas Brown

  • 使用基于 PATH 的查找来查找 sh。对于 Gentoo Prefix 等情况,我们希望使用来自前缀的 Bourne shell,而不是来自主系统的可能较旧的版本。— David Seifert

14.11. 4.9.3 版本

  • 更新了近期 gcc 和 clang 的 23 和 26 版本的 cxxstd。(#184) — Andrey Semashev

14.12. 4.9.2 版本

  • 修复了 msvc 链接操作过长的问题。— René Ferdinand Rivera Morell

14.13. 4.9.1 版本

  • 修复了 b2 开发树中引导文件的初始仅开发路径的错误计算。— René Ferdinand Rivera Morell

  • 修复了在 boost-build 规则中指定的路径中加载引导文件的最终回退中的错误路径计算。— René Ferdinand Rivera Morell

14.14. 4.9.0 版本

此版本主要进行了内部清理和重组。最重要的是:修复了所有内存泄漏,使用 boost-build 规则自动构建系统启动,Jam Python 接口以及未维护的 Python 构建系统端口。

  • optimization 功能添加了 minimaldebug 选项。— René Ferdinand Rivera Morell

  • 添加了 Rocket Lake、Alder Lake、Sapphire Rapids 和 Zen 3 指令集。— Andrey Semashev

  • 删除了所有 on-exit 内存泄漏并修复了所有 ASAN 错误。— René Ferdinand Rivera Morell

  • 删除了使用 boost-build.jam 作为初始化配置文件的用法。— René Ferdinand Rivera Morell

  • 删除了不完整的构建系统端口和 Jam 引擎 Python 支持扩展。— René Ferdinand Rivera Morell

  • 修复了无法在使用 darwinclang 工具集在 macOS 上执行 arm+x86 组合构建的问题。— René Ferdinand Rivera Morell

  • 修复了无法在使用 clang 工具集在 macOS 上执行交叉编译的问题。— René Ferdinand Rivera Morell

  • 修复了在使用 gccclang 工具集将大量具有长名称的目标文件收集到静态库中时出现的错误。— René Ferdinand Rivera Morell

  • 修复了 build.sh 引擎构建脚本中 QCC 的检测。— René Ferdinand Rivera Morell

  • 修复了 intel-win 工具集缺少汇编标志的问题。— René Ferdinand Rivera Morell

  • 修复了 msvc 工具集的链接操作可能超出命令行长度限制的错误。— René Ferdinand Rivera Morell

  • 新增:向 FILE_OPEN 内置规则添加了 "t" 模式,该模式在评估时提供文件的内容。— René Ferdinand Rivera Morell

此版本删除了使用 boost-build.jamboost-build 规则进行初始化。仍然搜索和加载 boost-build.jam 以不破坏现有操作。但它被认为是已弃用的,将在未来的版本中删除。

14.15. 4.8.2 版本

  • 修复了由递归销毁和不正确的目标列表弹出取消链接引起的目标列表退出清理时崩溃的问题。— René Ferdinand Rivera Morell

14.16. 4.8.1 版本

  • 修复了由于缺少 EXIT_SUCCESSEXIT_FAILURE 宏而在 9.0 之前的旧 macOS/XCode 版本上构建引擎的问题。— René Ferdinand Rivera Morell

14.17. 4.8.0 版本

  • 新增: 添加了对 LoongArch 的支持。— Zhang Na

  • 更改引擎构建以使用静态 Intel 库(如果可用),而不是 C++ 运行时静态库,以修复静态 C++ 运行时不可用的系统。— Alain Miniussi

  • 重新排序了 msvc cflagscxxflags,并添加了 compileflags,以修复用户无法覆盖标志的问题。— Peter Dimov

  • 不要在 clang-linux 上引用 RPATH,以修复使用双引号以使使用 $ORIGIN 成为可能的问题。— Dimitry Andric

  • 修复了 kFreeBSD 上的 b2 可执行文件检测。— Laurent Bigonville

  • .ipp 扩展名添加到标头扫描和有效的 C++ 文件中。— Jim King

  • 修复了当源目标使用要求中包含 build=no 时缺少安装目标的问题。— Dmitry Arkhipov

  • cxxstd 功能添加了一些未来的 C++ 版本。— René Ferdinand Rivera Morell

  • 修复了引擎中的许多内存泄漏。— René Ferdinand Rivera Morell

  • abort/exit 调用更改为干净的异常处理,以便在引擎中进行内存清理。— René Ferdinand Rivera Morell

14.18. 4.7.2 版本

  • 修复了当 PATH 中没有 icpx 但 PATH 中有 icpc 时,配置 intel-linux 工具集时出现的错误。— Mark E. Hamilton

  • 由于 VS 2019 及更高版本支持 cxxstd=20,因此将其添加到 msvc 工具集中。— Peter Dimov

14.19. 4.7.1 版本

  • 修复了使用 clang-win 工具集链接的回归问题。— Peter Dimov

14.20. 4.7.0 版本

此版本包含许多、许多修复和内部清理。但也为 VS 2022 预览工具集添加了自动检测和引导。

  • 新增: 添加了 vc143,又名 VS2022,又名 cl.exe 17.x 工具集支持。包括构建引擎和自动检测预发布工具集。— Sergei Krivonos

  • 允许别名目标继续,即使使用要求中包含 <build>no。这允许组合可能包含可选目标(如测试)的别名目标。— Dmitry Arkhipov

  • 修复了 gcc 工具集中 JAMSHELL 的使用。— René Ferdinand Rivera Morell

  • 修复了编译 b2 引擎以使其在跨架构模拟上下文中运行时工作的问题。即在 QEMU 64 位主机中运行 arm 二进制文件时。— René Ferdinand Rivera Morell

  • 在 64 位主机上默认使用 64 位 MSVC。— Matt Chambers

  • 删除仅用于资源 DLL 的 /NOENTRY 选项,以允许正确链接。— gnaggnoyil

  • 修复了在 OpenBSD 上编译引擎时 unix 的重定义错误。— Brad Smith

  • 修复了在 iOS 和 AppleTV 上使用 clang 构建时出现额外的无法识别的编译器选项的问题。— Konstantin Ivlev

  • boost 支持模块添加了缺少的 Boost.JSON。— Dmitry Arkhipov

  • 在 clang-win 工具集中添加了 arm/arm64 目标支持。— Volo Zyko

  • 避免了 qt5 的线程模型警告。— psandana

  • 统一了 Clang 和 GCC PCH 创建。— Nikita Kniazev

  • 将 Objective-C 支持移至 GCC 工具集。— Nikita Kniazev

  • 支持 Xilinx ZYNQ 的 instruction-set 功能的值。— Thomas Brown

  • MIPS:添加了通用的 mips 架构。— YunQiang Su

  • 修复了 MSVC 编译器上的预处理。— Nikita Kniazev

14.21. 4.6.1 版本

  • 修复了使用 cygwin64 构建 b2 引擎的问题。— René Ferdinand Rivera Morell

  • 修复了从编译器 exec 检测 clang 工具集版本的问题。— Nikita Kniazev

14.22. 4.6.0 版本

此版本总结了一些新功能,这些功能使使用某些工具集更加容易(感谢 Nikita)。现在还可以在命令行上指定空标志功能,例如 cxxfalgs=,并忽略这些功能。这有助于缩短 CI 脚本,因为它们不需要专门处理这些情况。与往常一样,还有许多错误修复和调整。感谢为本版本做出贡献的每个人。

  • 新增: 允许通过在命令行上使用 toolset=clang-xx 将 clang 工具集自动配置为特定版本。— Nikita Kniazev

  • 新增: 在 gcc 和 msvc 工具集上自动按需包含 pch 标头,以镜像 clang 功能。— Nikita Kniazev

  • 新增: 当在命令行上指定的值为空时,标记为“free”和“optional”的功能现在将被忽略。因此,可以在命令行上指定 cxxflags= 而不会出错。— René Ferdinand Rivera Morell

  • 保留 bootstrap.sh 调用参数以转发到 build.sh 脚本。— tkoecker

  • 删除 buils.shlocal 的使用,以与某些功能不完全的 shell 兼容。— Tanzinul Islam

  • 解决了 busybox shell 中 build.sh 中的 shell 数组引用错误。— tkoecker

  • 检查是否需要 -pthread 才能在某些平台上使用 gcc 构建引擎。— tkoecker

  • 默认在 MacOS 上使用 clang。— Stéphan Kochen

  • 添加 /python//numpy 目标以用作依赖项,以传达版本特定的属性。— Peter Dimov

  • 在使用自定义 cxx 工具集构建引擎时,从环境变量 CXXCXXFLAGS 添加 cxx 和 cxxflags 的默认值。— Samuel DebionneRené Ferdinand Rivera Morell

  • 修复了仅当编译器可执行文件位于 PATH 中时检测 intel-linux 工具集安装的问题。— René Ferdinand Rivera Morell

  • 修复了对于没有获取可执行文件路径的本机方法(如 OpenBSD)的平台,b2 可执行文件路径确定问题。— René Ferdinand Rivera Morell

  • 修复了 property.find 错误消息。— Thomas Brown

14.23. 4.5.0 版本

一些小的修复以改进一些旧问题。

  • 重新启用生成器返回 property-set 作为第一项的功能。— Andrew McCann

  • 修复了示例以在成功时返回 0。— Mateusz Łoskot

  • 处理 config_toolset.bat 中 CXX 路径中的空格。

  • 修复了 Conan b2 生成器链接和 pkg-config 文档构建错误。— René Ferdinand Rivera Morell

14.24. 4.4.2 版本

此版本是 B2 在 Build Frameworks Group 的新家中的第一个版本。

  • 更改了文档和 boost.org 源代码中的引用,以指向等效的 bfgroup 资源。— René Ferdinand Rivera Morell

  • B2 网站和文档的新主题。— René Ferdinand Rivera Morell

14.25. 4.4.1 版本

小补丁,用于纠正 macOS 默认引擎编译器缺少修复的问题。

  • 修复了 macOS/Xcode 上引擎构建默认使用 gcc 而不是 clang 的问题。— René Ferdinand Rivera Morell

14.26. 4.4.0 版本

除了各种修复之外,此版本还为某些工具集引入了“动态”响应文件支持。这意味着在大多数情况下,如果工具集支持,则不会生成响应文件。而是扩展命令以直接包含选项。

  • 新增: 添加了 response-file 功能,以控制工具集操作中响应文件的使用类型。— René Ferdinand Rivera Morell

  • 新增:@() 扩展添加了 :O=value 变量修饰符。— René Ferdinand Rivera Morell

  • 新增: 在完全扩展变量引用之后,为前缀和后缀值添加了 :⇐value:>=value 变量修饰符。— René Ferdinand Rivera Morell

  • 新增: 在 clang-win 和 clang-darwin 上实现了 PCH。— Nikita Kniazev

  • 新增: 为 intel-linux 工具集添加了对 Intel oneAPI 版本的支持。— René Ferdinand Rivera Morell

  • 新增: 为 intel-windows 工具集添加了对 Intel oneAPI 版本的支持。— Edward Diener

  • 删除了单次链接限制。很久以前,这是一种性能调整,因为硬件和软件无法同时执行多个链接。常见的设置设备更好。— René Ferdinand Rivera Morell

  • 修复了在 AIX 上使用 GCC 构建引擎的问题。— René Ferdinand Rivera Morell

  • 支持将引擎构建为 32 位或 64 位寻址模型。— René Ferdinand Rivera Morell

  • GNU/Hurd 上构建 b2 引擎的基本支持。— Pino Toscano

  • 更新了“borland”工具集以使用 bcc32c 构建 B2。— Tanzinul Islam

  • 确保 Embarcadero 工具集名称仅为“embtc”。— Tanzinul Islam

  • 适应 Emscripten 2.0 存档默认行为的更改。— Basil Fierz

  • 修复了向后兼容性的引导路径。— René Ferdinand Rivera Morell

  • 向引导搜索添加了缺少的 BOOST_ROOT。— René Ferdinand Rivera Morell

  • 修复了 FreeBSD 上的引擎编译。— René Ferdinand Rivera Morell

  • 将 MSVC 默认为本机平台,并删除不明确的隐式地址模型 ARM/ARM64 值。— Nikita Kniazev

  • 修复了 b2 引擎构建的 MIPS32 检测。— Ivan Melnikov

  • 启用在 Windows 上使用 clang 构建 b2 引擎。— Gei0r

  • 修复了使用 Intel Linux icpc 构建 b2 引擎的问题。— Alain Miniussi

  • 重做了 build.sh 以修复许多错误并避免使用通用环境变量。— René Ferdinand Rivera Morell

  • 删除了配置检查的相关功能限制。— René Ferdinand Rivera Morell

  • 重新格式化了配置检查输出,以合理简洁的形式通知检查的变体。— René Ferdinand Rivera Morell

  • 支持在带有 Mingw 的 Windows Bash 上构建引擎。— René Ferdinand Rivera Morell

14.27. 4.3.0 版本

此版本包含许多单独的修复。非常感谢大家的贡献。特别感谢 Nikita 对 msvc 的许多改进以及对所有编译器中支持漏洞的总体修补。

Dmitry、Edward 和 Nikita 提供了一些值得注意的新功能

  • 新增: 添加了 force-include 功能,以在所有源文件之前包含标头。— Nikita Kniazev

  • 新增: 基于 clang-5 部分支持 Embarcadero C++ 编译器。— Edward Diener

  • 新增: 实现了使用功能的可配置安装前缀。— Dmitry Arkhipov

  • 新增: 添加了 translate-path 功能。translate-path 功能允许在每个目标的基础上使用提供的规则进行自定义路径处理。这可以用于支持自定义路径语法。— René Ferdinand Rivera Morell

  • 新增: 添加了便携式 B2 系统安装选项。这允许 b2 可执行文件和构建系统文件并排放置。因此可以(重新)定位到磁盘上的任何位置。很快将用于支持 Windows 和其他安装程序。这消除了 bootstrap 对 boost-build.jam 文件的需求。使用户更容易上手。— René Ferdinand Rivera Morell

  • 取消了从 VS Preview 命令提示符构建的破坏。— Marcel Raad

  • 修复了 macOS darwin 工具集上的编译器版本检查。— Bo Anderson

  • 删除了 GCC 上 PCH 目标命名限制。— Nikita Kniazev

  • 选择了适当的 QNX 目标平台。— Alexander Karzhenkov

  • 对 Windows 上 b2 引擎构建的各种空间和性能改进。— Nikita Kniazev

  • 为每个编译器填充了额外的和吹毛求疵的警告选项。— Nikita Kniazev

  • 包含引擎 IO 失败的操作系统错误原因。— Nikita Kniazev

  • 使用 /Zc:inline 和 /Zc:throwingNew 标志以获得更好的语言一致性。— Nikita Kniazev

  • 为 C++20 添加了 cxxstd 值 20。— Andrey Semashev

  • MSVC 上的并行 B2 引擎编译。— Nikita Kniazev

  • 更新了 instruction-set 功能以及新的 x86 目标。— Andrey Semashev

  • 将 /nologo 传递给 Windows 编译器上的 rc。— Nikita Kniazev

  • 修复了条件属性中的否定。— Nikita Kniazev

  • 删除了剩余的清单生成过早退出。— Nikita Kniazev

  • 修复了时间戳增量计算。— Nikita Kniazev

  • 向 clang-win.jam 添加了缺少的汇编程序选项,以启用 Context 构建。— Peter Dimov

  • :BS 示例更新了稀缺的 :chars 文档。— Nikita Kniazev

  • 修复了在 linux 上静态链接 boost-python 的问题。— Joris Carrier

  • 正在清理引擎构建警告。— René Ferdinand Rivera Morell

  • 允许自测使用响应文件的工具集。— René Ferdinand Rivera Morell

  • Jambase 移植到原生 C++。因此移除了原始 Jam 引导过程中最古老的部分之一。— René Ferdinand Rivera Morell

14.28. 4.2.0 版本

此版本主要进行引擎的次要修复和清理。 特别是 bootstrap/build 过程现在清楚地传达了 C++11 的要求。

  • 添加 saxonhe_dir 操作。— Richard Hodges

  • 为 Windows MSVC 上的历史 Boost 版本添加 CI 测试。— René Ferdinand Rivera Morell

  • 在构建引擎时检查 C++11 支持。 包括关于此事实的信息性错误消息。— René Ferdinand Rivera Morell

  • 使用最新的 bison 版本更新 Jam 语法解析器。— René Ferdinand Rivera Morell

  • 允许即使 bison 语法生成器不可用,root b2 b2 引擎构建也能工作。— René Ferdinand Rivera Morell

  • 至少在 Windows、macOS 和 Linux 上实现无警告的引擎构建。— René Ferdinand Rivera Morell

  • 清理 Windows 引擎构建,使其一致地使用 ANSI Win32 API。— Mateusz Loskot

  • 修复 b2 引擎在检测到 Jam 语言错误时未提前退出并报错的问题。— Mateusz Loskot

  • 打印本地模块(即当前目录)的帮助信息。— Thomas Brown

14.29. 4.1.0 版本

此版本包含许多小的错误修复。 但也有一些新功能。 现在有一个 lto 功能来指定 LTO 的使用及其类型。 现有的 stdlib 功能现在具有真实的值和一些工具集的相应选项。 但最重要的是,所有功能都有了新的文档。

感谢所有通过这些更改为本版本做出贡献的用户

  • 支持 intel-vin 19.0 的 VS2019。— Edward Diener

  • 修复在 Cygwin 上构建 b2 时关于 -std=gnu11 的编译器警告。— Andrey Semashev

  • 添加为单个头文件创建多个 PCH 的示例。— René Ferdinand Rivera Morell

  • 为 GCC 工具集添加 QNX 线程标志。— Aurelien Chartier

  • 修复构建 b2 引擎时 IBM 和 Sun 编译器的版本选项 — Juan Alday

  • b2 引擎中将 strings.h 重命名为 jam_strings.h,以避免与 POSIX strings.h 头文件冲突。— Andrey Semashev

  • 为 IBM 编译器的 cxxstd 功能添加选项。— Edward Diener

  • 修复了 intel-win 工具集的许多问题。— Edwad Diener

  • 为基于 gcc 的工具集添加 z15 指令集。— Neale Ferguson

  • 改进从 Cygwin shell 中使用 MSVC。— Michael Haubenwallner

  • 为 gcc 和 clang 工具集添加 LTO 功能和相应的支持。— Dmitry Arkhipov

  • 修复源文件没有类型时发生的错误。— Peter Dimov

  • 为功能添加文档。— Dmitry Arkhipov

  • 增强 clang、gcc 和 sun 工具集的 stdlib 功能以及相应的文档。— Dmitry Arkhipov

  • Install 规则现在仅显式地使其创建的直接目标生效。— Dmitry Arkhipov

  • 为 msvc 工具集添加 armasm (32 位和 64 位) 支持。— Michał Janiszewski

  • 修复自定义的未版本控制的 gcc 工具集规范的错误。— Peter Dimov

  • 允许在 gcc 工具集规范中覆盖 arflags。— hyc

  • 修复找到的库未使其进入 clang-win 链接命令行的问题。— Peter Dimov

  • 更新了 intel-win 工具集,以支持 Intel C++ 19.1。— Edward Diener

  • 在 b2 引擎中检测 MIPS32 和 MIPS64 的操作系统差异。— YunQiang Su

14.30. 4.0.1 版本

此补丁版本修复了一个小问题,该问题发生在尝试配置使用非版本标签覆盖工具集版本时。 目前已知这仅在以下情况下成为问题: (a) 将工具集版本配置为类似 “tot” 的内容 (b) 在 Boost 1.72.0 中,当它创建 cmake 安装工件时。 此问题的修复由 Peter Dimov 提供。

14.31. 4.0.0 版本

经过更多年的开发,构建系统的格局发生了很大变化,编译器的情况也是如此。 此版本标志着 B2 开始过渡到 C++ 实现。 最初,这意味着引擎将作为 C++ 源代码编译,但该源代码仍然是基本的 C 实现。 随着时间的推移,它将在引擎和构建系统中转换为 C++ 代码库。 此启动版本中的一些更改

  • 需要 C++ 11 才能构建引擎。

  • 简化的构建脚本使其更易于维护。

  • 使用 C++ 优化进行构建可立即提高性能。

此版本中的其他更改

  • 添加对使用预构建的 OpenSSL 的支持。— Damian Jarek

  • 定义 riscv 架构功能。— Andreas Schwab

  • 为 MSVC 添加 ARM64 作为有效架构。— Marc Sweetgall

  • 为 gcc 和 clang 设置来自 coverage 功能的覆盖率标志。— Damian Jarek

  • 在 gcc/clang 中添加 s390x CPU 和支持。— Neale Ferguson

  • 支持导入 pkg-config 包。— Dmitry Arkhipov

  • 支持 leak sanitizer。— Damian Jarek

  • 修复 clang-win 中缺少的 /manifest 选项,以修复名称中带有 “update” 的 exes 的管理员权限提升问题。— Peter Dimov

  • freertos 添加到 os 功能。— Thomas Brown

  • 默认并行作业 (-jX) 为可用的 CPU 线程数。— René Ferdinand Rivera Morell

  • 更简单的 coverage 功能。— Hans Dembinski

  • 更好的 sanitizers 堆栈。— James E. King III

此版本中,默认并行作业数已从 “1” 更改为核心数。 在某些情况下,该默认值可能大于分配的 cpu 资源,例如在某些虚拟化容器安装中。

附录 A:许可证

除了 B2 根据 Boost 软件许可证 - 1.0 版本获得许可外,B2 还使用了其他许可不同的库,如下所示。

A.1. MIT 许可证

MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
用于现代 C++ 的 JSON

版权所有 (c) 2013-2022 Niels Lohmann


1. 请参阅名为 “功能属性” 的章节
2. 许多功能将在子项目中被覆盖,而不是添加到子项目中。 有关更多信息,请参阅名为 “功能属性” 的章节
3. 请参阅 名为 “功能属性” 的章节中 “free” 的定义。
4. 此名称是历史遗留的,最终将更改为 metatarget
5. 这种先创建后注册的模式是由 Boost.Jam 语言的限制引起的。 Python 端口可能永远不会创建重复的目标。