概览
为了使用正确的选项调用编译器,Bazel 需要一些关于编译器内部的知识,例如添加目录和重要标志。换句话说,Bazel 需要一个简化的编译器模型来了解其工作原理。
Bazel 需要知道以下信息:
- 编译器是否支持 thinLTO、模块、动态链接或 PIC(位置无关代码)。
- 指向所需工具(例如 gcc、ld、ar、objcopy 等)的路径。
- 内置系统包含目录。Bazel 需要这些头文件来验证源文件中包含的所有头文件已在
BUILD
文件中正确声明。 - 默认的 sysroot。
- 用于编译、链接和归档的标志。
- 哪些标志用于支持的编译模式(opt、dbg、Fastbuild)。
- 使变量成为编译器特别需要的变量。
如果编译器支持多个架构,则 Bazel 需要单独配置这些架构。
CcToolchainConfigInfo
是一个提供程序,提供所需的细化程度,用于配置 Bazel 的 C++ 规则的行为。默认情况下,Bazel 会自动为您的 build 配置 CcToolchainConfigInfo
,但您可以选择手动配置。为此,您需要一条提供 CcToolchainConfigInfo
的 Starlark 规则,并将 cc_toolchain
的 toolchain_config
属性指向您的规则。您可以通过调用 cc_common.create_cc_toolchain_config_info()
来创建 CcToolchainConfigInfo
。您可以在 @rules_cc//cc:cc_toolchain_config_lib.bzl
中找到此过程中需要的所有结构体的 Starlark 构造函数。
当 C++ 目标进入分析阶段时,Bazel 会根据 BUILD
文件选择相应的 cc_toolchain
目标,并从 cc_toolchain.toolchain_config
属性中指定的目标获取 CcToolchainConfigInfo
提供程序。cc_toolchain
目标通过 CcToolchainProvider
将此信息传递给 C++ 目标。
例如,由 cc_binary
或 cc_library
等规则实例化的编译或链接操作需要以下信息:
- 要使用的编译器或链接器
- 编译器/链接器的命令行标记
- 通过
--copt/--linkopt
选项传递的配置标志 - 环境变量
- 执行操作的沙盒所需的工件
除沙盒中所需的工件之外,上述所有信息均在 cc_toolchain
指向的 Starlark 目标中指定。
要传送到沙盒的工件在 cc_toolchain
目标中声明。例如,借助 cc_toolchain.linker_files
属性,您可以指定要传送到沙盒中的链接器二进制文件和工具链库。
工具链选择
工具链选择逻辑的运作方式如下:
用户在
BUILD
文件中指定cc_toolchain_suite
目标,并使用--crosstool_top
选项将 Bazel 指向该目标。cc_toolchain_suite
目标引用多个工具链。--cpu
和--compiler
标志的值决定了仅基于--cpu
标志值或联合--cpu | --compiler
值选择其中哪些工具链。选择流程如下:如果指定了
--compiler
选项,Bazel 会从带有--cpu | --compiler
的cc_toolchain_suite.toolchains
属性中选择相应条目。如果 Bazel 没有找到相应的条目,则会抛出错误。如果未指定
--compiler
选项,Bazel 会从cc_toolchain_suite.toolchains
属性中选择仅包含--cpu
的相应条目。如果未指定标志,Bazel 会检查主机系统,并根据其发现结果选择
--cpu
值。请参阅检查机制代码。
选择工具链后,Starlark 规则中相应的 feature
和 action_config
对象将控制 build(即后文所述的项目)的配置。这些消息让您可以在 Bazel 中实现完整的 C++ 功能,而无需修改 Bazel 二进制文件。C++ 规则支持多个唯一操作,Bazel 源代码中对此进行了详细介绍。
功能
功能是需要命令行标志、操作、对执行环境的限制条件或依赖项更改的实体。功能可以很简单,比如允许 BUILD
文件选择标志的配置(例如 treat_warnings_as_errors
),或者与 C++ 规则交互,并向编译添加新的编译操作和输入(例如 header_modules
或 thin_lto
)。
理想情况下,CcToolchainConfigInfo
包含一个功能列表,其中每个功能都包含一个或多个标志组,每个标志组都定义了一系列适用于特定 Bazel 操作的标志。
功能按名称指定,这样可以将 Starlark 规则配置与 Bazel 版本完全分离。也就是说,Bazel 版本不会影响 CcToolchainConfigInfo
配置的行为,前提是这些配置不需要使用新功能。
可通过以下某种方式启用功能:
- 该地图项的
enabled
字段设置为true
。 - Bazel 或规则所有者已明确启用它。
- 用户通过
--feature
Bazel 选项或features
规则属性启用 Bazel。
功能可以相互依赖,具体取决于命令行标志、BUILD
文件设置和其他变量。
特征关系
依赖项通常由 Bazel 直接管理,Bazel 只是强制执行要求并管理 build 中定义的功能本质的冲突。工具链规范允许直接在用于控制功能支持和扩展的 Starlark 规则中使用更精细的限制条件。它们是:
限制条件 | 说明 |
requires = [ feature_set (features = [ 'feature-name-1', 'feature-name-2' ]), ] |
功能级别。仅当启用了指定的必需功能时,才支持该功能。例如,某项功能仅在某些构建模式(opt 、dbg 或 fastbuild )中受支持。如果“require”包含多个“feature_set”,只要满足其中任意一项“feature_set”就表示支持该功能(即启用了所有指定的功能)。
|
implies = ['feature'] |
功能级别。此功能隐含指定的特征。启用某个功能也会隐式启用它暗含的所有功能(即,以递归方式运行)。 此外,还能够从一组功能中分离出常见的功能子集,例如排错程序的常见部分。隐式功能无法停用。 |
provides = ['feature'] |
功能级别。表示此功能是多个互斥的备用功能之一。例如,所有排错程序都可以指定 当用户同时要求两个或多个互斥功能时,这样可以通过列出替代项来提高错误处理效果。 |
with_features = [ with_feature_set( features = ['feature-1'], not_features = ['feature-2'], ), ] |
标志集级别。一项功能可以指定具有多个标志集的多个标志集。指定 with_features 后,仅当至少有一个 with_feature_set 启用了指定 features 集中的所有功能,且 not_features 集中指定的所有功能已停用时,标记集才会扩展为构建命令。
如果未指定 with_features ,系统将无条件地为指定的每项操作应用该标志集。
|
Action
通过操作,您可以灵活地修改执行操作的环境,而无需假定操作的运行方式。action_config
指定操作调用的工具二进制文件,而 feature
指定配置(标志),以确定工具在调用操作时的行为。
功能通过引用操作来指示它们会影响哪些 Bazel 操作,因为操作可以修改 Bazel 操作图。CcToolchainConfigInfo
提供程序包含的操作具有与之关联的标志和工具,例如 c++-compile
。系统会通过将每项操作与某项功能相关联来为其分配标志。
每个操作名称都代表 Bazel 执行的一种操作类型,例如编译或关联。不过,操作与 Bazel 操作类型之间存在多对一的关系,其中 Bazel 操作类型是指实现操作的 Java 类(例如 CppCompileAction
)。具体而言,下表中的“编译器操作”和“编译器操作”为 CppCompileAction
,而链接操作为 CppLinkAction
。
汇编器操作
操作 | 说明 |
preprocess-assemble
|
通过预处理进行汇编。通常用于 .S 文件。
|
assemble
|
无需预处理即可组装。通常用于 .s 文件。
|
编译器操作
操作 | 说明 |
cc-flags-make-variable
|
将 CC_FLAGS 传播到 Genrule。
|
c-compile
|
编译为 C。 |
c++-compile
|
编译为 C++。 |
c++-header-parsing
|
对头文件运行编译器的解析器,以确保头文件是独立的,否则会产生编译错误。仅适用于支持模块的工具链。 |
链接操作
操作 | 说明 |
c++-link-dynamic-library
|
关联包含其所有依赖项的共享库。 |
c++-link-nodeps-dynamic-library
|
关联仅包含 cc_library 源代码的共享库。
|
c++-link-executable
|
关联可直接运行的最终库。 |
AR 操作
AR 操作通过 ar
将对象文件组合到归档库(.a
文件)中,并将某些语义编码到名称中。
操作 | 说明 |
c++-link-static-library
|
创建静态库(归档)。 |
LTO 操作
操作 | 说明 |
lto-backend
|
将位码编译到原生对象的 ThinLTO 操作。 |
lto-index
|
生成全局索引的 ThinLTO 操作。 |
使用 action_config
action_config
是一个 Starlark 结构体,通过指定要在操作期间调用的工具(二进制文件)以及由功能定义的标志集来描述 Bazel 操作。这些标志会对操作的执行施加限制条件。
action_config()
构造函数包含以下参数:
属性 | 说明 |
action_name
|
此操作对应的 Bazel 操作。Bazel 使用此属性来探索每次操作的工具和执行要求。 |
tools
|
要调用的可执行文件。应用于操作的工具将是列表中第一个具有与功能配置匹配的功能集的工具。必须提供默认值。 |
flag_sets
|
适用于一组操作的标志列表。与功能相同。 |
env_sets
|
应用于一组操作的环境限制列表。与为地图项创建时相同。 |
action_config
可以需要并暗示其他功能和 action_config
(如前文所述的特征关系所规定)。此行为与地图项的行为类似。
最后两个属性与地图项的相应属性重复,之所以包含这些属性,是因为某些 Bazel 操作需要特定标志或环境变量,目标是避免不必要的 action_config
+feature
对。通常,最好在多个 action_config
之间共享一项功能。
您不能在同一工具链中使用同一 action_name
定义多个 action_config
。这样可以防止工具路径中产生歧义,并强制执行 action_config
背后的意图:在工具链的单个位置清楚地描述操作的属性。
使用工具构造函数
action_config
可以通过其 tools
参数指定一组工具。tool()
构造函数接受以下参数:
字段 | 说明 |
tool_path
|
相关工具的路径(相对于当前位置)。 |
with_features
|
一个特征集列表,其中必须至少满足其中一个特征集才能应用此工具。 |
对于给定的 action_config
,只有一个 tool
会将其工具路径和执行要求应用于 Bazel 操作。如需选择工具,可以遍历 action_config
的 tools
属性,直到找到 with_feature
集与功能配置匹配的工具(如需了解详情,请参阅本页面前面的特征关系)。您应该使用与空功能配置对应的默认工具作为工具列表的结尾。
用法示例
功能和操作可搭配使用,以实现具有各种跨平台语义的 Bazel 操作。例如,若要在 macOS 上生成调试符号,需要在编译操作中生成符号,然后在链接操作期间调用专用工具以创建压缩的 dsym 归档文件,然后解压缩该归档文件以生成可供 Xcode 使用的应用软件包和 .plist
文件。
使用 Bazel 时,此流程可以按以下方式实现,其中 unbundle-debuginfo
是 Bazel 操作:
load("@rules_cc//cc:defs.bzl", "ACTION_NAMES")
action_configs = [
action_config (
config_name = ACTION_NAMES.cpp_link_executable,
action_name = ACTION_NAMES.cpp_link_executable,
tools = [
tool(
with_features = [
with_feature(features=["generate-debug-symbols"]),
],
tool_path = "toolchain/mac/ld-with-dsym-packaging",
),
tool (tool_path = "toolchain/mac/ld"),
],
),
]
features = [
feature(
name = "generate-debug-symbols",
flag_sets = [
flag_set (
actions = [
ACTION_NAMES.c_compile,
ACTION_NAMES.cpp_compile
],
flag_groups = [
flag_group(
flags = ["-g"],
),
],
)
],
implies = ["unbundle-debuginfo"],
),
]
对于使用 fission
的 Linux 或生成 .pdb
文件的 Windows,可以完全以不同的方式实现这一功能。例如,基于 fission
的调试符号生成实现可能如下所示:
load("@rules_cc//cc:defs.bzl", "ACTION_NAMES")
action_configs = [
action_config (
name = ACTION_NAMES.cpp_compile,
tools = [
tool(
tool_path = "toolchain/bin/gcc",
),
],
),
]
features = [
feature (
name = "generate-debug-symbols",
requires = [with_feature_set(features = ["dbg"])],
flag_sets = [
flag_set(
actions = [ACTION_NAMES.cpp_compile],
flag_groups = [
flag_group(
flags = ["-gsplit-dwarf"],
),
],
),
flag_set(
actions = [ACTION_NAMES.cpp_link_executable],
flag_groups = [
flag_group(
flags = ["-Wl", "--gdb-index"],
),
],
),
],
),
]
标记群组
CcToolchainConfigInfo
可让您将标志捆绑到用于特定用途的组中。您可以在标记值中使用预定义变量指定一个标记,编译器会在将标记添加到构建命令时扩展该标记。例如:
flag_group (
flags = ["%{output_file_path}"],
)
在这种情况下,该标志的内容将被替换为相应操作的输出文件路径。
标志组会按照其在列表中显示的顺序(从上到下、从左到右)扩展为构建命令。
对于在添加到构建命令时需要使用不同值重复的标志,标志组可以迭代 list
类型的变量。例如,类型为 list
的变量 include_path
:
flag_group (
iterate_over = "include_paths",
flags = ["-I%{include_paths}"],
)
对于 include_paths
列表中的每个路径元素,会展开为 -I<path>
。标志组声明正文中的所有标志(或 flag_group
)都将作为一个单元展开。例如:
flag_group (
iterate_over = "include_paths",
flags = ["-I", "%{include_paths}"],
)
对于 include_paths
列表中的每个路径元素,会展开为 -I <path>
。
一个变量可以重复多次。例如:
flag_group (
iterate_over = "include_paths",
flags = ["-iprefix=%{include_paths}", "-isystem=%{include_paths}"],
)
会扩展为:
-iprefix=<inc0> -isystem=<inc0> -iprefix=<inc1> -isystem=<inc1>
变量可对应于使用点分表示法访问的结构。例如:
flag_group (
flags = ["-l%{libraries_to_link.name}"],
)
结构可以是嵌套的,还可以包含序列。为防止名称冲突并确保名称明确,您必须通过字段指定完整路径。例如:
flag_group (
iterate_over = "libraries_to_link",
flag_groups = [
flag_group (
iterate_over = "libraries_to_link.shared_libraries",
flags = ["-l%{libraries_to_link.shared_libraries.name}"],
),
],
)
条件扩展
标志组支持根据是否存在特定变量或其字段,使用 expand_if_available
、expand_if_not_available
、expand_if_true
、expand_if_false
或 expand_if_equal
属性进行条件扩展。例如:
flag_group (
iterate_over = "libraries_to_link",
flag_groups = [
flag_group (
iterate_over = "libraries_to_link.shared_libraries",
flag_groups = [
flag_group (
expand_if_available = "libraries_to_link.shared_libraries.is_whole_archive",
flags = ["--whole_archive"],
),
flag_group (
flags = ["-l%{libraries_to_link.shared_libraries.name}"],
),
flag_group (
expand_if_available = "libraries_to_link.shared_libraries.is_whole_archive",
flags = ["--no_whole_archive"],
),
],
),
],
)
CcToolchainConfigInfo 参考
本部分提供了成功配置 C++ 规则所需的 build 变量、功能和其他信息的参考信息。
CcToolchainConfigInfo 编译变量
以下是对 CcToolchainConfigInfo
构建变量的参考。
变量 | 操作 | 说明 |
source_file
|
compile | 要编译的源文件。 |
input_file
|
strip | 要剥离的工件。 |
output_file
|
compile | 编译输出。 |
output_assembly_file
|
compile | 发出的汇编文件。仅在 compile 操作发出汇编文本时适用,通常在使用 --save_temps 标志时。内容与 output_file 相同。
|
output_preprocess_file
|
compile | 预处理的输出。仅适用于仅预处理源文件的编译操作(通常在使用 --save_temps 标记时)。内容与 output_file 相同。
|
includes
|
compile | 编译器必须无条件地包含编译的源代码中的文件序列。 |
include_paths
|
compile | 编译器会在其中搜索使用 #include<foo.h> 和 #include "foo.h" 包含的头文件的序列目录。
|
quote_include_paths
|
compile | -iquote 序列包括 - 目录,编译器会在该目录中搜索使用 #include "foo.h" 包含的头文件。
|
system_include_paths
|
compile | -isystem 序列包括 - 目录,编译器会在该目录中搜索使用 #include <foo.h> 包含的头文件。
|
dependency_file
|
compile | 编译器生成的 .d 依赖项文件。 |
preprocessor_defines
|
compile | defines 的序列,例如 --DDEBUG 。
|
pic
|
compile | 将输出编译为与位置无关的代码。 |
gcov_gcno_file
|
compile | gcov 覆盖率文件。
|
per_object_debug_info_file
|
compile | 每个对象的调试信息 (.dwp ) 文件。 |
stripotps
|
strip | stripopts 序列。
|
legacy_compile_flags
|
compile | 旧版 CROSSTOOL 字段中的标志序列,如 compiler_flag 、optional_compiler_flag 、cxx_flag 和 optional_cxx_flag 。
|
user_compile_flags
|
compile | 来自 copt 规则属性或 --copt 、--cxxopt 和 --conlyopt 标志的标志序列。
|
unfiltered_compile_flags
|
compile | unfiltered_cxx_flag 旧版 CROSSTOOL 字段或 unfiltered_compile_flags 功能的标志序列。这些规则不会按 nocopts 规则属性进行过滤。 |
sysroot
|
sysroot 。 |
|
runtime_library_search_directories
|
链接 | 链接器运行时搜索路径(通常使用 -rpath 标志设置)中的条目。 |
library_search_directories
|
链接 | 链接器搜索路径中的条目(通常使用 -L 标志设置)。
|
libraries_to_link
|
链接 | 用于提供要链接的文件作为链接器调用中的输入的标志。 |
def_file_path
|
链接 | 在 Windows 上使用 MSVC 的 def 文件的位置。 |
linker_param_file
|
链接 | bazel 为克服命令行长度限制而创建的链接器参数文件的位置。 |
output_execpath
|
链接 | 链接器输出的执行路径。 |
generate_interface_library
|
链接 | "yes" 或 "no" ,具体取决于是否应生成接口库。
|
interface_library_builder_path
|
链接 | 接口库构建器工具的路径。 |
interface_library_input_path
|
链接 | 接口库 ifso 构建器工具的输入。
|
interface_library_output_path
|
链接 | 使用 ifso 构建器工具生成接口库的路径。 |
legacy_link_flags
|
链接 | 来自旧版 CROSSTOOL 字段的链接器标记。 |
user_link_flags
|
链接 | 来自 --linkopt 或 linkopts 属性的链接器标记。
|
symbol_counts_output
|
链接 | 要写入符号计数的路径。 |
linkstamp_paths
|
链接 | 提供 linkstamp 路径的 build 变量。 |
force_pic
|
链接 | 此变量的存在表示应生成 PIC/PIE 代码(传递了 Bazel 选项“--force_pic”)。 |
strip_debug_symbols
|
链接 | 此变量的存在表示应该删除调试符号。 |
is_cc_test
|
链接 | 当前操作是 cc_test 关联操作时为 true,否则为 false。
|
is_using_fission
|
编译, 链接, compile, link | 此变量的存在表示中断(每个对象的调试信息)已激活。调试信息位于 .dwo 文件(而不是 .o 文件)中,编译器和链接器需要知道这一点。
|
fdo_instrument_path
|
编译, 链接, compile, link | 存储 FDO 插桩配置文件的目录的路径。 |
fdo_profile_path
|
compile | FDO 配置文件的路径。 |
fdo_prefetch_hints_path
|
compile | 缓存预提取配置文件的路径。 |
csfdo_instrument_path
|
编译, 链接, compile, link | 存储上下文相关 FDO 插桩配置文件的目录的路径。 |
已知特征
下面介绍了功能及其激活条件。
功能 | 文档 |
opt | dbg | fastbuild
|
根据编译模式默认处于启用状态。 |
static_linking_mode | dynamic_linking_mode
|
根据关联模式默认处于启用状态。 |
per_object_debug_info
|
如果指定并启用 supports_fission 功能,并且在 --fission 标志中指定了当前编译模式,则启用。
|
supports_start_end_lib
|
启用(并且设置了 --start_end_lib 选项)后,Bazel 不会链接到静态库,而是使用 --start-lib/--end-lib 链接器选项来直接链接到对象。这样可以加快构建速度,因为 Bazel 无需构建静态库。
|
supports_interface_shared_libraries
|
启用(并且设置了 --interface_shared_objects 选项)后,Bazel 会将 linkstatic 设置为 False(默认为 cc_test )的目标关联到接口共享库。这样可以更快地进行增量重新关联。
|
supports_dynamic_linker
|
启用后,C++ 规则将知道工具链可以生成共享库。 |
static_link_cpp_runtimes
|
启用后,Bazel 将在静态链接模式下以静态方式链接 C++ 运行时,而在动态链接模式下会以动态方式链接 C++ 运行时。在 cc_toolchain.static_runtime_lib 或 cc_toolchain.dynamic_runtime_lib 属性中指定的工件(具体取决于关联模式)将添加到关联操作中。
|
supports_pic
|
启用后,工具链将知道将 PIC 对象用于动态库。每当需要 PIC 编译时,系统就会显示“pic”变量。如果默认未启用并且传递了“--force_pic”,Bazel 将请求“supports_pic”并验证该功能是否已启用。如果该功能缺失或无法启用,则无法使用“--force_pic”。 |
static_linking_mode | dynamic_linking_mode
|
根据关联模式默认处于启用状态。 |
no_legacy_features
|
阻止 Bazel 将旧版功能(如果存在)添加到 C++ 配置。请参阅下面的功能的完整列表。 |
旧版功能修补逻辑
Bazel 会将以下更改应用于工具链的功能,以实现向后兼容性:
- 将
legacy_compile_flags
功能移至工具链顶部 - 将
default_compile_flags
功能移至工具链顶部 - 在工具链顶部添加
dependency_file
(如果不存在)功能 - 在工具链顶部添加
pic
(如果不存在)功能 - 在工具链顶部添加
per_object_debug_info
(如果不存在)功能 - 在工具链顶部添加
preprocessor_defines
(如果不存在)功能 - 在工具链顶部添加
includes
(如果不存在)功能 - 在工具链顶部添加
include_paths
(如果不存在)功能 - 在工具链顶部添加
fdo_instrument
(如果不存在)功能 - 在工具链顶部添加
fdo_optimize
(如果不存在)功能 - 在工具链顶部添加
cs_fdo_instrument
(如果不存在)功能 - 在工具链顶部添加
cs_fdo_optimize
(如果不存在)功能 - 在工具链顶部添加
fdo_prefetch_hints
(如果不存在)功能 - 在工具链顶部添加
autofdo
(如果不存在)功能 - 在工具链顶部添加
build_interface_libraries
(如果不存在)功能 - 在工具链顶部添加
dynamic_library_linker_tool
(如果不存在)功能 - 在工具链顶部添加
symbol_counts
(如果不存在)功能 - 在工具链顶部添加
shared_flag
(如果不存在)功能 - 在工具链顶部添加
linkstamps
(如果不存在)功能 - 在工具链顶部添加
output_execpath_flags
(如果不存在)功能 - 在工具链顶部添加
runtime_library_search_directories
(如果不存在)功能 - 在工具链顶部添加
library_search_directories
(如果不存在)功能 - 在工具链顶部添加
archiver_flags
(如果不存在)功能 - 在工具链顶部添加
libraries_to_link
(如果不存在)功能 - 在工具链顶部添加
force_pic_flags
(如果不存在)功能 - 在工具链顶部添加
user_link_flags
(如果不存在)功能 - 在工具链顶部添加
legacy_link_flags
(如果不存在)功能 - 在工具链顶部添加
static_libgcc
(如果不存在)功能 - 在工具链顶部添加
fission_support
(如果不存在)功能 - 在工具链顶部添加
strip_debug_symbols
(如果不存在)功能 - 在工具链顶部添加
coverage
(如果不存在)功能 - 在工具链顶部添加
llvm_coverage_map_format
(如果不存在)功能 - 在工具链顶部添加
gcc_coverage_map_format
(如果不存在)功能 - 向工具链底部添加
fully_static_link
(如果不存在)功能 - 向工具链底部添加
user_compile_flags
(如果不存在)功能 - 向工具链底部添加
sysroot
(如果不存在)功能 - 向工具链底部添加
unfiltered_compile_flags
(如果不存在)功能 - 向工具链底部添加
linker_param_file
(如果不存在)功能 - 向工具链底部添加
compiler_input_flags
(如果不存在)功能 - 向工具链底部添加
compiler_output_flags
(如果不存在)功能
此处列出的功能很长。我们计划在完成 Starlark 中的交叉工具后删除它们。如有兴趣了解,请参阅 CppActionConfigs 中的实现;对于生产工具链,请考虑添加 no_legacy_features
以使工具链更加独立。