利用平台进行构建

报告问题 查看源代码 每夜 build · 8.0 · 7.4 · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

本页面总结了平台的用途,并介绍了如何使用平台进行构建。

要点:Bazel 的平台和工具链 API 已可供使用,但在所有语言规则、select() 和其他旧版参考都更新完毕之前,这些 API 无法在所有环境中运行。这项工作仍在进行中。最终,所有 build 都将基于平台。请阅读下文,了解您的 build 适合哪种类型。

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

背景

平台工具链的引入是为了标准化软件项目以不同的机器为目标平台并使用合适的语言工具进行构建的方式。

这是 Bazel 中相对较新的一个功能。我们之所以提出这个想法,是因为观察到语言维护者已经在以临时且不兼容的方式执行此操作。例如,C++ 规则使用 --cpu--crosstool_top 来设置 build 的目标 CPU 和 C++ 工具链。这两种方法都无法正确对“平台”进行建模。过去曾尝试这样做,但导致的 build 不准确且不易使用。这些标志也不控制 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")。其提供程序会将此信息传递给需要使用这些工具进行构建的规则。

工具链会声明其可定位到的机器的 constraint_value (target_compatible_with = ["@platforms//os:linux"]),以及其工具可在其上运行的机器的 constraint_value (exec_compatible_with = ["@platforms//os:mac"])。

构建 $ 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 项目与支持平台的 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 使用此机制来保证在整个 build 过程中(包括通过转换)一致地应用所有设置(包括平台级设置和旧版设置)。

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

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

问题

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

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

另请参阅