B2
具有解释型过程式语言。 b2
中的语句是规则(过程)定义、规则调用、控制流结构、变量赋值以及各种语言支持。
B2
将其输入文件视为以空格分隔的标记,有两个例外:双引号(")可以将空格括起来以将其嵌入到标记中,并且规则操作定义中匹配的花括号({})之间的所有内容都被视为单个字符串。反斜杠(\)可以转义双引号或任何单个空格字符。
B2
要求所有标记周围都有空格(空格、制表符或换行符),包括冒号(:)和分号(;)标记。
B2
关键字(本文档中提到)是保留的,通常必须用双引号(")括起来才能用作任意标记,例如变量或目标名称。
注释以 #
字符开头,一直延续到行尾。
基本的 b2
数据实体是目标。构建目标是要更新的文件。源目标是用于更新构建目标的文件。构建目标和源目标统称为文件目标,构建目标通常是其他构建目标的源目标。伪目标是表示对其他目标的依赖关系的符号,但本身并不与任何实际文件相关联。
文件目标的标识符通常是文件的名称,可以是绝对根目录、相对于 b2
调用目录的相对路径,或者只是本地路径(没有目录)。最常见的是最后一种情况,实际文件路径使用 $(SEARCH)
和 $(LOCATE)
特殊变量绑定。请参阅下面的 SEARCH 和 LOCATE 变量。本地文件名可以选择性地用 grist 资格,grist 是用于确保唯一性的字符串值。具有 file(member) 形式标识符的文件目标是库成员(通常是 Unix 上的 ar
(1) 存档)。
每当将目标绑定到文件系统中的位置时,Boost Jam 都会查找名为 BINDRULE
的变量(首先在要绑定的目标上,然后在全局模块中)。如果非空,=$(BINDRULE[1])= 将命名一个规则,该规则将使用目标的名称和正在绑定的路径调用。=$(BINDRULE[1])= 命名的规则的签名应与以下匹配
rule bind-rule ( target : path )
此功能对于正确的头文件扫描很有用,因为许多编译器将在包含执行 #include
指令的文件的目录中首先搜索 #include
文件。 $(BINDRULE)
可用于记录该目录。
基本的 b2
语言实体称为规则。规则定义分为两个部分:过程和操作。过程是在调用规则时运行的一组 jam 语句;操作是在更新规则的构建目标时执行的操作系统 shell 命令。
规则可以返回值,这些返回值可以展开成一个列表 "[ rule args ... ]"。规则的值是其最后一条语句的值,但只有以下语句具有值:“if”(选择的分支的值)、“switch”(选择的情况的值)、set(结果变量的值)以及“return”(其参数的值)。请注意,“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
$(>)
仅包括标记为要更新的源目标本身。
您可以描述规则接受的参数,并在规则中按名称引用它们。例如,以下代码将 "I'm sorry, Dave" 打印到控制台
rule report ( pronoun index ? : state : names + ) { local he.suffix she.suffix it.suffix = s ; local I.suffix = m ; local they.suffix you.suffix = re ; ECHO $(pronoun)'$($(pronoun).suffix) $(state), $(names[$(index)]) ; } report I 2 : sorry : Joe Dave Pete ;
正式参数列表中的每个名称(在规则声明中用 ":
" 分隔)都绑定到对应实际参数的单个元素,除非后面跟着以下修饰符之一
符号 |
前面符号的语义 |
---|---|
|
optional |
|
绑定到实际参数的零个或多个未绑定元素。当 |
|
绑定到实际参数的一个或多个未绑定元素。 |
将检查实际参数和正式参数的一致性,如果不一致,将导致 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 代码时使用参数列表。
B2
有一套不断增长的内置规则,所有这些规则都是没有更新操作的纯过程规则。它们分为三组:第一组构建依赖关系图;第二组修改它;第三组只是实用程序规则。
rule DEPENDS ( targets1 * : targets2 * )
构建直接依赖关系:使每个 targets1 依赖于每个 targets2。通常,如果 targets2 本身被重建或比 targets1 更新,则将重建 targets1。
rule INCLUDES ( targets1 * : targets2 * )
构建兄弟依赖关系:使依赖于任何 targets1 的任何目标也依赖于每个 targets2。这反映了当一个源文件包含另一个文件时出现的依赖关系:从源文件构建的对象既依赖于原始源文件,也依赖于包含的源文件,但这两个源文件并不相互依赖。例如
DEPENDS foo.o : foo.c ; INCLUDES foo.c : foo.h ;
在此示例中,“foo.o
” 依赖于 “foo.c
” 和 “foo.h
”。
六个规则 ALWAYS
、LEAVES
、NOCARE
、NOTFILE
、NOUPDATE
和 TEMPORARY
修改依赖关系图,以便 b2
在其目标绑定阶段以不同的方式处理目标。请参阅上面的绑定。通常,如果目标丢失、其文件系统修改时间早于其任何依赖关系(递归地)或其任何依赖关系正在更新,则 b2
会更新目标。通过调用以下规则,可以更改此基本行为
rule ALWAYS ( targets * )
导致无论 targets 是否是最新的,都要重建它们(它们仍然必须在依赖关系图中)。这用于 clean 和 uninstall 目标,因为它们没有依赖关系,否则将永远不会出现需要构建的情况。最好将其应用于也是 NOTFILE
目标的目标,但它也可以用于强制更新实际文件。
rule LEAVES ( targets * )
使每个 targets 只依赖于其叶源,而不依赖于任何中间目标。这使得它不受其依赖关系更新的影响,因为“叶”依赖关系是那些没有自身依赖关系且没有更新操作的依赖关系。这允许仅当原始源文件更改时才更新目标。
rule NOCARE ( targets * )
当 b2
遇到无法找到且没有更新动作的 目标 时,会忽略它们。通常情况下,b2
会发出警告,然后跳过依赖这些缺失目标的其他目标。Jambase
中的 HdrRule
使用 NOCARE
来处理头文件扫描期间找到的头文件名,以告知 b2
包含的文件可能不存在。例如,如果 #include
位于 #ifdef
内,那么包含的文件可能实际上并不存在。
对于具有构建动作的目标:如果它们的构建动作以非零返回值退出,依赖目标仍将被构建。
rule NOTFILE ( targets * )
将 目标 标记为伪目标而不是真实文件。不会检查时间戳,因此只有在目标的依赖项被更新,或者目标也被标记为 ALWAYS
时,才会执行此目标上的动作。默认的 b2
目标 "all
" 是一个伪目标。在 Jambase
中,NOTFILE
用于定义一些额外的方便的伪目标。
rule NOUPDATE ( targets * )
导致忽略 目标 的时间戳。这有两个影响:首先,一旦创建目标,它将永远不会被更新;其次,手动更新目标不会导致其他目标被更新。例如,在 Jambase
中,此规则由 MkDir
规则应用于目录,因为 MkDir
只关心目标目录是否存在,而不关心它上次更新的时间。
rule TEMPORARY ( targets * )
将 目标 标记为临时文件,允许在其他依赖它们的目標更新后删除它们。如果 TEMPORARY
目标丢失,b2
会使用目标父目录的时间戳。 Jambase
使用 TEMPORARY
来标记在构建后被归档到库中的目标文件,以便在归档后可以删除它们。
rule FAIL_EXPECTED ( targets * )
为了处理预期构建动作会失败的目标(例如,在测试断言或编译时类型检查是否正常工作时),Boost Jam 以与 NOCARE
等规则类似的方式提供了 FAIL_EXPECTED
规则。在目标更新期间,对 FAIL_EXPECTED
的参数的构建动作的返回值会被反转:如果它失败,依赖目标的构建将继续进行,就好像它成功了。如果它成功,依赖目标将被跳过。
rule RMOLD ( targets * )
B2
会删除构建这些目标的规则失败时磁盘上可能存在的任何目标文件。但是,默认情况下,不会删除依赖项构建失败的目标。RMOLD
规则会导致如果它的任何参数的依赖项构建失败,则删除它的参数。
rule ISFILE ( targets * )
ISFILE
将目标标记为必须是文件。这改变了 b2
搜索目标的方式,使其忽略对不是文件的系统项(如目录)的匹配。这使得可以避免 #include "exception"
匹配,如果碰巧在头文件搜索路径中有一个名为 exception 的目录。
目前尚未完全实现。
两个规则 ECHO
和 EXIT
是工具规则,仅在 b2
的解析阶段使用。
rule EXIT ( message * : result-value ? )
将 message 输出到标准输出,然后以失败状态退出,如果未提供 result-value,否则它将以给定的 result-value 退出。
"Echo
","echo
","Exit
" 和 "exit
" 被接受为 ECHO
和 EXIT
的别名,因为很难判断这些是内置规则,而不是语言的一部分,就像 "include
"。
GLOB
规则执行文件名通配。
rule GLOB ( directories * : patterns * : downcase-opt ? )
使用与 switch 语句中的模式相同的通配符。它通过在 "[ ]
" 内作为规则调用的参数来调用。例如:" FILES = [ GLOB dir1 dir2 : *.c *.h ]
" 将 FILES
设置为 dir1
和 dir2
中的 C 源代码和头文件的列表。生成的 文件名为完整路径名,包括目录,但模式仅应用于不包含目录的文件名。
如果提供了 downcase-opt,则在与模式匹配之前,文件名将被转换为全小写;可以使用它来使用小写模式进行不区分大小写的匹配。如果操作系统提供了路径,则返回的路径仍然可能混合大小写。在 Windows NT 和 Cygwin 上,文件名在匹配之前总是被转换为小写。
MATCH
规则执行模式匹配。
rule MATCH ( regexps + : list * )
将 egrep
(1) 风格的正则表达式 regexps 与 list 中的字符串进行匹配。结果是对 list 中的每个字符串以及 regexps 中的每个正则表达式的每个匹配的 ()
子表达式列表。
rule BACKTRACE ( )
返回一个四元组列表:filename line module rulename...,描述调用堆栈的每个较浅的级别。此规则可用于从 Jam 规则生成有用的诊断消息。
rule UPDATE ( targets * )
经典的 jam 将命令行中任何非选项元素视为要更新的目标名称。这阻止了对命令行进行更复杂的处理。现在已重新启用此功能,但对 UPDATE
规则进行了额外更改,以允许灵活地更改要更新的目标列表。UPDATE 规则有两个影响
如果未使用 UPDATE
规则指定任何目标,则不会更新任何目标。为了以更实用的方式支持更改更新列表,该规则还会返回以前在更新列表中的目标。这使得可以像这样添加目标
local previous-updates = [ UPDATE ] ; UPDATE $(previous-updates) a-new-target ;
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_DWORD
','REG_SZ
','REG_EXPAND_SZ
','REG_MULTI_SZ
'。带有 'REG_DWORD
' 类型的數據将被转换为字符串,'REG_MULTI_SZ
' 将被转换为字符串列表,而对于带有 'REG_EXPAND_SZ
' 类型的數據,其中的环境变量将被其定义的值替换。带有 'REG_SZ
' 类型的數據和其他不支持的类型将被放入字符串中,而不会进行修改。如果无法接收数据的价值,它只返回一个空列表。例如,
local PSDK-location = [ W32_GETREG HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MicrosoftSDK\\Directories : "Install Dir" ] ;
rule W32_GETREGNAMES ( path : result-type )
仅针对 win32 平台定义。它读取 Windows 的注册表。'path' 是信息的存储位置,'result-type' 是 'subkeys
' 或 'values
' 之一。有关 'path' 格式和约束的更多信息,请参阅 W32_GETREG
。
根据 'result-type',该规则返回以下内容之一
subkeys
'path' 的所有直接子密钥的名称。
values
注册表中给定 'path' 的密钥包含的值的名称。只有在密钥的值已在注册表中设置的情况下,密钥的“默认”值才会出现在返回的列表中。
如果 'result-type' 无法识别,或请求的数据无法检索,则该规则返回一个空列表。示例
local key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths" ; local subkeys = [ W32_GETREGNAMES "$(key)" : subkeys ] ; for local subkey in $(subkeys) { local values = [ W32_GETREGNAMES "$(key)\\$(subkey)" : values ] ; for local value in $(values) { local data = [ W32_GETREG "$(key)\\$(subkey)" : "$(value)" ] ; ECHO "Registry path: " $(key)\\$(subkey) ":" $(value) "=" $(data) ; } }
rule SHELL ( command : * )
SHELL
执行 command,然后返回 command 的标准输出。 SHELL
仅在 C 库中具有 popen()
函数的平台上工作。在没有工作 popen()
函数的平台上, SHELL
被实现为无操作。 SHELL
在 Unix、MacOS X 和大多数 Windows 编译器上工作。 SHELL
在 Windows 下的 Metrowerks 编译器上是无操作。有一组允许的选项作为附加参数
exit-status
除了输出外,执行命令的结果状态还作为结果的第二个元素返回。
no-output
不要捕获命令的输出。而是返回一个空 ("") 字符串值来代替输出。
由于 Perforce/Jambase 定义了一个隐藏内置规则的 SHELL
规则,因此在这种情况下,COMMAND
可以用作 SHELL
的别名。
rule SPLIT_BY_CHARACTERS ( string : delimiters )
SPLIT_BY_CHARACTERS
将指定的 字符串 在 分隔符 中存在的任何分隔符字符处进行拆分,并返回结果列表。
rule FILE_OPEN ( filename : mode )
FILE_OPEN
规则打开指定的 文件并返回文件描述符。 模式 参数可以是 "w" 或 "r"。请注意,目前只有 UPDATE_NOW
规则可以使用结果文件描述符号。
rule UPDATE_NOW ( targets * : log ? : ignore-minus-n ? )
UPDATE_NOW
导致指定的 目标立即更新。如果更新成功,则返回非空字符串。如果存在 日志 参数,则指定将构建的所有输出重定向到的 文件的描述符。如果指定了 忽略-n 参数,即使在命令行上指定了 -n
参数,也会更新目标。
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 是列表 a 和 b 中第一个不匹配的元素
a <= b
每个 a 字符串小于或等于其 b 对应项
a > b
a[i] 字符串大于 b[i] 字符串,其中 i 是第一个不匹配的元素
a >= b
每个 a 字符串大于或等于其 b 对应项
a in b
如果所有 a 元素都可以在 b 中找到,或者 a 没有元素,则为真
! cond
条件不为真
cond && cond
连接
cond || cond
析取
( cond )
优先级分组
include file ;
导致 b2
读取名为 file 的文件。 file 的绑定方式与普通目标相同(请参阅上面的绑定),但与普通目标不同的是,包含 file 不能被构建。
包含 file 在解析阶段被插入到输入流中。主输入文件和所有包含文件都被视为单个文件;也就是说,b2
不会从包含文件中推断任何作用域边界。
local vars [ = values ] ;
在封闭的 {}
块内创建新的 vars,遮蔽它们可能具有的任何先前值。当当前块结束时,会恢复变量的先前值。任何被调用或包含的规则都会看到局部值而不是先前值(这有时被称为动态作用域)。local 语句可以出现在任何位置,即使在块之外(在这种情况下,先前值将在输入结束时恢复)。如果存在,则将 vars 初始化为 values,否则保持未初始化状态。
return values ;
在规则主体中,return 语句会设置规则调用的返回值。它不会导致规则返回;规则的实际值是执行的最后一条语句的值,因此 return 应该是规则“自然”返回之前执行的最后一条语句。
switch value { case pattern1 : statements ; case pattern2 : statements ; ... }
switch 语句根据哪个(如果有)是第一个其 pattern 与 value 匹配的 case,执行零个或一个封闭的 statements。 pattern 值不会被变量展开。模式值可以包含以下通配符
?
匹配任何单个字符
*
匹配零个或多个字符
[chars]
匹配 chars 中的任何单个字符
[^chars]
匹配不在 chars 中的任何单个字符
\x
匹配 x(转义其他通配符)
while cond { statements }
只要 cond 在进入时保持为真,就重复执行 statements。(请参阅上面 if 下 cond 表达式语法的描述)。
B2
变量是零个或多个元素的列表,每个元素都是字符串值。未定义的变量与具有空列表的变量没有区别,但是,定义的变量可能有一个或多个元素,这些元素是空字符串。所有变量都引用为 $(variable)
。
变量可以是全局的,也可以是特定于目标的。在后一种情况下,变量只在更新特定目标期间采用给定值。
用以下方式定义变量
variable = elements ; variable += elements ; variable on targets = elements ; variable on targets += elements ; variable default = elements ; variable ?= elements ;
前两种形式全局设置 variable。第三和第四种形式设置特定于目标的变量。 =
运算符将 variable 的所有先前元素替换为 elements;+=
运算符将 elements 添加到 variable 的元素列表中。最后两种形式是同义词:它们全局设置 variable,但前提是它以前未设置。
在更新命令中引用的变量将替换为其值;特定于目标的值优先于全局值。作为参数传递给操作的变量($(1)
和 $(2)
)将替换为其绑定值;可以在操作上使用“bind
”修饰符,以便将其他变量替换为绑定值。请参阅上面的操作修饰符。
B2
变量不会重新导出到执行更新操作的 shell 的环境中,但更新操作可以使用 $(variable)
引用 b2
变量。
在解析期间,b2
对不是关键字或规则名称的每个标记执行变量展开。具有嵌入式变量引用的此类标记将替换为零个或多个标记。变量引用采用 $(v)
或 $(vm)
的形式,其中 v 是变量名称,而 m 是可选的修饰符。
规则操作中的变量展开类似于语句中的变量展开,但操作字符串以空格分隔,无论是否引用。
标记在变量展开后的结果是标记组件的 乘积,其中每个组件都是一个文字子字符串或一个替换变量引用的列表。例如
$(X) -> a b c t$(X) -> ta tb tc $(X)z -> az bz cz $(X)-$(X) -> a-a a-b a-c b-a b-b b-c c-a c-b c-c
变量名称和修饰符本身可以包含变量引用,并且它也参与乘积
$(X) -> a b c $(Y) -> 1 2 $(Z) -> X Y $($(Z)) -> a b c 1 2
由于这种乘积展开,如果标记中的任何变量引用未定义,则展开的结果将是空列表。如果任何变量元素是空字符串,则结果将传播非空元素
$(X) -> a "" $(Y) -> "" 1 $(Z) -> -$(X)$(Y)- -> -a- -a1- -- -1- -$(X)$(Z)- ->
变量元素的字符串值可以解析为磨料和与文件名相关的组件。变量的修饰符用于选择元素、选择组件和替换组件。修饰符是
[n]
选择元素编号 n(从 1 开始)。如果变量包含的元素少于 n 个,则结果为空列表。 n 可以为负数,在这种情况下,返回最后一个左边的元素编号 n。
[n-m]
选择元素编号 n 到 m。 n 和 m 可以为负数,在这种情况下,它们指的是从最后一个左边开始的元素。
[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"
:chars
选择 chars 中列出的组件。
:G=grist
用 grist 替换 grist。
:D=path
用 path 替换目录。
:B=base
用 base 替换文件名基部。
:S=suf
用 suf 替换文件名的后缀。
:M=mem
用 mem 替换存档成员名称。
:R=root
如果未根目录化,则将 root 附加到整个文件名。
:E=value
如果变量未设置,则将 value 分配给变量。
:J=joinval
将列表元素连接成单个元素,用 joinval 分隔。
在 VMS 上,$(var:P)
是 $(var:D)
的父目录。
Boost Jam 允许您在循环中直接声明局部循环控制变量。
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)
形式的子表达式,并在用 filecontents
设置内容后用 filename
替换表达式。这对于创建编译器响应文件和其他“内部”文件很有用。扩展在解析和操作执行期间都有效。因此,可以在三个构建阶段中的任何一个阶段创建文件。
本节讨论对 b2
具有特殊意义的变量。所有这些变量都必须在全局模块中定义或使用——在命名模块中使用这些变量将不会产生预期的效果。请参阅 模块。
这两个变量控制文件目标名称在文件系统中的位置绑定。通常,$(SEARCH)
用于查找现有源代码,而 $(LOCATE)
用于修复构建目标的位置。
根目录化(绝对路径)文件目标按原样绑定。未根目录化的文件目标名称通常也按原样绑定,因此相对于当前目录,但 $(LOCATE)
和 $(SEARCH)
的设置会改变这一点。
$(LOCATE)
,则目标相对于 $(LOCATE)
中的第一个目录绑定。只有第一个元素用于绑定。$(SEARCH)
,则目标绑定到 $(SEARCH)
中的第一个目录,其中目标文件已经存在。$(SEARCH)
搜索失败,目标无论如何都会绑定到当前目录。$(SEARCH)
和 $(LOCATE)
都应该特定于目标,而不是全局设置。如果它们在全局范围内设置,b2
将对所有文件绑定使用相同的路径,这不可能产生合理的结果。在编写自己的规则时,尤其是那些没有建立在 Jambase 上的规则,您可能需要直接设置 $(SEARCH)
或 $(LOCATE)
。Jambase 中定义的大多数规则分别针对它们正在寻找的源代码和它们创建的目标设置 $(SEARCH)
和 $(LOCATE)
为合理的价值。
这两个变量控制头文件扫描。 $(HDRSCAN)
是一个 egrep(1)
模式,其中 () 包含文件名,用于在源文件中查找文件包含语句。 Jambase
使用 $(HDRPATTERN)
作为 $(HDRSCAN)
的模式。 $(HDRRULE)
是一个规则的名称,该规则将使用扫描结果来调用:扫描的文件是目标,找到的文件是源文件。这是 b2
通过变量设置调用规则的唯一地方。
为了进行头文件扫描,必须同时设置 $(HDRSCAN)
和 $(HDRRULE)
,并且它们应该特定于目标,而不是全局设置。如果它们在全局范围内设置,所有文件,包括可执行文件和库,都将被扫描以查找头文件包含语句。
扫描头文件包含并不精确,但至少是动态的,因此无需运行类似 makedepend(GNU)
的东西来创建静态依赖文件。扫描机制倾向于包含(即,它更可能返回实际上未被编译器使用的文件名,而不是遗漏包含文件),因为它无法判断 #include
行是否位于 #ifdefs
或其他条件逻辑中。在 Jambase
中,HdrRule
将 NOCARE
规则应用于扫描期间找到的每个头文件,因此,如果文件尚不存在,不会导致编译失败,b2
将不会关心。
此外,正则表达式扫描仅在包含的文件名在源文件中时有效。它无法处理允许使用变量名称包含文件的语言(就像 Jam
语言本身一样)。
有时需要禁止某些操作的并行执行。例如
Craig McPeeters 扩展了 Perforce Jam 来解决这些问题,该扩展已集成到 Boost.Jam 中。
任何目标都可以被分配一个 信号量,方法是在该目标上设置一个名为 SEMAPHORE
的变量。变量的值是信号量名称。它必须不同于任何声明目标的名称,但否则是任意的。
信号量的语义是,在一组具有相同信号量的目标中,无论“-j”选项如何,都只能在当前时刻更新一个目标。
许多 Jam 内置变量可用于标识运行时平台。
OS
OS 标识符字符串
OSPLAT
在适用时,底层体系结构
MAC
在 MAC 平台上为真
NT
在 NT 平台上为真
OS2
在 OS2 平台上为真
UNIX
在 Unix 平台上为真
VMS
在 VMS 平台上为真
JAMDATE
b2
启动时的时间和日期,作为 ISO-8601 UTC 值。
JAMUNAME
uname(1) 命令的输出(仅限 Unix)
JAMVERSION
b2
版本,当前为“3.1.19”
JAM_VERSION
一个具有两个元素的预定义全局变量表示 Boost Jam 的版本号。Boost Jam 版本从“03
” “00
” 开始。早期的 Jam
版本不会自动定义 JAM_VERSION
。
当 b2
执行规则的操作块时,它会派生并执行一个 shell,并将操作块作为参数传递给 shell。可以使用 $(JAMSHELL)
控制 shell 的调用。Unix 上的默认值为,例如
JAMSHELL = /bin/sh -c % ;
%
将被替换为操作块的文本。
B2
不直接支持在多个主机上并行构建,因为这在很大程度上依赖于本地环境。要在多个主机上并行构建,您需要编写自己的 shell 来提供对多个主机的访问。然后将 $(JAMSHELL)
重置为引用它。
就像 b2
将 %
扩展为规则的操作块的文本一样,它将 !
扩展为多进程插槽号。插槽号在命令行上给出的 -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__
可以设置为规则的名称,以便 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__
中规则名称后的任何值都将在此处传递。
target
已构建的 b2
目标。
command
操作主体中执行的命令的文本。
status
执行的命令的整数结果。
start
执行的命令的开始时间戳,作为 ISO-8601 UTC 值。
end
执行的命令的完成时间戳,作为 ISO-8601 UTC 值。
user
执行的命令花费的用户 CPU 秒数,作为浮点数。
system
执行的命令花费的系统 CPU 秒数,作为浮点数。
输出
命令的输出作为单个字符串。输出内容反映了 -pX
选项的使用。
如果两个变量都为目标设置,则都将被调用,首先是 __TIMING_RULE__
,然后是 __ACTION_RULE__
。
Boost Jam 引入了对模块的支持,模块为规则和变量提供了基本的命名空间保护。还引入了新的关键字“module
”。本节中描述的功能是原语,这意味着它们旨在提供编写 Jam 规则所需的运算,这些规则提供了更优雅的模块接口。
module expression { ... }
{ ... }
中的代码在由表达式评估的模块中执行。规则定义可以在模块自身的命名空间中找到,也可以在全局模块的命名空间中找到,名称为 module-name.rule-name,因此在模块中,该模块中的其他规则始终可以在不限定的情况下被调用
module my_module { rule salute ( x ) { ECHO $(x), world ; } rule greet ( ) { salute hello ; } greet ; } my_module.salute goodbye ;
当在当前模块的命名空间中找不到被调用的规则时,会在全局模块的命名空间中查找,因此限定调用跨模块有效
module your_module
{
rule bedtime ( ) { my_module.salute goodnight ; }
}
每个模块都有自己的一组动态嵌套变量范围。当执行从模块 A 转到模块 B 时,A 中的所有变量绑定都变得不可用,并将被属于 B 的绑定所取代。这同样适用于本地变量和全局变量
module A { x = 1 ; rule f ( ) { local y = 999 ; # becomes visible again when B.f calls A.g B.f ; } rule g ( ) { ECHO $(y) ; # prints "999" } } module B { y = 2 ; rule f ( ) { ECHO $(y) ; # always prints "2" A.g ; } }
访问另一个模块的变量的唯一方法是进入该模块
rule peek ( module-name ? : variables + ) { module $(module-name) { return $($(>)) ; } }
请注意,由于每次进入新的模块范围时,现有的变量绑定都会发生变化,因此参数绑定将变得不可用。这解释了上面 peek 规则中使用“$(>)
”的原因。
rule VARNAMES ( module ? )
返回给定模块中所有变量绑定的名称列表。如果省略了 module,则返回全局模块中所有变量绑定的名称。
这包括调用堆栈中来自尚未返回的规则的所有本地变量,这些规则在 VARNAMES
调用时尚未返回。
IMPORT
允许跨模块的规则名称别名
rule IMPORT ( source_module ? : source_rules * : target_module ? : target_rules * )
The IMPORT
规则将 source_module 中的规则复制到 target_module 中,作为本地规则。如果 source_module 或 target_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
允许跨模块的规则名称别名
rule EXPORT ( module ? : rules * )
The 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.
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)} ;
rule DELETE_MODULE ( module ? )
DELETE_MODULE
从给定模块(或者没有提供模块的情况下,从全局模块)中删除所有变量绑定和其他未引用的规则,并将它们的内存返回给系统。
虽然它不会影响当前正在执行的规则,直到它们完成,但 DELETE_MODULE
应该谨慎使用,因为它会立即擦除所有其他规则和所有变量(包括该模块中的本地变量)。由于动态绑定的工作方式,被本地变量隐藏的变量不会被销毁,因此结果可能非常不可预测。