为远程执行调整 Bazel 规则

本页面适用于编写自定义构建和测试规则的 Bazel 用户,这些用户希望了解远程执行环境中 Bazel 规则的要求。

远程执行允许 Bazel 在单独的平台(例如数据中心)上执行操作。Bazel 使用 gRPC 协议进行远程执行。您可尝试使用 bazel-buildfarm(一个旨在提供分布式远程执行平台的开源项目)进行远程执行。

本页面在提及不同的环境类型或平台时,使用以下术语:

  • 主机平台 - 运行 Bazel 的位置。
  • 执行平台 - 运行 Bazel 操作的位置。
  • 目标平台 - 运行构建输出(和某些操作)的位置。

概览

为远程执行配置 Bazel 构建时,您必须遵循本页面中所述的指南,以确保构建在远程执行时不会出现错误。这是由于远程执行的性质所致,即:

  • 隔离的构建操作 -构建工具不会保留状态,并且依赖项不会在它们之间泄露。

  • 多样化的执行环境。本地构建配置并非始终适用于远程执行环境。

本页面介绍了为远程执行实现自定义 Bazel 构建和测试规则时可能出现的问题,以及如何避免这些问题。它包含以下主题:

通过工具链规则调用构建工具

Bazel 工具链规则是一种配置提供程序,用于告知构建规则要使用哪些构建工具(如编译器和链接器),以及如何使用规则创建者定义的参数对其进行配置。工具链规则允许构建和测试规则以与远程执行兼容的可预测预配置方式调用构建工具。例如,使用工具链规则,而不要通过 PATHJAVA_HOME 或其他可能在远程执行环境中未设置为等效值(或根本未设置为)的局部变量调用构建工具。

目前,针对 ScalaRustGo 的 Bazel 构建和测试规则存在工具链规则,并且正在针对其他语言和工具(如 bash)开发新的工具链规则。如果您的规则使用的工具不存在工具链规则,请考虑创建工具链规则

管理隐式依赖项

如果构建工具可以跨构建操作访问依赖项,则这些操作在远程执行时会失败,因为每个远程构建操作都是彼此分开执行的。某些构建工具会在工具调用中保留尚未明确包含的构建操作和访问依赖项的状态,这会导致远程执行的构建操作失败。

例如,当 Bazel 指示有状态编译器在本地构建 foo 时,编译器会保留对 foo 的构建输出的引用。然后,当 Bazel 指示编译器构建依赖于 foobar 时,但未在 BUILD 文件中明确说明该依赖项以包含在编译器调用中,只要同一编译器实例针对这两项操作都执行了此操作,该操作就会成功执行(正如本地执行的典型情况)。但是,由于在远程执行场景中,每个构建操作都会执行单独的编译器实例,因此编译器状态和 barfoo 的隐式依赖将丢失,并且构建将会失败。

为了帮助检测和消除这些依赖项问题,Bazel 0.14.1 提供了本地 Docker 沙盒,该沙盒对依赖项的限制与远程执行相同。使用沙盒识别和解决与依赖项相关的构建错误,为远程执行做好准备。如需了解详情,请参阅使用 Docker Sandbox 排查 Bazel Remote Execution 问题

管理依赖于平台的二进制文件

通常,由于依赖项可能不匹配,在主机平台上构建的二进制文件无法在任意远程执行平台上安全地执行。例如,Bazel 随附的 SingleJar 二进制文件以主机平台为目标。不过,对于远程执行,必须在构建代码的过程中编译 SingleJar,使其以远程执行平台为目标。(请参阅目标选择逻辑。)

请勿随源代码一起提供构建所需的构建工具二进制文件,除非您确定它们将在执行平台中安全运行。请改为执行以下操作之一:

  • 提供或外部引用工具的源代码,以便针对远程执行平台构建该工具。

  • 如果工具足够稳定,请将其预安装到远程执行环境(例如工具链容器)中,并使用工具链规则在构建中运行它。

管理配置样式的 WORKSPACE 规则

Bazel 的 WORKSPACE 规则可用于探测构建所需的工具和库的主机平台,对于本地构建,它们也是 Bazel 的执行平台。如果构建明确依赖于本地构建工具和工件,那么当远程执行平台与主机平台不完全相同时,构建将在远程执行期间失败。

WORKSPACE 规则执行的以下操作与远程执行不兼容:

  • 构建二进制文件。在 WORKSPACE 规则中执行编译操作会导致二进制文件与远程执行平台不兼容(如果与主机平台不同)。

  • 安装 pip 软件包。通过 WORKSPACE 规则安装的 pip 软件包要求其依赖项预安装在主机平台上。此类软件包是专门为主机平台构建的,如果与主机平台不同,则将与远程执行平台不兼容。

  • 以符号链接到本地工具或工件。如果符号链接指向通过 WORKSPACE 规则创建的主机平台上安装的工具或库,则会导致在远程执行平台上构建失败,因为 Bazel 无法找到它们。请改为使用标准构建操作创建符号链接,以便从 Bazel 的 runfiles 树访问符号链接的工具和库。请勿使用 repository_ctx.symlink 对外部代码库目录之外的目标文件进行符号链接。

  • 更改主机平台。请避免在 Bazel runfiles 树之外创建文件、创建环境变量和类似操作,因为它们可能会在远程执行平台上出现意外行为。

为帮助找出潜在的非封闭行为,您可以使用工作区规则日志

如果外部依赖项执行依赖于主机平台的特定操作,您应在 WORKSPACE 和构建规则之间拆分这些操作,如下所示:

  • 平台检查和依赖项枚举 -这些操作可以通过 WORKSPACE 规则在本地安全执行,该规则可以检查安装了哪些库、下载必须构建的软件包,以及准备编译所需的工件。对于远程执行,这些规则还必须支持使用预检查的工件来提供通常在主机平台检查期间获得的信息。通过预先检查的工件,Bazel 可以描述依赖项,就像它们是本地依赖项一样。为此,请使用条件语句或 --override_repository 标志。

  • 生成或编译特定于目标的工件和平台变更。这些操作必须通过常规构建规则执行。为外部依赖项生成特定于目标的工件的操作必须在构建期间执行。

为了更轻松地生成预检查的工件以用于远程执行,您可以使用 WORKSPACE 规则来发出生成的文件。您可以在每个新的执行环境中运行这些规则(例如在每个工具链容器内),并将远程执行构建的输出检查到源代码库中以供参考。

例如,对于 Tensorflow 的 cudapython 规则,WORKSPACE 规则会生成以下 BUILD files。对于本地执行,系统会使用通过检查主机环境生成的文件。 对于远程执行,环境变量上的条件语句允许规则使用签入代码库中的文件。

BUILD 文件声明了 genrules,它可以在本地和远程运行,并执行之前通过 repository_ctx.symlink 完成的必要处理,如此处所示。