利用平台进行构建

Bazel 为建模平台工具链提供了复杂的支持。要将此 API 与实际项目集成,需要代码所有者、规则维护者和核心 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。是等待全球迁移完成,还是提早选择实施,都取决于您具体的价值 / 费用需求:

  • 您可以对您关注的确切属性(而不是 --cpu 等硬编码标志)执行 select() 或选择工具链。例如,多个 CPU 可以支持同一指令集
  • 更多正确的 build。如果您在上述示例中使用 --cpu 执行 select(),然后添加了支持同一指令集的新 CPU,则 select() 无法识别新的 CPU。但平台上的 select() 依然准确。
  • 用户体验更简单。所有项目都了解:--platforms=//:myplatform。无需在命令行中使用多个特定于语言的标志。
  • 语言设计更简单。所有语言都共用一个通用 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"]) 以及可以运行其工具的机器 (exec_compatible_with = ["@platforms//os:mac"])。

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

您可以使用 register_toolchainsWORKSPACE 中注册可用的工具链集,也可以在命令行中使用 --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 中自动生成的,因此无需明确定义。它会使用 @platforms 中声明的 constraint_value 映射本地机器的 OSCPU

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 规则使用平台来选择工具链。

默认情况下,此功能处于未启用状态。但迁移工作进展顺利。

苹果

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

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

其他语言

如果要为新语言设计规则,请使用平台选择语言的工具链。如需查看良好的演示,请参阅工具链文档

select()

项目可以在 constraint_value 目标上执行 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 转换会改变 build 图的下部分标志。如果您的项目使用的转换设置了 --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

另请参阅