概览
要使用正确的选项调用编译器,Bazel 需要 编译器内部结构,例如 include 目录和重要标志。 换句话说,Bazel 需要一个简化的编译器模型来了解其工作原理。
Bazel 需要具备以下知识:
- 编译器是否支持 thinLTO、模块、动态链接或 PIC(位置无关代码)。
- 所需工具(例如 gcc、ld、ar、objcopy 等)的路径。
- 内置系统包含目录。Bazel 需要这些信息来验证源文件中包含的所有头文件是否已在
BUILD
文件中正确声明。 - 默认的 sysroot。
- 编译、链接、归档时要使用哪些标志。
- 应针对支持的编译模式(opt、dbg、fastbuild)使用哪些标志。
- 创建编译器专门需要的变量。
如果编译器支持多个架构,则 Bazel 需要配置 。
CcToolchainConfigInfo
是一个提供程序,可提供必要的精细级别来配置 Bazel 的 C++ 规则的行为。默认情况下
Bazel 会自动为您的构建配置 CcToolchainConfigInfo
,但您可以
可以选择手动配置为此,你需要一条 Starlark 规则
提供 CcToolchainConfigInfo
,并且您需要指向
的 toolchain_config
属性
cc_toolchain
添加到您的规则中。
您可以通过调用CcToolchainConfigInfo
cc_common.create_cc_toolchain_config_info()
。
您可以在 @rules_cc//cc:cc_toolchain_config_lib.bzl
中找到此过程中所需的所有结构体的 Starlark 构造函数。
当 C++ 目标进入分析阶段时,Bazel 会选择合适的
cc_toolchain
基于 BUILD
文件进行定位,并获取
CcToolchainConfigInfo
cc_toolchain.toolchain_config
属性。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 只需使用--cpu
即可从cc_toolchain_suite.toolchains
属性中选择相应的条目。如果未指定任何标志,则 Bazel 会检查宿主系统,
--cpu
值。请参阅 检查机制代码。
选择工具链后,相应的 feature
和 action_config
Starlark 规则中的对象来控制 build 的配置(即
)。借助这些消息,您可以在不修改 Bazel 二进制文件的情况下在 Bazel 中实现完整的 C++ 功能。C++ 规则支持多个唯一操作(详细说明)
Bazel 源代码中的方法。
功能
功能是指需要命令行标志、操作、对执行环境的约束条件或依赖项更改的实体。功能
可以非常简单,例如允许 BUILD
文件选择
标志,例如 treat_warnings_as_errors
,或者与 C++ 规则和
将新的编译操作和输入添加到编译中,例如
header_modules
或 thin_lto
。
理想情况下,CcToolchainConfigInfo
应包含一个功能列表,其中每个功能都由一个或多个标志组组成,每个标志组都定义了适用于特定 Bazel 操作的标志列表。
功能通过名称指定,这允许将 Starlark 规则配置与 Bazel 版本完全解耦。换句话说,只要 CcToolchainConfigInfo
配置不需要使用新功能,Bazel 版本就不会影响其行为。
您可以通过以下某种方式启用某项功能:
- 地图项的
enabled
字段设置为true
。 - Bazel 或规则所有者明确启用该功能。
- 用户通过
--feature
Bazel 选项或features
规则启用该功能 属性。
功能之间可能存在相互依赖关系,并且可能取决于命令行标志、BUILD
文件设置和其他变量。
特征关系
通常,依赖项是直接通过 Bazel 进行管理的,Bazel 只会强制执行要求并管理构建中定义的功能固有的冲突。借助工具链规范,您可以使用更精细的约束条件,直接在用于管理功能支持和扩展的 Starlark 规则中使用。它们是:
限制条件 | 说明 |
requires = [ feature_set (features = [ 'feature-name-1', 'feature-name-2' ]), ] |
特征级。只有指定的必需项支持该功能
功能。例如,如果某个功能仅在
某些构建模式(opt 、dbg 或
fastbuild )。如果 `requires` 包含多个 `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 ,则添加到 build 命令
指定features 中的所有特征集的特征
已启用,并且 not_features 中指定的所有功能
一组被停用。
如果未指定 with_features ,系统将无条件地对指定的每个操作应用标志集。
|
操作
借助操作,您可以灵活地修改操作的执行环境,而无需假定操作的运行方式。一个
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 传播到 genrules。
|
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()
构造函数接受以下参数:
字段 | 说明 |
path
|
相关工具的路径(相对于当前位置)。 |
with_features
|
功能集列表,必须满足其中至少一个条件,此工具才能应用。 |
对于给定的 action_config
,只有一个 tool
会将其工具路径和执行要求应用于 Bazel 操作。已选择工具
方法是遍历 action_config
上的 tools
属性,直到工具
具有与功能配置匹配的 with_feature
集
(请参阅本页前面的特征关系)
)。您应在工具列表末尾使用默认值
对应的功能。
用法示例
可以结合使用特性和操作来实现 Bazel 操作
具有多样化的跨平台语义的特征。例如,调试符号生成在
macOS 要求在编译操作中生成符号,然后调用
在关联操作期间创建经过压缩的 dsym 归档文件的专用工具,以及
然后解压缩该归档,以生成应用软件包和 .plist
文件的大小
使用 Bazel 时,此过程可以改为按如下方式实现,其中 unbundle-debuginfo
是 Bazel 操作:
load("@rules_cc//cc:defs.bzl", "ACTION_NAMES")
action_configs = [
action_config (
action_name = ACTION_NAMES.cpp_link_executable,
tools = [
tool(
with_features = [
with_feature(features=["generate-debug-symbols"]),
],
path = "toolchain/mac/ld-with-dsym-packaging",
),
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"],
),
]
在 Linux 中,此功能的实现方式完全不同,
fission
或 Windows 版,它会生成 .pdb
文件。例如,基于 fission
的调试符号生成的实现可能如下所示:
load("@rules_cc//cc:defs.bzl", "ACTION_NAMES")
action_configs = [
action_config (
name = ACTION_NAMES.cpp_compile,
tools = [
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
允许您将标记捆绑到不同的组中,以便
特定用途。您可以在 中使用预定义的变量来指定标志,
标记值内,当将标记添加到
build 命令。例如:
flag_group (
flags = ["%{output_execpath}"],
)
在这种情况下,该标志的内容将替换为输出文件路径 操作。
标志组会按照其显示顺序展开到构建命令中 。
对于添加到 build 命令时需要使用不同值重复的标志,标志组可以迭代类型为 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++ 规则所需的构建变量、功能和其他信息的参考信息。
CcToolchainConfigInfo 编译变量
以下是 CcToolchainConfigInfo
build 变量的参考。
变量 | 操作 | 说明 |
source_file
|
compile | 要编译的源文件。 |
input_file
|
strip | 要去除的制品。 |
output_file
|
编译, 剥离 | 编译输出。 |
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 ) 文件。
|
stripopts
|
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 属性。
|
linkstamp_paths
|
链接 | 用于提供链接标记路径的构建变量。 |
force_pic
|
链接 | 如果存在此变量,则表示 PIC/PIE 代码应该 (已传递 Bazel 选项“--force_pic”)。 |
strip_debug_symbols
|
链接 | 如果存在此变量,则表示应剥离调试符号。 |
is_cc_test
|
链接 | 当前操作为“cc_test ”时为真
关联操作,否则为 false。
|
is_using_fission
|
编译, 链接, compile, link | 如果存在此变量,则表示存在裂解(每个对象的调试信息)
已启用。调试信息将改为存储在 .dwo 文件中
的 .o 文件,以及编译器和链接器需要知道这一点。
|
fdo_instrument_path
|
编译、链接 | 存储 FDO 插桩配置文件的目录的路径。 |
fdo_profile_path
|
compile | FDO 配置文件的路径。 |
fdo_prefetch_hints_path
|
compile | 缓存预提取配置文件的路径。 |
cs_fdo_instrument_path
|
编译、链接 | 用于存储上下文感知型 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
(如果不存在)功能添加到工具链的顶部 - 将
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
,以使工具链更加独立。