Boost C++ 库

……世界上最受推崇和设计精良的 C++ 库项目之一。 Herb SutterAndrei AlexandrescuC++ 编码规范

1. 简介

想了解 B2 的功能?从 教程 开始,然后阅读 概述。准备好实际尝试 B2 时,请转到 安装 部分。

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

在您的项目中设置 B2?请查看 概述扩展程序手册

如果您发现此文档中任何内容不清楚,请直接在 问题跟踪器 中报告问题。对于更普遍的问题,请将其发布到我们的讨论论坛 (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. 安装

要安装来自官方发布版本的 B2(可在 GitHub 上获得),请按照以下步骤操作

  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 的最基本功能。我们将从“Hello, world”示例开始,学习如何使用库,并以测试和安装功能结束。

3.1. Hello, world

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,如 “项目层次结构”部分 所述)派生的属性的组合。例如,`#include` 的头文件的位置通常不是在命令行上指定的,而是在 Jamfile 中作为 *目标需求* 来描述,并自动与这些目标的构建请求组合。多线程编译是目标需求的另一个示例。下面的 Jamfile 片段说明了如何指定这些需求。

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

构建 hello 时,上面指定的两个需求将始终存在。如果在 b2 命令行上给出的构建请求明确地与目标的需求相矛盾,则目标的需求通常会覆盖(或者,对于像 <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/app/中的项目在top/中的项目构建时被构建。但是,top/util/foo/中的目标只有在top/top/app/中的目标需要它们时才会被构建。

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路径添加到app需求中作为<include>特性的值,但这项工作将对所有使用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. 静态库和共享库

库可以是静态的,这意味着它们包含在使用它们的执行文件中,也可以是共享的(又名动态的),它们只从执行文件中引用,并且必须在运行时可用。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 ;

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

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
    ;

在上面的示例中,每当network使用<link>shared构建时,<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创建可执行文件所需的 target。根据配置的属性,可以使用不同的命令行。然而,add_program 虽然是更高级别的,但仍然是比较薄弱的层次。所有 target 都会在解析构建描述时立即创建,这使得执行多变体构建成为不可能。通常,任何构建属性的更改都需要完全重新配置构建树。

为了支持真正的多变体构建,B2 引入了元目标定义主要目标元目标metatarget的概念——一个在解析构建描述时创建的对象,稍后可以结合特定的构建属性来生成实际的目标。

考虑一个例子:

exe a : a.cpp ;

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

b2 toolset=gcc toolset=msvc

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

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

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

有关需求和概念的更深入讨论,您可以参考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的命名操作。大括号内的文本是要调用的命令。$(<)变量将展开为生成的FileList,$(>)变量将展开为源FileList。

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

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编译器可用。

如果tool支持此用法,则可以在需要tool的Jamfile中放置using <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 特征的值传递给两者。例如,要配置一个始终生成 64 位代码的gcc工具集,你可以编写:

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
尽管用于指定工具集选项的语法与用于在 Jamfile 中指定需求的语法非常相似,但工具集选项与特性并不相同。不要尝试在工具集初始化中指定特性值。

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 使用在同一项目中定义的库。第二个使用 Jamfile 上一级定义的某个目标(很可能是一个库)。最后,第三个目标使用一个C++ Boost库,使用其绝对符号名称引用它。有关目标引用的更多信息,请参见名为“依赖目标”的部分名为“目标标识符和引用”的部分

4.5.3. 需求

需求是在构建目标时始终存在的属性。通常,它们是包含项和定义

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 ;

此语法是忽略父项中的自由属性(例如定义)的唯一方法。它也适用于普通属性。考虑以下示例

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 ;

除非用户明确请求单线程版本,否则将构建多线程目标。需求和默认构建之间的区别在于,需求无法以任何方式被覆盖。

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 ;

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

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 使用“生成器”——对应于编译器和链接器等工具的对象。每个生成器都声明它可以生成的 target 类型以及它需要的源代码类型。使用此信息,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库的所有目标的编译器包含路径中。此功能极大地简化了Jamfiles。

5.3. 别名

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

alias core : im reader writer ;

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

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

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

并且只在您的Jamfiles中使用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支持自动或手动加载包管理器生成的构建文件。例如,使用生成conanbuildinfo.jam文件的Conan包管理器,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中的搜索路径指定了一个键值对列表,包含项目ID路径。该键值对列表的各个部分,顾名思义,是一个路径分隔符分隔的列表。例如,如果我们要查找这两个项目:/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

搜索路径规范中的项目ID将该项目根目录映射到指定的路径。B2将使用该路径来搜索具有该项目ID根目录的任何项目和子项目。

5.11.2. 搜索过程

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

对于形式为/d1/d2/../dn的给定项目ID,我们将按以下顺序搜索:

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

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

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

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

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

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

  7. 以及在为/根目录注册的任何路径中的d1_d2_.._dn.jam jamfile。

例如,使用以下搜索路径:

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

  • //usr/share/b2/external

并给定要解析的/boost/core项目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将分配给项目ID。或者加载找到的第一个*.jam文件。

5.11.3. 加载过程

项目jamfile或*.jam文件决定了项目的加载方式。

使用项目ID路径加载项目jamfile相当于从具有引用的项目的上下文中调用use-project project-id : path ;

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

5.12. 命令数据库和IDE集成

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

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

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

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

  • 它禁用所有操作执行输出。即如同指定-d0选项。

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

目前,json是作为遵循Clang JSON Compilation Database Format Specification的格式支持的。

--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 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 函数接受单个参数——一个元目标名称列表。由指定元目标生成的 target 将始终被视为过期。考虑以下示例

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,如果从 helper 目录调用 **b2**,则变量 DATA 将设置为 data/a.txt

build-project

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

test-suite

此规则已弃用,等效于 alias

import-search
Jam

规则 import-search ( reference )

将给定的 reference 路径添加到 import 将搜索的目录集。reference 可以是普通目录或已知的项目路径。如果给定项目路径,则将对其进行搜索并解析,以在引用中包含任何子项目路径。如果给定目录,则它将相对于当前项目位置进行根目录设置。项目路径使用示例

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

6.2.1. b2::require_b2

Jam

规则 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

允许的值:3264

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

address-sanitizer

允许的值:onnorecover

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

allow

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

architecture

允许的值:x86ia64sparcpowermipsmips1mips2mips3mips4mips32mips32r2mips64pariscarms390xloongarch

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

archiveflags

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

asmflags

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

asynch-exceptions

允许的值:offon

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

build

允许的值:no

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

cflagscxxflagslinkflags

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

compileflags

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

conditional

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

@rulename

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

rule rulename ( properties * )

将为每个目标及其属性调用该规则,并应返回任何附加属性。有关示例,另请参见 要求 部分。

coverage

允许的值:offon

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

cxxflags

参见 <cflags>

cxxstd

允许的值98030x111y141z172a202b232c26latest

指定要使用哪个版本的 C++ 标准语言进行构建。自“98”以来,所有正式版本的标准都包含在内。也可以指定使用实验性的、正在进行中的 latest 版本。一些编译器为最终发布的标准版本之前的实验版本指定了中间版本。这些版本按照 GNU 命名法包含在内,如 0x1y1z2a2b2c。根据编译器,latest 将映射到其中一个。

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

的子特性 cxxstd

允许的值isognums

指示是否应使用非标准方言。这些通常具有或非扩展或平台特定功能。不指定方言将默认为“iso”,这将尽最大努力尝试使用 ISO C++ 标准一致性。

c++abi

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

c++-template-depth

允许的值:任何正整数。

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

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

允许的值:onoff

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

define

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

def-file

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

dependency

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

dll-path(仅限 *Unix*)

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

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

请注意,相对路径将在其前面加上相关jam文件(在b2命令行中提供)的目录,这会严重限制其实际用途!

有关详细信息,请参阅有关dll-pathhardcode-dll-paths的常见问题解答条目,并参阅hardcode-dll-paths功能来自动添加开发路径。

embed-manifest

允许的值:onoff

此功能特定于msvc工具集(参见Microsoft Visual C++),并控制清单文件是嵌入到可执行文件和共享库中,还是放置在其旁边。此功能对应于在项目设置对话框中找到的IDE选项,位于“配置属性”→“清单工具”→“输入和输出”→“嵌入清单”下。

embed-manifest-file

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

embed-manifest-via

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

exception-handling

允许的值:onoff

禁用异常。

extern-c-nothrow

允许的值:offon

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

fflags

编译Fortran源代码时,此功能的值将不经修改地传递给工具。

file

在预构建库目标的要求中使用时,此功能指定库文件的路径。有关示例,请参见预构建目标

find-shared-library

添加要链接的共享库。通常情况下,应优先使用lib目标而不是此功能。

find-static-library

添加要链接的静态库。通常情况下,应优先使用lib目标而不是此功能。

flags

此功能用于工具的通用(即非特定于语言的)标志。此功能的值将不经修改地传递给将构建目标的工具。

hardcode-dll-paths(仅限Unix

允许的值:truefalse
默认为:true(exe)、false(install)
忽略:lib

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

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

此功能的目的是为了辅助开发;默认情况下,生成的执行文件(exe - 但不是install目标)可以运行,无需更改系统共享库路径或将库安装到系统路径。
有关详细信息,请参阅常见问题解答条目

implicit-dependency

指示此功能的值命名的目标可能会生成被声明的目标的源代码包含的文件。有关更多信息,请参见生成的标头部分。

force-include

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

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

include

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

inlining

允许的值:offonfull

启用内联。

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

允许的值:onnorecover

启用内存泄漏检测器。值norecover禁用检测器的恢复功能。此功能是可选的,因此默认情况下不启用任何检测器。

linemarkers

允许的值:off

在预处理目标上,更改行为以发出/省略类似于#line#linenum的行指令。

注意:该值不会传播。

link

允许的值:sharedstatic

控制库的构建方式。

linkflags

参见 <cflags>

local-visibility

允许的值:globalprotectedhidden

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

local-visibility功能支持与visibility功能相同的值,含义也相同。默认情况下,如果目标未指定local-visibility,则使用visibility功能的值。

location

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

location-prefix

将目标的构建目录设置为项目构建目录,并在其前面加上此功能的值。有关示例,请参见目标路径部分。

mflags

编译Objective C源代码时,此功能的值将不经修改地传递给工具。

mmflags

编译Objective C++源代码时,此功能的值将不经修改地传递给工具。

name

在预构建库目标的要求中使用时,此功能指定库的名称(库文件的名称,不包含任何特定于平台的后缀或前缀)。有关示例,请参见预构建目标

<install>目标的要求中使用时,它指定目标文件的名称。

optimization

允许的值:offspeedspace、'minimal'、'debug'。

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

profiling

允许的值:offon

启用生成额外代码以写入概要信息。

relevant

允许的值:任何功能的名称。

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

  • 如果满足以下任何条件,则该功能将被视为相关

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

    • 它由生成器的需求使用

    • 它是相关功能的子功能

    • 它具有相关的子功能

    • 它是复合功能,并且任何组合功能都是相关的

    • 它影响主目标的目标替代方案的选择

    • 它是一个传播特性,与任何依赖项相关

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

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

    • 它被明确命名为相关

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

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

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

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

rtti

允许的值:onoff

禁用运行时类型信息。

runtime-debugging

允许的值:onoff

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

runtime-link

允许的值:sharedstatic

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

search

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

source

<source>X属性对构建目标的影响与将X放入源列表中的影响相同。当您想将相同的源添加到项目中的所有目标时(您可以将<source>放入需求中)或有条件地包含源(使用条件需求,请参见条件和替代方案部分)时,这很有用。另请参见<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

允许的值:nativegnugnu11libc++sun-stlportapache

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

native

使用编译器的默认值。

gnu

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

gnu11

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

libc++

使用LLVM libc++。

sun-stlport

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

apache

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

strip

允许的值:offon

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

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

禁止链接器创建导入库。

tag

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

@rulename

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

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

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

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

target-os

允许的值:aixandroidappletvbsdcygwindarwinfreebsdhaikuhpuxiphonelinuxnetbsdopenbsdosfqnxqnxntosgisolarisunixunixwarewindowsvmsvxworksfreertos

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

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

threadapi

允许的值:pthreadwin32

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

threading

允许的值:singlemulti

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

thread-sanitizer

允许的值:onnorecover

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

toolset

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

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

undef

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

undefined-sanitizer

允许的值:onnorecover

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

use

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

user-interface

允许的值:consoleguiwincenativeauto

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

console

控制台应用程序。

gui

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

wince

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

native

应用程序在没有子系统环境的情况下运行。

auto

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

variant

允许的值:debugreleaseprofile

一个组合了几个低级特性的特性,可以轻松请求常见的构建配置。

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

允许的值:offonfull

启用矢量化。

version

此特性未被任何内置工具使用,但可以用于例如通过<tag>特性调整目标的名称。

visibility

允许的值:globalprotectedhidden

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

支持的值具有以下含义

global

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

protected

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

hidden

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

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

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

允许值:onallextrapedanticoff

控制编译器的警告级别。

on

启用默认/“合理”警告级别。

all

启用大多数警告。

extra

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

pedantic

启用可能无关紧要且冲突的警告。

off

禁用所有警告。

默认值为all

warnings-as-errors

允许的值:offon

可以将警告视为错误,并在出现警告时中止编译。

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的子特性

允许值:fullthinfat

指定要使用的 LTO 类型。

full

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

thin

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

fat

生成 gcc 的 fat LTO 对象:编译文件同时包含适合 LTO 的中间语言和适合常规链接的对象代码。

response-file

允许值:autofilecontents

控制在适用目标的构建期间是否使用响应文件。对于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-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-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-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-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-value`语法提供以下选项

cflags

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

cxxflags

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

compileflags

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

linkflags

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

Embarcadero C++ 编译器

embarcadero模块支持 32 位命令行 C 编译器 bcc32x 和 64 位命令行 C 编译器 bcc64(均基于 clang),它们都在 Microsoft Windows 上运行。这些是所有 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 编译器。如果指定了``32 的编译器选项,则默认编译器将为 bcc32x。在这两种情况下,如果未给出命令,Boost Build 都将假定编译器位于 PATH 中。因此,如果您接受默认编译器并且 Embarcadero C 二进制目录位于 PATH 中,则不必指定命令。

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

compiler options:

可以使用`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上的GNU C++ 编译器,包括SunOS在内的一些类Unix系统以及Windows(CygwinMinGW)。

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

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

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

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

可以使用`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

指定资源编译器的类型。该值可以是windres(msvc资源编译器)或rc(Borland资源编译器)。

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

Tru64 Unix 的 HP C++ 编译器

hp_cxx模块支持适用于Tru64 Unix的HP C++编译器

使用以下语法初始化模块

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

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

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

可以使用`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-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-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

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

mc-compiler

编译Microsoft消息目录文件的命令。如果未指定,将使用mc。在执行安装脚本并调整PATH变量后,将调用该命令。

resource-compiler

编译资源文件的命令。如果未指定,将使用rc。在执行安装脚本并调整PATH变量后,将调用该命令。

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目标

即使在64位Windows上,也可以始终使用32位主机编译器。相反,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 Store和Phone生成二进制文件。要指定要定位的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 Store 8.1构建

.\b2 toolset=msvc-12.0 windows-api=store architecture=arm

请注意,当以Windows Phone 8.1为目标时,12.0版本不包含vcvars phone设置脚本。可以从这里单独下载。

Sun Studio

sun模块支持适用于Solaris操作系统的Sun Studio C++编译器。

使用以下语法初始化模块

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

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

如果未指定命令,B2将在/opt/SUNWspro/bin和PATH中搜索名为CC的二进制文件。

当在此编译器上使用复杂的C代码(例如[Boost C库](https://boost.ac.cn))时,建议在初始化`sun`模块时指定以下选项。

-library=stlport4 -features=tmplife -features=tmplrefstatic

详情请参见Sun C++前端技巧

可以使用`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平台上使用该库。由于每个STLport版本的库命名不同,Linux不受官方支持。

在使用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

在真实文件名扩展名之前附加到扩展模块名称的字符串。通常,我们只需根据``功能的值来计算此值。但是,ubuntu的`python-dbg`包使用windows约定,即在调试构建扩展模块中附加_d。我们无法检测到ubuntu,也无法探测python的“_d”要求,如果您使用`--with-pydebug`配置和构建python,您将使用标准的*nix约定。默认为""(或在目标为windows且``已设置时为"_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-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-value`语法提供以下选项

format

允许的值:`html`、`xhtml`、`htmlhelp`、`onehtml`、`man`、`pdf`、`ps`、`docbook`、`fo`、`tests`。

`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-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-home`或`java`来指定查找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

规则 binding ( module_name )

C++

value_ref binding(std::string module_name);

返回给定模块的文件系统绑定。

例如,模块可以使用以下方法获取自身位置:

me = [ modules.binding $(__name__) ] ;
b2::modules::record_binding
Jam

规则 record-binding ( module_name : binding )

C++

void record_binding(std::string module_name, value_ref value);

此辅助函数由 load 用于记录每个已加载模块的绑定(路径)。

module_name 将被忽略。取而代之的是使用当前正在加载模块的内部跟踪来记录绑定。
b2::modules::poke
Jam

规则 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

规则 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

规则 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

规则 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

规则 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

规则 run-tests ( m )

C++

void run_tests(value_ref m, bind::context_ref_ context_ref);

运行指定模块的内部 B2 单元测试。模块的test规则在其自己的模块中执行,以消除测试模块依赖项(如 assert)对模块本身的任何意外影响。

b2::modules::load
Jam

规则 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

要在其中搜索 filename 的目录。默认为 $(BOOST_BUILD_PATH)

b2::modules::import
Jam

规则 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

规则 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

规则 bases ( class )

C++

list_ref bases(std::string class_name);

返回给定类的基类。

b2::class::is_derived
Jam

规则 is-derived ( class : bases + )

C++

bool is_derived(value_ref class_name, list_cref class_bases);

当给定类是从给定基类派生时返回 true。

b2::class::is_instance
Jam

规则 is-instance ( value )

C++

bool is_instance(std::string value);

如果给定值是任何类的实例,则返回 true。

b2::class::is_a
Jam

规则 is-a ( instance : type )

C++

bool is_a(std::string instance, value_ref type);

如果给定实例是给定类型,则返回 true。

6.5.3. errors 模块。

b2::jam::errors::backtrace
Jam

规则 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

规则 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

规则 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

规则 try ( )

C++

void error_try();

开始查找错误消息。

Jam

规则 catch ( messages * : * )

C++

void error_catch(const lists & rest, bind::context_ref_ context_ref);

停止查找错误消息;如果在错误调用中的相应参数中找不到 messages 的参数,则生成错误。

b2::jam::errors::error
Jam

规则 error ( messages * : * )

C++

void error(const lists & rest, bind::context_ref_ context_ref);

打印带有堆栈回溯的错误消息并退出。

b2::jam::errors::user_error
Jam

规则 user-error ( messages * : * )

C++

void user_error(const lists & rest, bind::context_ref_ context_ref);

与“error”相同,但生成的回溯将仅包含用户文件。

b2::jam::errors::warning
Jam

规则 warning ( messages * : * )

C++

void warning(const lists & rest, bind::context_ref_ context_ref);

打印带有堆栈回溯的警告消息并继续执行。

b2::jam::errors::lol_to_list
Jam

规则 lol→list ( * )

C++

list_ref lol_to_list(const lists & rest);

将任意参数列表转换为具有“:”分隔符和表示相同信息的带引号元素的列表。这主要用于格式化报告错误时调用的规则的参数的描述。

b2::jam::errors::nearest_user_location
Jam

规则 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

规则 split ( string separator )

C++

b2::list_ref rb2::egex_split(const std::tuple<b2::value_ref, b2::value_ref> & string_separator);

返回以下子字符串的列表:

  1. 从开头到“分隔符”的第一次出现或到结尾。

  2. 在“分隔符”的每次出现和下一次出现之间。

  3. 从“分隔符”的最后一次出现到结尾。

如果没有分隔符,结果将只包含一个元素。

b2::regex_split_each
Jam

规则 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

规则 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

规则 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

规则 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

规则 replace ( 字符串 match 替换字符串 )

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

规则 replace-list ( 列表 * : match : 替换字符串 )

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

规则 grep ( 目录 + : 文件 + : 模式 + : 结果表达式 * : 选项 * )

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

规则 add ( 元素 * )

C++

void b2::set::add(b2::list_cref elements);, void b2::set::add(const b2::set & elements);

elements添加到集合中。

b2::set::contains
Jam

规则 contains ( 元素 )

C++

bool b2::set::contains(b2::value_ref element) const;

集合是否包含给定的element

b2::set::to_list
Jam

规则 list ( )

C++

b2::list_ref b2::set::to_list() const;

返回包含集合所有元素的列表。

b2::set::difference
Jam

规则 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

规则 intersection ( set1 * : set2 * )

C++

static b2::list_ref b2::set::intersection(b2::list_cref set1, b2::list_cref set2);

移除同时出现在set1set2中的所有项。

b2::set::equal
Jam

规则 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

规则 whitespace ( )

C++

b2::value_ref string_whitespace();

返回规范的空格字符集,作为一个单一字符串。

b2::string_chars
Jam

规则 chars ( 字符串 )

C++

b2::list_ref string_chars(b2::value_ref s);

将给定的string拆分为一个字符串列表,该列表由string中每个字符按顺序组成。

b2::string_abbreviate
Jam

规则 abbreviate ( 字符串 )

C++

b2::value_ref string_abbreviate(b2::value_ref s);

对字符串应用一组标准转换,以生成长度不超过 5 个字符的缩写。

b2::string_join
Jam

规则 join ( 字符串 * : 分隔符 ? )

C++

b2::value_ref string_join(b2::list_cref strings, b2::value_ref separator);

连接给定的strings,在每个字符串之间插入给定的separator

b2::string_words
Jam

规则 words ( 字符串 : 空格 * )

C++

b2::list_ref string_words(std::string s, b2::list_cref whitespace);

将字符串拆分为空格分隔的单词。

b2::string_is_whitespace
Jam

规则 is-whitespace ( 字符串 ? )

C++

bool string_is_whitespace(b2::value_ref s);

检查给定的string是否完全由空格组成。

6.5.7. version 模块。

b2::version_less
Jam

规则 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

规则 emplace ( 键 + : 值 )

C++

void emplace(list_cref k, value_ref v);

设置或添加路径key处具有value的元素。路径可以包含两种位置项:数组索引或对象成员。数组索引是"[]" n。其中"[]"表示数组中从零开始的索引,n紧随其后。其他任何内容都将被视为成员字段名称。

b2::property_db::write_file
Jam

规则 write-file ( 文件名 : 格式 ? )

C++

void write_file(value_ref filename, value_ref format);

将数据的表示形式写入给定filename文件,格式为format。支持的格式:JSON

b2::property_db::dump
Jam

规则 dump ( 格式 ? )

C++

std::string dump(value_ref format);

将数据的表示形式作为格式为format的字符串写入。支持的格式:JSON

6.5.9. path

执行各种路径操作。路径始终采用“规范化”表示形式。其中,路径可以是

  • '.',或者

  • ['/'] [ ( '..' '/' )* (token '/')* token ]

简单来说,路径可以是根路径,'..'元素只允许出现在开头,并且它永远不会以斜杠结尾,除非路径只包含斜杠。

  1. 规则 make ( 原生路径 )

    将原生路径转换为规范化形式。

  2. 规则 native ( 路径 )

    构建路径的原生表示形式。

  3. 规则 is-rooted ( 路径 )

    测试路径是否为根路径。

  4. 规则 has-parent ( 路径 )

    测试路径是否具有父路径。

  5. 规则 basename ( 路径 )

    返回路径,不包含任何目录组件。

  6. 规则 parent ( 路径 )

    返回路径的父目录。如果不存在父目录,则发出错误。

  7. 规则 reverse ( 路径 )

    返回path2,使得[ join path path2 ] = "."。路径可能不包含".."元素或根路径。

  8. 规则 join ( 元素 + )

    连接传递的路径元素。如果除第一个元素之外的任何元素都是根路径,则会生成错误。跳过任何空或未定义的路径元素。

  9. 规则 root ( 路径 根路径 )

    如果path是相对路径,则将其根路径设置为root。否则,它保持不变。

  10. 规则 pwd ( )

    返回当前工作目录。

  11. 规则 glob ( 目录 * : 模式 + : 排除模式 * )

    返回指定目录中与给定模式匹配的文件列表。目录和模式都作为可移植路径提供。每个模式都应该是非绝对路径,并且不能包含“.”或“..”元素。模式的每个斜杠分隔的元素可以包含以下特殊字符

    • ’?’ 匹配任何字符

    • ’*’ 匹配任意数量的字符

      文件$(d)/e1/e2/e3(其中“d”位于$(dirs)中)与模式p1/p2/p3匹配当且仅当e1匹配p1,e2匹配p2等等。例如

      [ glob . : *.cpp ]
      [ glob . : */build/Jamfile ]
  12. 规则 glob-tree ( 根路径 * : 模式 + : 排除模式 * )

    glob 的递归版本。在构建文件的 glob 的同时,还在给定根路径的子目录中进行搜索。一组可选的排除模式将从结果中过滤掉匹配的条目。排除也适用于子目录扫描,因此匹配排除模式的目录将不会被搜索。

  13. 规则 exists ( 文件 )

    如果指定的文件存在,则返回 true。

  14. 规则 all-parents ( 路径 : 上限 ? : cwd ? )

    找出路径的绝对名称并返回所有父路径的列表,从直接父路径开始。父路径作为相对名称返回。如果指定了upper_limit,则其上方的目录将被剪枝。

  15. 规则 glob-in-parents ( 目录 : 模式 + : 上限 ? )

    dir的父目录中搜索patterns,直到并包括upper_limit(如果已指定),否则直到文件系统根目录。

  16. 规则 relative ( 子路径 父路径 : 无错误 ? )

    假设childparent的子目录,则返回从parentchild的相对路径。

  17. 规则 relative-to ( path1 path2 )

    返回相对于path1的path2的最小路径。

  18. 规则 programs-path ( )

    返回操作系统用于查找程序的路径列表。

  19. 规则 makedirs ( 路径 )

    创建目录以及尚不存在的所有父目录。

6.5.10. sequence

各种有用的列表函数。请注意,此模块中的算法主要在调用方的模块命名空间中执行,以便可以使用局部规则作为函数对象。还要注意,大多数谓词可以是多元素列表。在这种情况下,除了第一个元素之外的所有元素都将预先添加到作为第一个参数传递给由第一个元素命名的规则的元素。

  1. 规则 filter ( predicate + : sequence * )

    返回$(sequence)中的元素e,其中[ $(predicate) e ]的值非空。

  2. 规则 transform ( function + : sequence * )

    返回一个新的序列,该序列由$(sequence)中每个元素e[ $(function) $(e) ]组成。

  3. 规则 reverse ( s * )

    返回s中的元素,顺序相反。

  4. 规则 insertion-sort ( s * : ordered * )

    使用二元谓词ordereds进行插入排序。

  5. 规则 merge ( s1 * : s2 * : ordered * )

    使用二元谓词ordered合并两个有序序列。

  6. 规则 join ( s * : joint ? )

    s中的元素连接成一个长字符串。如果提供joint,则将其用作分隔符。

  7. 规则 length ( s * )

    查找任何序列的长度。

  8. 规则 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;否则返回 false。

  4. 规则 validate ( type )

    如果type未知,则发出错误。

  5. 规则 set-scanner ( type : scanner )

    设置将为此类型使用的扫描器类。

  6. 规则 get-scanner ( type : property-set )

    返回适合于typeproperty-set的扫描器实例。

  7. 规则 base ( type )

    返回给定类型的基类型,如果给定类型未派生,则返回空。

  8. 规则 all-bases ( type )

    按其与类型的距离顺序返回给定类型及其所有基类型。

  9. 规则 all-derived ( type )

    按其与类型的距离顺序返回给定类型及其所有派生类型。

  10. 规则 is-derived ( type base )

    如果type等于base或具有base作为其直接或间接基类型,则返回 true;否则返回 false。

  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为空,则以派生类特有的方式执行此目标的默认构建。

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。为此项目中包含的所有目标生成虚拟目标。

    成功后,返回

    • 一个属性集,其中包含要应用于依赖项的使用要求

    • 一个生成的虚拟目标列表,该列表可能为空。

  2. 规则 build-dir ( )

    返回项目的根构建目录。

  3. 规则 main-target ( name )

    返回对应于namemain-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. rule generate ( property-set )

    重写abstract-target.generate。通过查找所有其需求被property-set满足的备选方案并选择需求集最长的方案,为这个主目标选择一个备选方案。返回在该备选方案上调用generate的结果。

    成功后,返回

    • 一个属性集,其中包含要应用于依赖项的使用要求

    • 一个生成的虚拟目标列表,该列表可能为空。

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. rule init ( name : project : sources * : requirements * : default-build * : usage-requirements * )

    name

    目标的名称

    project

    声明目标的project

  2. rule generate ( property-set )

    重写abstract-target.generate。确定最终构建属性,生成源文件,并调用construct。此方法不应被重写。

    成功后,返回

    • 一个属性集,其中包含要应用于依赖项的使用要求

    • 一个生成的虚拟目标列表,该列表可能为空。

  3. rule construct ( name : source-targets * : property-set )

    为这个抽象目标构建虚拟目标。返回一个usage-requirements属性集和一个虚拟目标列表。应在派生类中重写。

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是最常见的目标备选方案。创建typed目标的规则会为每种类型自动定义。

  1. rule init ( name : project : type : sources * : requirements * : default-build * : usage-requirements * )

    name

    目标的名称

    project

    声明目标的project

    type

    目标的type

  2. rule type ( )

    返回目标的type

  3. rule 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 )
}

标识和值之间存在一一对应关系。该类的两个实例不相等。为保持此属性,应使用'property-set.create'规则创建新实例。实例是不可变的。

  1. rule raw ( )

    返回存储属性的Jam列表。

  2. rule str ( )

    返回存储属性的字符串表示。

  3. rule propagated ( )

    返回一个包含此property-set中所有propagated属性的property-set

  4. rule add ( ps )

    返回一个新的property-set,其中包含此property-setps中的属性的并集。

    如果ps包含应该覆盖此对象中值的非自由属性,请改用refine
  5. rule add-raw ( properties * )

    链接add,只是它采用属性列表而不是property-set

  6. rule refine ( ps )

    通过覆盖ps中指定了不同值的任何非自由和非条件属性来细化属性。返回生成的property-set

  7. rule get ( feature )

    返回feature的所有值。

6.7. 构建过程

构建过程的总体概述已在用户文档中给出。本节提供更多细节和一些特定规则。

概括地说,使用特定属性构建目标包括以下步骤:

  1. 应用默认构建;

  2. 选择要使用的主目标备选方案;

  3. 确定“公共”属性;

  4. 构建源列表和依赖属性引用的目标;

  5. 将构建依赖项时产生的使用需求添加到“公共”属性中;

  6. 使用生成器构建目标;

  7. 计算要返回的使用需求。

6.7.1. 备选方案选择

当目标有多个备选方案时,必须选择其中一个。流程如下:

  1. 对于每个备选方案,其条件定义为其需求中基本属性的集合。条件属性被排除在外。

  2. 只有当构建请求中存在其条件中的所有属性时,备选方案才可行。

  3. 如果只有一个可行的备选方案,则选择它。否则,尝试找到最佳备选方案。如果备选方案a的条件属性集是备选方案b的条件属性集的真子集,则备选方案a优于备选方案b。如果一个可行的备选方案优于所有其他备选方案,则选择它。否则,报告错误。

6.7.2. 确定公共属性

“公共”属性是一个有点人为的术语。这是中间属性集,从中派生依赖项的构建请求和构建目标的属性。

由于默认构建和备选方案已经处理完毕,我们只有两个输入:构建请求和需求。以下是关于公共属性的规则:

  1. 非自由特性只能有一个值。

  2. 需求中的非条件属性始终存在于公共属性中。

  3. 构建请求中的属性存在于公共属性中,除非它被需求中的属性覆盖。

  4. 如果构建请求或需求(非条件或条件)包含可扩展属性(复合属性或具有指定的子特性值),则行为等效于将所有扩展属性显式添加到构建请求或需求中。

  5. 如果需求包含条件属性,并且此属性的条件在公共属性的上下文中为真,则条件属性也应在公共属性中。

  6. 如果此处其他规则未给出特性的值,则其在公共属性中具有默认值。

这些规则是声明式的。它们没有指定如何计算公共属性。但是,它们为用户提供了足够的信息。重要的是处理条件需求。条件可以由构建请求中的属性、非条件需求甚至另一个条件属性来满足。例如,以下示例按预期工作:

exe a : a.cpp
      : <toolset>gcc:<variant>release
        <variant>release:<define>FOO ;

6.7.3. 目标路径

几个因素决定了具体文件目标的位置。项目中的所有文件都构建在bin目录下,除非构建目录项目属性覆盖了这一点。bin目录下有一条路径,该路径取决于用于构建每个目标的属性。这条路径由所有非自由、非偶然属性唯一确定。例如,给定一个包含以下属性集:<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>是一个偶然特性,<define><include>是自由特性,因此它们不影响路径。

有时,B2生成的路径可能会过长。有一些命令行选项可以解决这个问题。--abbreviate-paths将每个元素缩短到不超过五个字符。例如,link-static变为lnk-sttc--hash选项使用MD5哈希将路径缩短为单个目录。

有两个特性会影响构建目录。<location>特性完全覆盖默认构建目录。例如:

exe a : a.cpp : <location>. ;

a生成的所有文件构建在Jamfile的目录中。通常不建议这样做,因为它排除了变体构建。

<location-prefix>特性会在项目构建目录下向路径添加前缀。例如:

exe a : a.cpp : <location-prefix>subdir ;

将在bin/subdir/gcc-4.6.1/debug中创建a的文件。

6.8. 定义

6.8.1. 特性和属性

特性是构建配置的规范化(与工具集无关)方面,例如是否启用了内联。特性名称不能包含“>”字符。

构建配置中的每个特性都具有一个或多个关联的。非自由特性的特性值不能包含尖括号(“<”)、冒号(“:”)、等号(“=”)和短横线(“-”)标点符号。自由特性的特性值不能包含尖括号(“<”)字符。

属性是(特性,值)对,表示为<特性>值。

子特性是一个仅在其父特性存在时才存在的特性,其标识可以在其父特性的上下文中从其值派生出来。子特性的父特性永远不能是另一个子特性。因此,特性及其子特性形成了一个两级层次结构。

特征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. 属性有效性

对于自由特征,所有值都是有效的。对于所有其他特征,有效值是明确指定的,并且构建系统将报告使用无效特征值的错误。子属性的有效性可能会受到限制,因此某些值仅在存在某些其他子属性的情况下才有效。例如,可以指定<gcc-target>mingw属性仅在存在<gcc-version>2.95.2的情况下才有效。

6.8.3. 特征属性

每个特征都具有一组零个或多个以下属性。特征属性是对构建系统在构建请求中出现特征值时如何解释这些值的低级描述。我们也指属性的属性,例如,一个偶然的属性,其特征具有偶然的属性。

  • 偶然的

    假设偶然特征完全不会影响构建产品。因此,构建系统可能会对构建规范仅在偶然特征方面不同的目标使用相同的文件。控制编译器警告级别的特征就是一个可能的偶然特征示例。

    假设非偶然特征会影响构建产品,因此构建规范在非偶然特征方面不同的目标的文件将放置在不同的目录中,如目标路径中所述。

  • 传播的

    此类特征会传播到依赖项。也就是说,如果使用传播属性构建主目标,则构建系统会在构建该主目标的任何依赖项时尝试使用相同的属性。例如,当请求优化可执行文件时,通常希望它与优化库链接。因此,<optimization>特征是传播的。

  • 自由的

    大多数特征都有一组有限的允许值,并且在给定的构建规范中只能采用该集合中的单个值。另一方面,自由特征可以同时具有多个值,并且每个值都可以是任意字符串。例如,可以同时定义多个预处理器符号。

    <define>NDEBUG=1 <define>HAS_CONFIG_H=1
  • 可选的

    可选特征是不需要出现在构建规范中的特征。每个非可选非自由特征都有一个默认值,当未在目标的要求或用户的构建请求中指定特征的值时使用该值。[特征的默认值由特征声明中列出的第一个值给出。——将此移到其他位置 - dwa]

  • 对称的

    通常,只有当特征的值与其默认值不同时,特征才会生成子变体目录,这会导致某些特征值的子变体目录结构不对称。对称特征始终生成相应的子变体目录。

  • 路径

    路径特征的值指定一个路径。该路径被视为相对于使用路径特征的Jamfile目录,并在从不同目录调用构建时由构建系统适当地转换。

  • 隐式的

    仅隐式特征的值标识该特征。例如,用户不需要编写“<toolset>gcc”,而只需编写“gcc”。隐式特征名称也不会出现在变体路径中,尽管值会出现在变体路径中。因此:bin/gcc/…​而不是bin/toolset-gcc/…​。通常应该只有少数这样的特征,以避免可能的名称冲突。

  • 复合的

    复合特征实际上对应于属性组。例如,构建变体就是一个复合特征。从一组构建属性生成目标时,复合特征会递归展开并添加到构建属性集中,以便规则如有必要可以找到它们。非复合非自由特征会覆盖构建属性集中复合特征的组件。

  • 依赖的

    依赖特征的值是目标引用。当用于构建主目标时,依赖特征的值被视为附加依赖项。

    例如,依赖特征允许声明库A依赖于库B。结果,每当应用程序链接到A时,它也会链接到B。将B指定为A的依赖项与将B添加到A的源代码不同。

既不是自由的也不是偶然的特征称为基础特征。

6.8.4. 特征声明

低级特征声明接口是feature模块中的feature规则。

rule feature ( name : allowed-values * : attributes * )

可以使用feature.extend规则扩展特征的允许值。

6.8.5. 属性细化

当请求具有某些属性的目标并且该目标需要某些属性集时,需要找到用于构建的属性集。此过程称为属性细化,由这些规则执行。

  1. 所需集合中的每个属性都添加到原始属性集中。

  2. 如果原始属性集包含具有非自由特征的不同值的属性,则该属性将被删除。

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带有一个用于Jamfiles的调试器。要运行调试器,请使用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. 扩展程序手册

8.1. 简介

本节说明如何扩展B2以适应您的本地需求——主要是添加对您拥有的非标准工具的支持。在开始之前,请确保您已阅读并理解元目标的概念,概念,这对于理解其余内容至关重要。

当前版本的B2具有三个级别的目标,如下所示。

元目标

从Jamfiles中的声明创建的对象。可以使用一组属性调用它以生成具体目标。

具体目标

对应于文件或操作的对象。

Jam目标

特定于Boost.Jam构建引擎的低级具体目标。本质上是一个字符串——通常是文件名。

在大多数情况下,您只需要处理具体目标和从元目标创建具体目标的过程。很少需要扩展元目标级别。Jam目标通常仅在命令行模式中使用。

所有Boost.Jam目标相关的内置函数,如DEPENDSALWAYS都作用于Jam目标。将它们应用于元目标或具体目标无效。

8.1.1. 元目标

元目标 (Metatarget) 是一个对象,它记录 Jamfile 中指定的信息,例如元目标类型、名称、源文件和属性,并且可以结合特定属性来生成具体的目标。在代码层面,它由派生自 abstract-target 类的实例表示。[4]

generate 方法接收构建属性(作为 property-set 类的实例)并返回一个列表,包含:

  • 第一个元素——此调用中的使用需求(property-set 的实例)

  • 后续元素——已创建的具体目标(virtual-target 类的实例)。

可以使用 targets.resolve-reference 函数通过目标 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 的名称。第三个参数是应用于此操作的属性集。第二行创建目标。我们指定名称、类型和项目。我们还传递前面创建的操作对象。如果操作创建多个目标,我们可以多次重复第二行。

在某些情况下,创建具体目标的代码可能会使用相同的属性多次调用。返回对应于同一文件的两个不同的 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. 示例:一对一生成器

假设您正在编写生成 C++ 代码的应用程序。如果您曾经这样做过,您就会知道这不太好。将大量 C++ 代码嵌入字符串文字中非常笨拙。更好的解决方案是:

  1. 编写要生成的代码模板,在将要更改的位置留下占位符。

  2. 访问应用程序中的模板,并将占位符替换为相应的文本。

  3. 编写结果。

这很容易实现。您可以编写特殊的逐字文件,这些文件只是 C++,除了文件的首行包含应生成的变量的名称。创建了一个简单的工具,它接受逐字文件并创建一个 cpp 文件,其中包含一个 char* 变量,其名称取自逐字文件的首行,其值是文件的正确引用的内容。

让我们看看 B2 可以做什么。

首先,B2 不了解“逐字文件”。因此,您必须注册一个新的目标类型。以下代码就是这样做的:

import type ;
type.register VERBATIM : verbatim ;

type.register 的第一个参数给出已声明类型的名称。按照约定,它是大写的。第二个参数是此类型文件的后缀。因此,如果 B2 在源列表中看到 code.verbatim,它就知道它是 VERBATIM 类型。

接下来,您告诉 B2 逐字文件可以在一个构建步骤中转换为 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 ;

列出的逐字文件将自动转换为 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,您可以设置新类型的后缀。或者您可以为新类型编写一个特殊的生成器。例如,它可以为插件生成额外的元信息。无论哪种方式,只要可以使用 SHARED_LIB,就可以使用 PLUGIN 类型。例如,您可以直接将插件链接到应用程序。

可以将类型定义为“主”类型,在这种情况下,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 以支持新工具。

对于每个额外的工具,必须创建一个名为 generator 的 B2 对象。该对象接受并产生特定类型的目标。利用这些信息,B2 能够自动调用 generator。例如,如果您声明一个 generator,它接受类型为D的目标并产生类型为OBJ的目标,那么在源列表中放置扩展名为.d的文件将导致 B2 调用您的 generator,然后将生成的 obj 文件链接到应用程序。(当然,这需要您指定.d扩展名对应于D类型。)

每个 generator 应该都是从generator类派生的类的实例。在最简单的情况下,您不需要创建派生类,只需创建一个generator类的实例即可。让我们回顾一下我们在引言中看到的示例。

import generators ;
generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
actions inline-file
{
    "./inline-file.py" $(<) $(>)
}

我们声明一个标准 generator,指定其 ID、源类型和目标类型。调用时,generator 将创建一个类型为CPP的目标,其源目标类型为VERBATIM作为唯一源。但是,实际生成文件将使用什么命令?在 B2 中,操作是使用命名的“actions”块指定的,并且在创建目标时应指定 action 块的名称。按照约定,generator 使用与其自身 ID 相同的 action 块名称。因此,在上面的示例中,将使用“inline-file”actions 块将源转换为目标。

主要有两种 generator:标准 generator 和组合 generator,它们分别使用generators.register-standardgenerators.register-composing规则注册。例如

generators.register-standard verbatim.inline-file : VERBATIM : CPP ;
generators.register-composing mex.mex : CPP LIB : MEX ;

第一个(标准)generator 接受类型为VERBATIM单个源并产生结果。第二个(组合)generator 接受任意数量的源,这些源可以具有CPPLIB类型。组合 generator 通常用于生成顶级目标类型。例如,构建exe目标时调用的第一个 generator 是与正确的链接器对应的组合 generator。

您还应该了解两个用于注册 generator 的特定函数:generators.register-c-compilergenerators.register-linker。第一个设置 C 文件的头文件依赖扫描,第二个处理各种复杂性,例如搜索库。因此,在添加对编译器和链接器的支持时,您应该始终使用这些函数。

(需要关于 UNIX 的说明)

自定义 generator 类

标准 generator 允许您指定源类型和目标类型、操作和一组标志。如果您需要更复杂的内容,则需要使用您自己的逻辑创建一个新的 generator 类。然后,您必须创建该类的实例并注册它。以下是如何创建您自己的 generator 类的示例

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 ] ;

此 generator 的工作方式与我们上面定义的verbatim.inline-file generator 完全相同,但可以通过覆盖generator类的方法来定制行为。

有两个值得关注的方法。run方法负责整个过程——它接收多个源目标,将它们转换为正确的类型,并创建结果。generated-targets方法在所有源都转换为正确的类型以实际创建结果时调用。

当您想要向生成的 target 添加其他属性或使用其他源时,可以覆盖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 ] ;

generated-targets方法将使用类型为EXE的单个源目标调用。对virtual-target.traverse的调用将返回可执行文件依赖的所有目标,我们进一步找到没有从任何内容生成的那些文件。找到的目标将添加到源中。

可以覆盖run方法以完全自定义 generator 的工作方式。特别是,可以完全自定义将源转换为所需类型的过程。这是一个另一个真实的例子。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++ 编译器都具有相同的含义,用户不必担心传递给编译器命令行的确切选项。

除了这些可移植特性之外,还有特殊的“原始”特性,允许用户根据需要将任何值传递给特定工具的命令行参数。例如,<cxxflags>特性允许您将任何命令行选项传递给 C++ 编译器。<include>特性允许您传递任何以-I开头的字符串,并且解释是特定于工具的。(有关此特性的非常巧妙用法的示例,请参阅我可以使用 Boost.Jam 变量捕获外部程序输出吗?)。当然,人们应该始终努力使用可移植特性,但这些特性仍然作为后门提供,以确保 B2 不会从用户那里夺走任何控制权。

使用可移植特性是一个好主意,因为

  • 当可移植特性被赋予一组固定的值时,您可以使用特性的两种不同设置构建项目,B2 将自动为生成的文件使用两个不同的目录。B2 不会尝试分离使用不同原始选项构建的目标。

  • 与“原始”特性不同,您不需要在 Jamfile 中使用特定的命令行标志,并且它更有可能与其他工具一起工作。

添加特性的步骤

添加特性需要三个步骤

  1. 声明特性。为此,使用“feature.feature”规则。您必须决定特性属性的集合

    • 如果您希望一个目标的特性值集自动传播到其依赖的目标,则将其设置为“propagated”。

    • 如果一个特性没有固定的值列表,它必须是“free”。例如,include特性就是一个自由特性。

    • 如果一个特性用于引用相对于 Jamfile 的路径,它必须是一个“path”特性。此类特性的值也将自动转换为 B2 的内部路径表示。例如,include是一个路径特性。

    • 如果特性用于引用某个目标,它必须是一个“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 ;

并导入该模块,你就可以在Jamfiles中使用“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只读取一次,因此通常没有你可以访问的特征的单个值。

特征只有在构建目标时才具有特定值,并且你可以使用该值的方法有两种:

9.2. 我收到“实际目标名称重复”错误。这是什么意思?

最可能的情况是你试图编译同一个文件两次,具有几乎相同但不同的属性。例如:

exe a : a.cpp : <include>/usr/local/include ;
exe b : a.cpp ;

上面的代码片段需要编译两次不同的a.cpp,它们只有include属性不同。由于include特性被声明为free,B2不会为每个值创建单独的构建目录,这两个构建都将生成在同一个构建目录中生成的obj文件。忽略这一点并只编译一次文件将是危险的,因为不同的包含可能导致编译完全不同的代码。

要解决此问题,你需要确定是否应编译该文件一次或两次。

  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实际上为你稍微更改生成的obj文件名,从而避免任何冲突。

    请注意,在这两种情况下,include属性都将仅用于构建这些目标文件,而不会用于可能为目标ab添加的任何其他源代码。

一个很好的问题是为什么B2不能自动使用上述某些方法。问题是这种魔法只在半数情况下有用,而在另一半情况下,它会默默地做错事。在这种情况下,要求用户阐明其意图更简单、更安全。

9.3. 访问环境变量

许多用户希望在Jamfiles中使用环境变量,例如,控制外部库的位置。在许多情况下,最好在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包含目录,然后是b,即使它们指定的顺序相反。在大多数情况下,用户并不关心。但有时包含的顺序或其他属性的顺序很重要。对于这种情况,提供了一种特殊的语法:

exe a : a.cpp : <include>a&&b ;

&&符号分隔属性值,并指定应保留它们的顺序。建议你仅在属性顺序确实很重要时才使用此功能,而不是作为方便的快捷方式。到处使用它可能会对性能产生负面影响。

9.5. 如何控制Unix上的库链接顺序?

在类Unix操作系统上,调用链接器时指定静态库的顺序很重要,因为默认情况下,链接器会遍历库列表一次。以不正确的顺序传递库将导致链接错误。此外,此行为通常用于使一个库覆盖另一个库中的符号。因此,有时需要强制特定的库链接顺序。

B2尝试自动计算正确的顺序。主要规则是,如果库a“使用”库b,则库a将出现在命令行中库b之前。如果库b存在于库a的源代码中,或者其使用情况列在其需求中,则认为库a使用b。要显式指定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)位置?

你可能希望在Jamfiles中使用项目的根目录位置。要访问它,只需使用以下方法在你的Jamroot.jam文件中声明路径常量:

path-constant TOP : . ;

之后,可以在每个Jamfile中使用TOP变量。

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 page_或维基百科

为简洁起见,我们将在下面只使用_rpath 列表_。

9.9.1. hardcode-dll-paths

针对exe目标的hardcode-dll-paths功能在开发中尤其有用;由于构建系统已经知道所有已用共享库的路径,因此它默认会自动将它们添加到可执行文件的rpath列表中。

但是,当安装可执行文件时,情况有所不同;显然,已安装的可执行文件不应包含指向开发树的硬编码路径。install规则因此隐式地(即默认地)否定hardcode-dll-paths功能,如有必要,则重新链接可执行文件无需自动路径。

  • 对于exe规则

    • 使用<hardcode-dll-paths>true(默认值),所有包含已用共享库的目录的路径都会自动添加到目标的rpath列表中。

    • 需要显式<hardcode-dll-paths>false属性来禁用自动将目录路径添加到共享库。

  • 对于install规则

    • 如果需要,则需要显式<hardcode-dll-paths>true才能将添加到源目标的rpath列表传播到install目标。(这包括添加到源目标的显式dll-path条目。)

    • 默认情况下,隐式<hardcode-dll-paths>false属性将确保源目标的rpath列表不会传播到install目标。

  • <hardcode-dll-paths>功能在lib规则中被忽略。

9.9.2. dll-path

作为替代方案——或此外——您可以使用dll-path功能手动将显式目录路径添加到rpath列表中。
例如

install installed : application : <dll-path>/usr/lib/snake
                                  <location>/usr/bin ;

将允许应用程序找到放置在/usr/lib/snake目录中的库。

9.9.3. 结论

如果您将库安装到非标准位置并添加显式路径,则可以更好地控制将使用的库。系统位置中相同名称的库不会被意外使用。如果您将库安装到系统位置并且没有添加任何路径,则系统管理员将拥有更多控制权。可以单独升级每个库,所有应用程序都将使用新库。

哪种方法最佳取决于您的具体情况。如果库相对独立,并且可以被第三方应用程序使用,则应将其安装在系统位置。如果您有很多库只能由您的应用程序使用,那么将它们安装到非标准目录并添加显式路径是有意义的,如上例所示。另请注意,不同系统的准则在这方面有所不同。例如,Debian GNU 指南禁止任何附加搜索路径,而 Solaris 指南建议应始终使用它们。

共享库搜索路径总结

(适用于客户端目标——即使用共享库的目标。)

规则 特性 Rpath 列表添加

exe

hardcode-dll-paths

true(默认值)

所有共享库目录的绝对路径
如果这些是目标本身,则会添加它们的构建目录路径。

exe

hardcode-dll-paths

false

(无)

install

hardcode-dll-paths

true

rpath 列表从源(exelib目标)传播到已安装的二进制文件。(这包括添加到源目标的显式dll-path条目。)

install

hardcode-dll-paths

false(默认值)

(无)

lib

hardcode-dll-paths

已禁用
(无效)

(无)

exelibinstall

dll-path

(绝对路径)

指定的绝对路径

exelibinstall

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 和一个目标。现在,可以在任何Jamfile中编写

exe hello : hello.cpp /site-config//zlib ;

9.11. 纯头文件库

在现代C++中,库通常只包含头文件,而没有要编译的源文件。要使用此类库,您需要向项目添加适当的包含项和可能的定义。但是,对于大量的外部库来说,记住哪些库是纯头文件库,哪些库需要链接到是很麻烦的。但是,使用B2,可以将纯头文件库声明为B2目标,所有依赖项都可以使用此类库,而无需记住它是否是纯头文件库。

可以使用alias规则声明纯头文件库,将其包含路径指定为其使用要求的一部分,例如

alias my-lib
    : # no sources
    : # no build requirements
    : # no default build
    : <include>whatever ;

my-lib的使用要求中指定的包含项会自动添加到其所有依赖项的构建属性中。依赖项不需要关心my-lib是否是纯头文件库,并且以后可以将my-lib制作成常规编译库,而无需将其包含项添加到其依赖项声明中。

如果您已经为定义了纯头文件库的项目声明了适当的使用要求,则无需为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功能,不会传播。即,它只适用于在其上指定的 target。

功能: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配置。此特性会**传播**到依赖项。其用法在初始化章节中讨论。

特性:pkg-config-define

此**免费**特性会向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的值。

技巧
使用多个配置

假设您有两个.pc文件集合:一个用于平台A,另一个用于平台B。您可以初始化两个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:占用最少的空间:删除所有不必要的空格,属性值被压缩以具有最小的表示。

此特性是**可选的**,不会**传播**到依赖目标。如果未指定样式,则如果属性集包含属性<optimization>on,则选择compressed样式。否则,选择nested样式。

特性:sass-line-numbers

启用发出显示规则的原始行号的注释。这对于调试样式表很有用。可用值为onoff。此特性是**可选的**,不会**传播**到依赖目标。如果未指定此特性的值,则从特性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. 运行时检查

此示例演示如何在使用clang或gcc工具集时启用运行时检查

main.cpp
int main()
{
    char* c = nullptr;
    std::cout << "Hello sanitizers\n " << *c;
}

我们的jamroot.jam非常简短,只为程序指定了一个exe目标

jamroot.jam
exe main : main.cpp ;

可以通过将onnorecover传递到相应的运行时检查特性(例如thread-sanitizer=on)来启用运行时检查。norecover选项会导致程序在检测到第一个运行时检查问题后终止。以下示例演示如何在简单程序中启用addressundefined运行时检查

> 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_rvacpp

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 库的主引导脚本)使用。

--cxx=CXX

使用指定的编译器执行文件,而不是检测到的编译器执行文件。

--cxxflags=CXXFLAGS

除了检测到的编译器的标志外,还使用的编译器标志。

12.2. 语言

B2 具有解释型过程式语言。b2 中的语句包括规则(过程)定义、规则调用、控制流结构、变量赋值以及各种语言支持。

12.2.1. 词法特性

B2 将其输入文件视为由空格分隔的标记,但有以下两个例外:双引号(") 可以包含空格以将其嵌入到标记中,并且规则操作定义中匹配的花括号({}) 之间的所有内容都被视为单个字符串。反斜杠 (\) 可以转义双引号或任何单个空格字符。

B2 要求所有标记(包括冒号 (:) 和分号 (;) 标记)周围都必须有空格(空格、制表符或换行符)。

B2 关键字(本文档中已提及)是保留字,通常必须用双引号(") 引起来才能用作任意标记,例如变量名或目标名。

注释以 # 字符开头,并延伸到行尾。块注释以 #| 开头,并延伸到下一个 |#

12.2.2. 目标

b2 的基本数据实体是目标。构建目标是要更新的文件。源目标是用于更新构建目标的文件。构建目标和源目标统称为文件目标,构建目标通常是其他构建目标的源目标。伪目标是表示对其他目标的依赖关系的符号,但本身不与任何真实文件关联。

文件目标的标识符通常是文件名,可以是绝对路径、相对于 b2 调用目录的相对路径,或者只是本地路径(没有目录)。大多数情况下是最后一种情况,实际文件路径使用 $(SEARCH)$(LOCATE) 特殊变量绑定。请参见下面的 SEARCH 和 LOCATE 变量。本地文件名可以选择使用粒度进行限定,粒度是一个字符串值,用于确保唯一性。标识符形式为 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 语句;操作是在更新规则的构建目标时执行的操作系统 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 ]

规则使用 *field1* 到 *fieldN* 中的值进行调用。它们可以在过程的语句中作为 $(1)$(N) (最多 9 个)引用,并且只有前两个可以在操作的 *commands* 中作为 $(1)$(2) 引用。$(<)$(>)$(1)$(2) 同义。

规则分为两类:更新规则(带有操作)和纯过程规则(没有操作)。更新规则将参数 $(1)$(2) 分别视为构建目标和源,而纯过程规则可以接受任意参数。

调用更新规则时,其更新操作将添加到与其构建目标 ($(1)) 关联的操作中,然后运行规则的过程。之后,为了在更新阶段构建目标,*commands* 将传递到操作系统命令 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

命令将重复调用,每次调用使用 $(>) 的子集,该子集足够小以适合此操作系统的命令缓冲区。

actions quietly

操作不会回显到标准输出。

actions together

对同一构建目标的同一操作的多次调用的 $(>) 将合并在一起。

actions updated

$(>) 只包含标记为要更新的源目标。

参数列表

您可以描述规则接受的参数,并在规则中按名称引用它们。例如,以下代码将“对不起,戴夫”打印到控制台:

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 * )

忽略目标上的时间戳。这有两个作用:首先,目标创建后将永远不会更新;其次,手动更新目标不会导致其他目标更新。例如,在Jambase中,此规则由MkDir规则应用于目录,因为MkDir只关心目标目录是否存在,而不关心它上次更新的时间。

TEMPORARY
rule TEMPORARY ( targets * )

目标标记为临时目标,允许在依赖它们的其它目标更新后将其删除。如果TEMPORARY目标缺失,b2将使用目标父级的时间戳。Jambase使用TEMPORARY标记构建后存档到库中的目标文件,以便在存档后可以删除它们。

FAIL_EXPECTED
rule FAIL_EXPECTED ( targets * )

为了处理构建操作预计会失败的目标(例如,当测试断言或编译时类型检查是否正常工作时),Boost Jam 提供了与NOCARE等类似风格的FAIL_EXPECTED规则。在目标更新期间,FAIL_EXPECTED参数的构建操作的返回码将被反转:如果失败,则依赖目标的构建将继续进行,就好像它成功了一样。如果它成功了,则跳过依赖目标。

RMOLD
rule RMOLD ( targets * )

当用于构建这些目标的规则失败时,B2将删除磁盘上可能存在的任何目标文件。但是,默认情况下不会删除依赖项构建失败的目标。

ISFILE
rule ISFILE ( targets * )

ISFILE 将目标标记为必须是文件。这改变了b2搜索目标的方式,使其忽略不是文件的系统项(如目录)的匹配项。这使得可以避免在头文件搜索路径中恰好有一个名为exception的目录时匹配#include "exception"

此功能目前尚未完全实现。
实用工具

ECHOEXIT这两个规则是实用程序规则,仅在`b2`的解析阶段使用。

ECHO
rule ECHO ( args * )

将消息args输出到标准输出。

EXIT
rule EXIT ( message * : result-value ? )

message输出到标准输出,然后如果未给出result-value则以失败状态退出,否则以给定的result-value退出。

EchoechoExitexit被接受为ECHOEXIT的别名,因为很难分辨这些是内置规则还是语言的一部分,例如include

GLOB

GLOB规则执行文件名通配。

rule GLOB ( directories * : patterns * : downcase-opt ? )

使用与switch语句中的模式相同的通配符。它通过用作“[ ]”内的规则调用的参数来调用。例如:FILES = [ GLOB dir1 dir2 : *.c *.h ]FILES设置为dir1dir2中C源文件和头文件的列表。生成的 filenames 是全路径名,包括目录,但模式只应用于不包含目录的文件名。

如果提供downcase-opt,则在与模式匹配之前,文件名将转换为全小写;可以使用它使用小写模式进行不区分大小写的匹配。如果操作系统提供它们,返回的路径仍然可能大小写混合。在Windows NT和Cygwin以及OpenVMS上,文件名在匹配前总是转换为小写。

GLOB_ARCHIVE

GLOB_ARCHIVE规则执行对象存档成员的名称通配。

rule GLOB_ARCHIVE ( archives * : member-patterns * : downcase-opt ? : symbol-patterns ? )

GLOB类似,此规则用于匹配存档(静态对象库)中成员文件的名称。返回成功匹配的成员列表,否则返回空值。生成的成员名称以包含存档的路径名的形式限定,形式为archive-path(member-name)。成员模式仅用于匹配成员名称;当未指定通配符时,假设完全匹配。成员名称通常对应于目标文件名,因此是特定于平台的——在匹配模式中使用平台定义的目标后缀可以提高可移植性。

如果提供downcase-opt,则在与模式匹配之前,成员名称将转换为全小写;可以使用它使用小写模式进行不区分大小写的匹配。如果操作系统提供它们,返回的路径仍然可能大小写混合。在Windows NT、Cygwin和OpenVMS上,文件名在匹配前总是转换为小写。

此外,在受支持的平台上(目前仅限OpenVMS),可以使用符号/函数模式匹配成员。在这种情况下,将返回包含匹配符号的成员。成员和符号模式将作为OR条件应用,成员模式优先。在不受支持的平台上,如果指定任何符号模式,则返回空值。

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',规则返回以下之一:

子键

'path'的所有直接子键的名称。

'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实现为无操作。SHELL在Unix、MacOS X和大多数Windows编译器上有效。在Windows下的Metrowerks编译器上,SHELL是无操作的。有一组允许的选项作为附加参数

退出状态

除了输出外,执行命令的结果状态还作为结果的第二个元素返回。

无输出

不捕获命令的输出。相反,空("")字符串值将代替输出返回。

去除行尾

删除输出中的尾随换行符(如有)。

因为Perforce/Jambase定义了一个隐藏内置规则的SHELL规则,所以在这种情况下,COMMAND可以用作SHELL的别名。

MD5
rule MD5 ( string )

MD5计算作为参数传递的字符串的MD5哈希值并返回它。

SPLIT_BY_CHARACTERS
rule SPLIT_BY_CHARACTERS ( string : delimiters )

SPLIT_BY_CHARACTERS根据delimiters中存在的任何分隔符字符拆分指定的string,并返回结果列表。

PRECIOUS
rule PRECIOUS ( targets * )

PRECIOUS 规则指定,即使更新目标的命令失败,也不应删除作为参数传递的每个目标。

PAD
rule PAD ( string : width )

如果字符串少于宽度个字符,则在右侧用空格字符填充它,并返回结果。否则,返回未修改的字符串

FILE_OPEN
rule FILE_OPEN ( filename : mode )

FILE_OPEN 规则打开指定的文件,如果mode参数为“w”或“r”,则返回文件描述符。请注意,目前只有UPDATE_NOW规则可以使用结果文件描述符编号。如果mode参数为“t”,则将文件作为文本文件打开,并将内容作为单个字符串返回。

UPDATE_NOW
rule UPDATE_NOW ( targets * : log ? : ignore-minus-n ? )

UPDATE_NOW导致立即更新指定的 target。如果更新成功,则返回非空字符串。如果存在log参数,则指定一个文件描述符,所有构建输出都将重定向到该文件。如果指定了ignore-minus-n参数,即使在命令行上指定了-n参数,也会更新目标。

12.2.5. 控制流

B2 有几个简单的控制流语句。

for var in list { statements }

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

if cond { statements }
[ else { statements } ]

显而易见;else子句是可选的。cond由以下构成:

a

如果任何a元素是非零长度字符串,则为真。

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没有元素,则为真。

! cond

条件不为真。

cond && cond

合取。

cond || cond

析取。

( cond )

优先级分组。

include file ;

导致b2读取名为file的文件。file像常规目标一样绑定(参见上面的绑定),但与常规目标不同的是,包含的file不能被构建。

包含文件在解析阶段插入到输入流中。主输入文件和所有包含文件都被视为单个文件;也就是说,b2不会从包含文件中推断任何作用域边界。

local vars [ = values ] ;

在封闭的{}块内创建新的vars,隐藏它们可能具有的任何先前值。当当前块结束时,将恢复 vars 的先前值。任何调用的规则或包含的文件都将看到局部值,而不是先前值(这有时称为动态作用域)。局部语句可以出现在任何地方,即使在块之外(在这种情况下,当输入结束时将恢复先前值)。如果存在values,则将vars初始化为values,否则保持未初始化状态。

return values ;

在规则体中,return语句设置规则调用的返回值并返回调用者。

switch value
{
    case pattern1 : statements ;
    case pattern2 : statements ;
    ...
}

switch语句执行封闭的语句中的零个或一个,具体取决于哪个(如果有的话)是第一个其模式匹配的case。模式值不会进行变量扩展。模式值可以包含以下通配符:

?

匹配任何单个字符。

*

匹配零个或多个字符。

[chars]

匹配chars中的任何单个字符。

[^chars]

匹配chars中不存在的任何单个字符。

\x

匹配x(转义其他通配符)。

while cond { statements }

cond在进入时保持为真时,重复执行语句。(参见上面if下cond表达式语法的描述)。

break ;

立即退出最近的封闭while或for循环。

continue ;

跳转到最近的封闭while或for循环的顶部。

12.2.6. 变量

B2变量是零个或多个元素的列表,每个元素都是一个字符串值。未定义的变量与具有空列表的变量无法区分,但是,已定义的变量可能具有一个或多个为null字符串的元素。所有变量都引用为$(variable)

变量是全局的或目标特定的。在后一种情况下,变量仅在更新特定目标期间采用给定值。

变量定义如下:

variable = elements ;
variable += elements ;
variable on targets = elements ;
variable on targets += elements ;
variable default = elements ;
variable ?= elements ;

前两种形式全局设置变量。第三和第四种形式设置目标特定的变量。=运算符用elements替换变量的任何先前元素;+=运算符将elements添加到变量的元素列表中。最后两种形式是同义词:它们全局设置变量,但前提是它以前未设置。

在更新命令中引用的变量将替换为它们的值;目标特定的值优先于全局值。作为参数($(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)- ->

变量元素的字符串值可以解析为原料和文件名相关的组件。变量的修饰符用于选择元素、选择组件和替换组件。修饰符如下:

[n]

选择元素编号n(从1开始)。如果变量包含少于n个元素,则结果为零元素列表。n可以为负数,在这种情况下,将返回从最后一个向左的元素编号n

[n-m]

选择元素编号nmnm可以为负数,在这种情况下,它们指的是从最后一个向左计数的元素。

[n-]

选择元素编号n到最后一个。n可以为负数,在这种情况下,它指的是从最后一个向左计数的元素。

:B

选择文件名基名——没有扩展名的基名。

:S

选择文件扩展名——(最后一个)文件名后缀。

:M

选择存档成员名称。

:D

选择目录路径。

:P

选择父目录。

:G

选择原料。

: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

: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

在评估变量后缀的展开后,将给定的赋给展开表达式值的元素。

局部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。这对于创建编译器响应文件和其他“内部”文件非常有用。展开在解析和动作执行期间都有效。因此,可以在任何三个构建阶段创建文件。此展开遵循与变量展开相同的修饰符。生成的文件展开接受这些 (:O=) 展开选项值:

F

始终将@()引用替换为生成的文件名。

C

始终将@()引用替换为内容,即:E=value表达式中的

FCCF

根据内容长度 (:E=value) 替换为文件或内容。如果命令长度短于允许的命令长度限制,则在操作中将替换为内容。否则,引用将替换为文件名。

内置变量

本节讨论对b2具有特殊意义的变量。所有这些都必须在全局模块中定义或使用——在命名模块内使用这些变量不会产生预期的效果。参见模块

SEARCH和LOCATE

这两个变量控制文件目标名称与文件系统中位置的绑定。通常,$(SEARCH)用于查找现有源文件,而$(LOCATE)用于修复已构建目标的位置。

根目录(绝对路径)文件目标按原样绑定。非根目录文件目标名称通常也按原样绑定,因此相对于当前目录,但是$(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

操作系统标识符字符串

OSPLAT

适用的底层架构

MAC

在MAC平台上为真

NT

在NT平台上为真

OS2

在OS2平台上为真

UNIX

在Unix平台上为真

VMS

在VMS平台上为真

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并执行一个shell,并将操作块作为参数传递给shell。shell的调用可以通过$(JAMSHELL)来控制。例如,Unix上的默认值为:

JAMSHELL = /bin/sh -c % ;

%将被操作块的文本替换。

B2不直接支持跨多个主机并行构建,因为这在很大程度上取决于本地环境。要跨多个主机并行构建,你需要编写自己的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 $($(>)) ;
    }
}

请注意,由于每次进入新的模块作用域时现有的变量绑定都会发生变化,因此参数绑定将不可用。这解释了上面窥视规则中使用$(>)的原因。

局部规则
local rule rulename...

规则在当前模块中局部声明。它不会使用限定符在全局模块中被注册,其名称也不会出现在以下结果中:

[ RULENAMES module-name ]
RULENAMES规则
rule RULENAMES ( module ? )

返回给定模块中所有非局部规则的名称列表。如果省略module,则返回全局模块中所有非局部规则的名称。

VARNAMES规则
rule VARNAMES ( module ? )

返回给定模块中所有变量绑定的名称列表。如果省略module,则返回全局模块中所有变量绑定的名称。

这包括在VARNAMES调用时尚未返回的调用栈中规则的任何局部变量。
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_module中的rules标记为非局部(因此是可导出的)。如果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)

找不到源文件,也没有创建它们的 Actions。

can't make N target(s)

由于找不到源文件,无法创建其他目标。

warning: X depends on itself

目标直接或通过其源文件依赖于自身。

don't know how to make X

目标不存在,并且未定义创建它的 Actions。

X skipped for lack of Y

源文件构建失败,因此无法构建目标。

warning: using independent target X

使用$(<)$(>)引用了不是任何其他目标的依赖项的目标。

X removed

B2在中断后删除了部分构建的目标。

12.3.2 错误和限制

为了成功进行并行构建,必须正确说明文件之间的依赖关系,因为目标往往会按照最快优先的顺序构建。此外,请注意那些将固定名称的文件放入当前目录的不可并行化命令,例如yacc(1)

设置不当的$(JAMSHELL)可能会导致静默失败。

12.3.3 基础知识

本节内容源自 Jam 官方文档以及使用 Jam 和阅读 Jambase 规则的经验。我们在此重复这些信息,主要是因为它对于理解和使用 Jam 至关重要,但在单个位置并未进行整合。官方文档中甚至缺少一些内容。我们希望它对希望熟悉 Jam 和 Boost 构建系统的任何人都有用。

  • Jam rules实际上是简单的过程实体。可以将其视为函数。参数用冒号分隔。

  • Jam **目标**是由任意字符串标识的抽象实体。内置的DEPENDS规则在命名目标之间的依赖关系图中创建链接。

  • 请注意,内置INCLUDES规则的原始 Jam 文档是不正确的:INCLUDES targets1 : targets2会导致依赖于targets1成员的所有内容都依赖于targets2的所有成员。它通过将targets2附加到targets1中所有内容的依赖列表中的特殊尾部部分来实现这一点。这样创建循环依赖似乎是可以的;事实上,当单个构建操作同时产生targets1targets2时,这似乎是“正确的做法”。

  • 调用规则时,如果声明的actions与规则同名,则将这些actions添加到由规则的第一个参数标识的目标的更新操作中。如果声明了相应的actions,则实际上可以调用未声明的规则:该规则被视为为空。

  • 目标(NOTFILE目标除外)通过称为绑定的过程与文件系统中的路径关联。绑定是一个基于目标特定的SEARCHLOCATE变量设置,搜索与目标(不含grist)同名的文件的过程。

  • 除了局部变量和全局变量外,jam 还允许您在目标上设置变量。目标特定的变量值通常无法读取,并且仅在以下上下文中生效:

    • 在更新操作中,首先在由第一个参数命名的目标(正在更新的目标)上查找变量值。因为 Jam 在执行操作之前构建其完整的依赖树,所以 Jam 规则将目标特定的变量设置作为向相应操作提供参数的一种方式。

    • 绑定完全由目标特定的SEARCHLOCATE变量设置控制,如下所述。

    • 在用于头文件扫描的特殊规则中,首先在由规则的第一个参数(正在扫描的源文件)命名的目标上查找变量值。

  • 变量的“绑定值”是与变量命名的目标关联的路径。在构建操作中,前两个参数会自动替换为它们的绑定值。可以使用bind操作修饰符有选择地将目标特定的变量替换为它们的绑定值。

  • 请注意,Jam 文档中使用的术语“绑定”表示一个包含三个子阶段的处理阶段:绑定(是的!)、更新确定和头文件扫描。“绑定”一词的重复可能会导致一些混淆。特别是,Jam 文档中的“修改绑定”部分可能应该命名为“修改更新确定”。

  • “Grist”只是一个 <characters> 形式的字符串前缀。它用于 Jam 中基于更简单的名称创建唯一目标名称。例如,文件名test.exe可能被单独子项目中的目标使用,或者用于“相同”抽象目标的调试和发布版本。绑定到名为“test.exe”的文件的每个不同目标都有其自己的唯一 grist 前缀。Boost 构建系统还充分利用了 Jam 将字符串划分为 grist 边界的能力,有时将多个带 grist 的元素连接到字符串的开头。使用 grist 而不是使用绝对路径标识目标有两个原因:

    1. 目标的位置不能总是仅仅从用户放在 Jamfile 中的内容推导出,有时还取决于绑定过程。仍然需要某种机制来明确标识具有相同名称的目标。

    2. Grist 允许我们为每个构建的目标使用统一的抽象标识符,而不管目标文件位置如何(如设置 ALL_LOCATE_TARGET 所允许的那样)。

  • 当使用 $(var:G) 从名称中提取 grist 时,结果包括前导和尾随尖括号。当使用$(var:G=expr)将 grist 添加到名称时,首先会剥离现有的 grist。然后,如果expr非空,则根据需要添加前导 < 和尾随 > 以形成 <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 的功能。这长期以来一直是一个愿望清单项目。

  • 新增:添加对为一些 IDE 集成生成 compile_commands.json 命令数据库的支持。——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

  • 允许使用多次默认初始化某些工具。这些工具包括 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 工具集,识别使用工具链版本 14.40.33807 的 Visual Studio 2022 v17.10.0。——Dmitry Andric

  • 修复:对于 msvc 工具集,exception-handling=off 应该为 Dinkumware/MSSTL 定义 _HAS_EXCEPTIONS=0。——Nikita Kniazev

14.3. 版本 5.1.0

这主要是一个错误修复版本,用于解决影响 Boost 库的问题。不过,有一个“重大”更改。在运行大型构建时,查找构建失败可能相当困难。为了方便找出问题,构建结束时简短的摘要输出现在不再那么简短。它现在包含一个已排序的目标列表,这些目标已被跳过和失败。这些列表的输出反映了常规跳过/失败的项目。因此,可以快速搜索其余输出中的相同字符串。

  • 新增:在构建摘要末尾添加失败和跳过目标的列表,以便更容易找到失败之处。——René Ferdinand Rivera Morell

  • 新增:将 mpi.run-flags 添加到 mpi 工具集中,允许将任意标志应用于运行 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*

  • 新增:将 `class`、`errors`、`modules`、`regex`、`set`、`string` 和 `sysinfo` 模块移植到 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*

  • 新增:添加 linemarkers 功能,该功能在预处理目标上更改行为以发出/省略行指令,例如 `#line` 和 `#`。—— *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 报告)。通过更改 `no` 条件的求值以进行短路。—— *Nikita Kniazev*

  • 修复相同的工具集覆盖(继承覆盖)。—— *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` 以纠正运行失败行为。—— *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 版本

  • 修复某些平台/编译器上由于无效的垃圾读取(变参结束标记为 `int` 而不是 `nullptr`)导致的崩溃。

  • 在 Windows 上使用 GCC 时,不要强制使用 Windows 路径分隔符。因为它会混淆 Cygwin GCC 的相对包含路径处理。—— *René Ferdinand Rivera Morell*

  • 向项目声明中添加 `common-requirements`,作为同时为 `requirements` 和 `usage-requirements` 声明相同内容的简写。—— *René Ferdinand Rivera Morell*

  • 向项目 `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` 功能添加 `minimal` 和 `debug` 选项。—— *René Ferdinand Rivera Morell*

  • 添加 Rocket Lake、Alder Lake、Sapphire Rapids 和 Zen 3 指令集。—— *Andrey Semashev*

  • 删除所有退出时内存泄漏并修复所有 ASAN 错误。—— *René Ferdinand Rivera Morell*

  • 删除使用 `boost-build.jam` 作为初始化配置文件。—— *René Ferdinand Rivera Morell*

  • 删除不完整的构建系统端口和 Jam 引擎 Python 支持扩展。—— *René Ferdinand Rivera Morell*

  • 修复在 macOS 上使用 `darwin` 和 `clang` 工具集进行组合 arm+x86 构建时出现的问题。—— *René Ferdinand Rivera Morell*

  • 修复在 macOS 上使用 `clang` 工具集进行交叉编译时出现的问题。—— *René Ferdinand Rivera Morell*

  • 修复使用 `gcc` 和 `clang` 工具集将大量长名称对象文件收集到静态存档中时出现的错误。—— *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.jam` 和 `boost-build` 规则进行初始化。仍然会搜索和加载 `boost-build.jam` 以避免破坏现有操作,但这被认为已弃用,将在未来的版本中删除。

14.15. 4.8.2 版本

  • 修复由于递归销毁和目标列表弹出取消链接不正确导致的退出清理目标列表时崩溃的问题。—— *René Ferdinand Rivera Morell*

14.16. 4.8.1 版本

  • 修复在 9.0 之前的旧版 macOS/XCode 版本上构建引擎的问题,因为缺少 `EXIT_SUCCESS` 和 `EXIT_FAILURE` 宏。—— *René Ferdinand Rivera Morell*

14.17. 4.8.0 版本

  • 新增:添加对 LoongArch 的支持。—— *张娜 (Zhang Na)*

  • 更改引擎构建,如果可用则使用静态 Intel 库,而不是 C++ 运行时静态库,以修复静态 C++ 运行时不可用的系统。—— *Alain Miniussi*

  • 重新排序 msvc `cflags` 和 `cxxflags`,并添加 `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 版本

  • 修复如果 icpx 不在 PATH 中但 icpc 在 PATH 中则配置 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*

  • 允许别名目标即使 `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 的指令集特性值。——Thomas Brown

  • MIPS:添加通用 MIPS 架构。——YunQiang Su

  • 修复 MSVC 编译器的预处理问题。——Nikita Kniazev

14.21. 4.6.1 版本

  • 修复使用 cygwin64 构建 b2 引擎的问题。——René Ferdinand Rivera Morell

  • 修复从编译器执行文件检测 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.sh`中`local`的使用,以兼容某些功能不完整的 shell。——Tanzinul Islam

  • 在 busybox shell 上解决`build.sh`中 shell 数组引用错误的办法。——tkoecker

  • 检查某些平台上是否需要`-pthread`来使用 gcc 构建引擎。——tkoecker

  • 在 macOS 上默认使用 clang。——Stéphan Kochen

  • 添加`/python//numpy`目标作为依赖项,用于传递特定版本的属性。——Peter Dimov

  • 在使用自定义`cxx`工具集构建引擎时,从环境变量`CXX`和`CXXFLAGS`添加`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 在构建框架组的新家中的第一个版本。

  • 更改 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

  • 启用使用 clang 在 Windows 上构建 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 和其他安装程序。这消除了对`boost-build.jam`文件进行引导的需要。使用户更容易上手。——René Ferdinand Rivera Morell

  • 修复从 VS 预览命令提示符构建的问题。——Marcel Raad

  • 修复 macOS darwin 工具集上的编译器版本检查问题。——Bo Anderson

  • 删除 GCC 上 pch 目标命名限制。——Nikita Kniazev

  • 选择合适的 QNX 目标平台。——Alexander Karzhenkov

  • 对 Windows 上 b2 引擎构建的各种空间和性能改进。——Nikita Kniazev

  • 为每个编译器填充额外的和严格的警告选项。——Nikita Kniazev

  • 包含引擎 IO 失败的 OS 错误原因。——Nikita Kniazev

  • 使用`/Zc:inline`和`/Zc:throwingNew`标志以获得更好的语言一致性。——Nikita Kniazev

  • 添加 C++20 的`cxxstd`值 20。——Andrey Semashev

  • 在 MSVC 上并行编译 B2 引擎。——Nikita Kniazev

  • 使用新的 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 版本

此版本主要是对引擎的次要修复和清理。特别是引导/构建过程现在明确地传达了 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

  • 允许根`b2 b2`引擎构建即使`bison`语法生成器不可用也能工作。——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

  • 添加 LTO 特性以及对 gcc 和 clang 工具集的相应支持。——Dmitry Arkhipov

  • 修复源文件没有类型时的错误。——Peter Dimov

  • 添加特性的文档。——Dmitry Arkhipov

  • 增强`stdlib`特性及其相应的文档,用于 clang、gcc 和 sun 工具集。——Dmitry Arkhipov

  • 安装规则现在仅明确它创建的直接目标。——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*

  • 将ARM64添加为MSVC的有效架构。—— *Marc Sweetgall*

  • 为gcc和clang设置来自覆盖率功能的覆盖率标志。—— *Damian Jarek*

  • 在gcc/clang中添加s390x CPU和支持。—— *Neale Ferguson*

  • 支持导入pkg-config包。—— *Dmitry Arkhipov*

  • 支持内存泄漏检测器。—— *Damian Jarek*

  • 修复clang-win中缺少的/manifest选项,以修复名称中包含“update”的可执行文件的管理员提升权限问题。—— *Peter Dimov*

  • freertos添加到os特性中。—— *Thomas Brown*

  • 将默认并行作业(-jX)设置为可用的CPU线程数。—— *René Ferdinand Rivera Morell*

  • 简化了覆盖率功能。—— *Hans Dembinski*

  • 为静态分析工具提供了更好的堆栈。—— *James E. King III*

此版本中默认的并行作业数已从“1”更改为核心数。在某些情况下,此默认值可能大于分配的CPU资源,例如在某些虚拟化容器安装中。

附录A:许可证

除了B2根据Boost Software License - Version 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端口可能永远不会创建重复的目标。