针对远程执行调整 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 远程执行问题

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

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

除非您确定构建工具将在您的执行平台中安全运行,否则请不要在源代码中包含构建工具所需的二进制文件二进制文件。请改为执行以下操作之一:

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

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

管理配置式 WORKSPACE 规则

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

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

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

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

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

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

为帮助查找潜在的非封闭行为,您可以使用 Workspace 规则日志

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

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

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

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

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

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