C++ 工具链配置

<ph type="x-smartling-placeholder"></ph> 报告问题 <ph type="x-smartling-placeholder"></ph> 查看来源 每晚 · 7.2 条 · 7.1敬上 · 7.0 · 6.5 · 6.4

概览

要使用正确的选项调用编译器,Bazel 需要 编译器内部结构,例如 include 目录和重要标志。 换句话说,Bazel 需要一个简化的编译器模型才能理解 运行。

Bazel 需要具备以下知识:

  • 编译器是否支持 thinLTO、模块、动态链接或 PIC (位置无关代码)。
  • 所需工具(例如 gcc、ld、ar、objcopy 等)的路径。
  • 内置系统包含目录。Bazel 需要使用它们来验证 在 BUILD 文件。
  • 默认的 sysroot。
  • 用于编译、链接和归档的标志。
  • 用于支持的编译模式(opt、dbg、快速构建)的标志。
  • 创建编译器专门所需的变量。

如果编译器支持多个架构,则 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

当 C++ 目标进入分析阶段时,Bazel 会选择合适的 cc_toolchain 基于 BUILD 文件进行定位,并获取 CcToolchainConfigInfo cc_toolchain.toolchain_config 属性。cc_toolchain 目标 通过 CcToolchainProvider 将这些信息传递给 C++ 目标。

例如,编译或链接操作,由诸如 cc_binarycc_library 需要以下信息:

  • 要使用的编译器或链接器
  • 编译器/链接器的命令行标记
  • 通过 --copt/--linkopt 选项传递的配置标志
  • 环境变量
  • 执行操作的沙盒中所需的工件

除沙盒中所需的工件外,上述所有信息均 在 cc_toolchain 指向的 Starlark 目标中指定。

要传送到沙盒的工件在 cc_toolchain 中声明。 目标。例如,借助 cc_toolchain.linker_files 属性,您可以 指定要装入沙盒的链接器二进制文件和工具链库。

工具链选择

工具链选择逻辑的运作方式如下:

  1. 用户在 BUILD 文件中指定了 cc_toolchain_suite 目标,并点了 使用 --crosstool_top选项

  2. cc_toolchain_suite 目标引用了多个工具链。通过 --cpu--compiler 标志的值决定了 工具链,可以仅基于 --cpu 标志值进行选择,或者 基于联合 --cpu | --compiler 值。选择流程 如下:

    • 如果指定了 --compiler 选项,则 Bazel 会 cc_toolchain_suite.toolchains 中的相应条目 属性替换为 --cpu | --compiler。如果 Bazel 找不到 相应的条目,则会抛出错误。

    • 如果未指定 --compiler 选项,则 Bazel 会 cc_toolchain_suite.toolchains 中的相应条目 属性只使用 --cpu

    • 如果未指定任何标志,则 Bazel 会检查宿主系统, --cpu 值。请参阅 检查机制代码

选择工具链后,相应的 featureaction_config Starlark 规则中的对象来控制 build 的配置(即 )。这些消息允许 而无需修改 Bazel 中的成熟 C++ 功能 Bazel 二进制文件。C++ 规则支持多个唯一操作(详细说明) Bazel 源代码中的方法。

功能

功能是指需要命令行标记、操作 执行环境限制条件,或依赖项更改。功能 可以非常简单,例如允许 BUILD 文件选择 标志,例如 treat_warnings_as_errors,或者与 C++ 规则和 将新的编译操作和输入添加到编译中,例如 header_modulesthin_lto

理想情况下,CcToolchainConfigInfo 包含一个特征列表,其中每个特征 功能由一个或多个标志组组成,每个组定义一个标志列表 适用于特定 Bazel 操作的不同选项

地图项按名称指定,以便与 Starlark 完全分离 根据 Bazel 版本创建规则配置。换句话说,Bazel 版本 影响 CcToolchainConfigInfo 配置的行为,前提是这些 无需使用新功能。

您可以通过以下某种方式启用某项功能:

  • 地图项的 enabled 字段设置为 true
  • Bazel 或规则所有者明确启用它。
  • 用户通过 --feature Bazel 选项或 features 规则启用该功能 属性。

功能可以相互依赖,具体取决于命令行标志、BUILD 文件 设置和其他变量。

特征关系

依赖项通常直接通过 Bazel 管理, 功能特性的内在冲突 build 中定义的新值。工具链规范支持更精细的 可直接在管理地图项的 Starlark 规则内使用的约束条件 支持和扩展。它们是:

限制条件 说明
requires = [
   feature_set (features = [
       'feature-name-1',
       'feature-name-2'
   ]),
]
特征级。只有指定的必需项支持该功能, 功能。例如,如果某个功能仅在 某些构建模式(optdbgfastbuild)。如果 `requires` 包含多个 `feature_set` 如果满足任意一个 `feature_set`,则该特征受支持 (启用所有指定的功能时)。
implies = ['feature']

特征级。此特征隐含指定的特征。 启用某个功能也会隐式启用它隐含的所有功能 (即,以递归方式运行)。

此外,它还可以将常见的部分功能 例如消毒程序的常见组成部分暗示 功能无法停用。

provides = ['feature']

特征级。表示此地图项是几个相互独立的 独有的备选功能例如,所有的消毒程序都可以 指定 provides = ["sanitizer"]

这样可以在用户询问时列出替代项,从而改善错误处理情况 同时实现两项或多项互斥功能。

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 传播到 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 是描述 Bazel 的 Starlark 结构体 通过指定要在操作期间调用的工具(二进制文件)和 标志。这些标志会对操作的 执行。

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_availableexpand_if_not_available expand_if_trueexpand_if_falseexpand_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 参考文档

本部分提供了有关 build 变量、功能 成功配置 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) 文件。
stripotps strip stripopts 的序列。
legacy_compile_flags compile 旧版标志序列 CROSSTOOL 字段,例如 compiler_flagoptional_compiler_flagcxx_flagoptional_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 链接 提供 linkstamp 路径的 build 变量。
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 编译, 链接, compile, link 存储 FDO 插桩配置文件的目录的路径。
fdo_profile_path compile FDO 配置文件的路径。
fdo_prefetch_hints_path compile 缓存预提取配置文件的路径。
cs_fdo_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++ 运行时 模式,在动态链接模式下则动态加载。制品 在 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 中的 Crosstool 完成。对于有兴趣的读者,请参阅 CppActionConfigs, 对于生产工具链,请考虑添加 no_legacy_features 工具链的独立性