利用平台进行构建

7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bazel 为建模平台工具链提供复杂的支持。若要将其与实际项目集成,需要代码所有者、规则维护者和核心 Bazel 开发者之间进行细致的协作。

本页总结了平台的用途,并展示了如何使用这些平台构建应用。

tl;dr:Bazel 的平台和工具链 API 可用,但在所有语言规则、select() 和其他旧版引用都更新之前,并非在所有位置都可用。这项工作仍在进行中。最终所有 build 都将基于平台。 请阅读下文,了解您的 build 适合哪种类型。

如需更正式的文档,请参阅:

背景

我们引入了平台工具链,以对软件项目如何针对不同机器进行标准化处理,以及如何使用合适的语言工具进行构建。

这是 Bazel 中相对较新的一个功能。我们之所以提出这个想法,是因为观察到语言维护者已经在以临时且不兼容的方式执行此操作。例如,C++ 规则使用 --cpu--crosstool_top 来设置 build 的目标 CPU 和 C++ 工具链。这两种方法都无法正确对“平台”进行建模。过去尝试这样做会导致构建不当且不准确。这些标志也不控制 Java 编译,因为 Java 编译已经通过 --java_toolchain 演化了自己的独立接口。

Bazel 适用于大型、多语言、多平台项目。这需要对这些概念提供更具原则性的支持,包括鼓励语言和项目互操作性的清晰 API。这些新 API 就是为此而生。

迁移

平台和工具链 API 仅在项目实际使用它们时才有效。这并不简单,因为项目的规则逻辑、工具链、依赖项和 select() 都必须支持它们。这需要仔细规划迁移顺序,以确保所有项目及其依赖项正常运行。

例如,Bazel 的 C++ 规则支持平台。但 Apple 规则则不然。您的 C++ 项目可能不关注 Apple。但其他系统可能。因此,针对所有 C++ build 全局启用平台尚不安全。

本页的其余部分将介绍此迁移顺序,以及您的项目如何以及何时可以适应。

目标

当所有项目都使用以下格式构建时,Bazel 的平台迁移完成:

bazel build //:myproject --platforms=//:myplatform

这意味着:

  1. 项目使用的规则可以从 //:myplatform 推断出正确的工具链。
  2. 项目依赖项使用的规则可以从 //:myplatform 推断出正确的工具链。
  3. 或者,项目依赖于您支持的 //:myplatform或者您的项目支持旧版 API(例如 --crosstool_top)。
  4. //:myplatform 引用了 CPUOS 的 [通用声明][通用平台声明]{: .external},以及支持自动跨项目兼容性的其他通用概念。
  5. 所有相关项目的 select() 均理解 //:myplatform 隐含的机器属性。
  6. //:myplatform 应在一个清晰且可重复使用的位置进行定义:如果平台是项目专有的,则应在项目的代码库中进行定义;否则,应在所有可能使用此平台的项目都能找到的位置进行定义。

实现此目标后,旧 API 将被移除。然后,这将成为项目选择平台和工具链的标准方式。

我是否应使用平台?

如果您只想构建或交叉编译项目,则应遵循该项目的官方文档。

如果您是项目、语言或工具链维护者,最终会希望支持新 API。是等到全球迁移完成还是提前选择启用,取决于您的具体价值 / 费用需求:

  • 您可以select()或根据您关注的确切属性(而非 --cpu 等硬编码标志)选择工具链。例如,多个 CPU 可以支持相同的指令集
  • 更准确的 build。如果您在上述示例中使用 --cpu 执行 select(),然后添加支持相同指令集的新 CPU,select() 将无法识别新 CPU。但平台上的 select() 仍然准确无误。
  • 简化用户体验。所有项目都支持:--platforms=//:myplatform。无需在命令行中使用多个特定于语言的标志。
  • 语言设计更简单。所有语言共用一个通用 API,用于定义工具链、使用工具链以及为平台选择正确的工具链。
  • 如果目标与目标平台不兼容,则可以在构建和测试阶段跳过这些目标。

费用

  • 尚不支持平台的依赖项项目可能无法自动与您的项目搭配使用。
  • 使它们正常运行可能需要额外的临时维护
  • 新 API 和旧版 API 共存时,需要提供更细致的用户指南,以免造成混淆。
  • 常见属性(如 OSCPU)的规范定义仍在不断完善中,可能需要额外的初始贡献。
  • 特定于语言的工具链的规范定义仍在不断发展,可能需要额外的初始贡献。

API 审核

platformconstraint_value 目标的集合:

platform(
    name = "myplatform",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:arm",
    ],
)

constraint_value 是机器属性。同一“种类”的值将划分到共同的 constraint_setting 下:

constraint_setting(name = "os")
constraint_value(
    name = "linux",
    constraint_setting = ":os",
)
constraint_value(
    name = "mac",
    constraint_setting = ":os",
)

toolchainStarlark 规则。其属性声明语言的工具(例如 compiler = "//mytoolchain:custom_gcc")。其提供程序会将此信息传递给需要使用这些工具进行构建的规则。

工具链会声明它们可以针对的机器 (target_compatible_with = ["@platforms//os:linux"]) 以及能够运行其工具的机器 (exec_compatible_with = ["@platforms//os:mac"]) 的 constraint_value

构建 $ bazel build //:myproject --platforms=//:myplatform 时,Bazel 会自动选择可以在构建机器上运行的工具链,并为 //:myplatform 构建二进制文件。这称为“工具链解析”。

您可以在 WORKSPACE 中使用 register_toolchains 注册一组可用工具链,也可以在命令行中使用 --extra_toolchains 注册。

如需深入了解,请点击此处

状态

当前平台支持因语言而异。Bazel 的所有主要规则都迁移到了平台但此过程需要一些时间。这主要有三个原因:

  1. 必须更新规则逻辑,以便从新的工具链 API (ctx.toolchains) 获取工具信息,并停止读取 --cpu--crosstool_top 等旧版设置。这种方法相对简单。

  2. 工具链维护人员必须定义工具链,并使其可供用户访问(在 GitHub 代码库和 WORKSPACE 条目中)。这在技术上很简单,但必须巧妙地组织起来,以保持轻松的用户体验。

    平台定义也是必要的(除非您是针对运行 Bazel 的同一台机器进行构建)。通常,项目应自行定义平台。

  3. 现有项目必须迁移。select()转场效果也必须迁移。这是最大的挑战。这对于多语言项目而言尤其具有挑战性(如果所有语言都无法读取 --platforms,则可能会失败)。

如果要设计新的规则集,您必须从一开始就支持平台。这会自动使您的规则与其他规则和项目兼容,并且随着平台 API 变得越来越普及,规则的价值也会越来越大。

常见平台属性

各项目通用的平台属性(如 OSCPU)应在标准的集中位置声明。这有利于实现跨项目和跨语言兼容性。

例如,如果 MyAppconstraint_value@myapp//cpus:arm 上有 select(),而 SomeCommonLib@commonlib//constraints:arm 上有 select(),则这两个库会触发其“arm”模式,但所用条件不兼容。

全局通用属性在 @platforms 代码库中声明(因此,上述示例的规范标签为 @platforms//cpu:arm)。语言通用属性应在其相应语言的代码库中声明。

默认平台

通常,项目所有者应定义明确的平台,以描述他们要针对哪些类型的机器构建应用。然后,使用 --platforms 触发这些事件。

如果未设置 --platforms,则 Bazel 默认使用表示本地构建机器的 platform。该代码是在 @local_config_platform//:host 中自动生成的,因此无需明确定义。它会将本地计算机的 OSCPU@platforms 中声明的 constraint_value 进行映射。

C++

在设置 --incompatible_enable_cc_toolchain_resolution 时,Bazel 的 C++ 规则使用平台选择工具链 (#7260)。

这意味着您可以用以下代码配置 C++ 项目:

bazel build //:my_cpp_project --platforms=//:myplatform

而不再使用旧版:

bazel build //:my_cpp_project` --cpu=... --crosstool_top=...  --compiler=...

如果您的项目是纯 C++ 项目,并且非 C++ 项目不依赖于它,那么只要您的 select转换兼容,您就可以安全地使用平台。如需更多指导,请参阅 #7260配置 C++ 工具链

此模式默认处于停用状态。这是因为 Apple 项目仍然使用 --cpu--crosstool_top 配置 C++ 依赖项(示例)。因此,这取决于迁移到平台的 Apple 规则。

Java

Bazel 的 Java 规则使用平台。

这取代了旧版标志 --java_toolchain--host_java_toolchain--javabase--host_javabase

如需了解如何使用配置标志,请参阅 Bazel 和 Java 手册。如需了解详情,请参阅设计文档

如果您仍在使用旧版标志,请按照问题 #7849 中的迁移流程操作。

Android

在您设置 --incompatible_enable_android_toolchain_resolution 时,Bazel 的 Android 规则会使用平台来选择工具链。

默认情况下,此功能处于停用状态。但迁移工作正在稳步推进。

Apple

Bazel 的 Apple 规则尚不支持平台选择 Apple 工具链。

它们也不支持平台启用的 C++ 依赖项,因为它们使用旧版 --crosstool_top 设置 C++ 工具链。在迁移此方案之前,您可以将 Apple 项目与支持 Platom 的 C++ 与平台映射示例)混合使用。

其他语言

如果您要为新语言设计规则,请使用平台来选择该语言的工具链。有关详细的演示,请参阅工具链文档

select()

项目可以在 constraint_value 目标select(),但不能在完整平台上select()。这是有意为之,以便 select() 支持尽可能多的机器。除非有更具体的理由,否则包含特定于 ARM 的源代码的库应支持所有ARM 提供支持的机器。

如需选择一个或多个 constraint_value,请使用以下语法:

config_setting(
    name = "is_arm",
    constraint_values = [
        "@platforms//cpu:arm",
    ],
)

这相当于传统上在 --cpu 上进行选择:

config_setting(
    name = "is_arm",
    values = {
        "cpu": "arm",
    },
)

如需了解详情,请点击此处

--cpu--crosstool_top 等平台上的 select 无法识别 --platforms。将项目迁移到平台时,您必须将其转换为 constraint_values,或使用平台映射在迁移窗口期内支持这两种样式。

转场效果

Starlark 转换会更改构建图的部分标志。如果您的项目使用设置了 --cpu--crossstool_top 或其他旧版标志的转换,则读取 --platforms 的规则将不会看到这些更改。

将项目迁移到平台时,您必须将 return { "//command_line_option:cpu": "arm" } 等更改转换为 return { "//command_line_option:platforms": "//:my_arm_platform" },或者使用平台映射,以便在迁移期内支持这两种样式。

当前如何使用平台

如果您只想构建或交叉编译项目,则应遵循项目的官方文档。与平台集成的方式和时机以及集成的价值由语言和项目维护人员决定。

如果您是项目、语言或工具链维护者,并且您的 build 默认不使用平台,则有以下三种方法(除了等待全局迁移)可供选择:

  1. 切换您项目所用语言对应的“使用平台”标志(如果有),然后进行所需的测试,看看您关注的项目是否有效。

  2. 如果您关注的项目仍依赖于 --cpu--crosstool_top 等旧版标志,请将这些标志与 --platforms 搭配使用:

    bazel build //:my_mixed_project --platforms==//:myplatform --cpu=... --crosstool_top=...

    这会有一些维护费用(您必须手动确保设置一致)。但是,如果没有叛变的转换,这应该会起作用。

  3. 通过将 --cpu 样式设置映射到相应的平台(反之亦然),编写平台映射以支持这两种样式。

平台映射

平台映射是一个临时 API,可让依托平台的逻辑和依托旧版的逻辑在后者的废弃期限内在同一 build 中共存。

平台映射是 platform() 与一组相应的旧版标志(或反过来)的映射。例如:

platforms:
  # Maps "--platforms=//platforms:ios" to "--cpu=ios_x86_64 --apple_platform_type=ios".
  //platforms:ios
    --cpu=ios_x86_64
    --apple_platform_type=ios

flags:
  # Maps "--cpu=ios_x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
  --cpu=ios_x86_64
  --apple_platform_type=ios
    //platforms:ios

  # Maps "--cpu=darwin --apple_platform_type=macos" to "//platform:macos".
  --cpu=darwin
  --apple_platform_type=macos
    //platforms:macos

Bazel 使用此方式来保证所有设置(包括基于平台的设置和旧版设置)在整个构建过程中始终如一地应用,包括通过转换

默认情况下,Bazel 会从工作区根目录中的 platform_mappings 文件中读取映射。您还可以设置 --platform_mappings=//:my_custom_mapping

如需了解完整详情,请点击此处

问题

如需常规支持以及有关迁移时间表的问题,请与 bazel-discuss@googlegroups.com 或相应规则的所有者联系。

如需讨论平台/工具链 API 的设计和演变,请与 bazel-dev@googlegroups.com 联系。

另请参阅