本页面面向编写自定义构建和测试规则的 Bazel 用户,适用于希望了解远程执行环境中 Bazel 规则要求的用户。
借助远程执行功能,Bazel 可以在单独的平台(例如数据中心)上执行操作。Bazel 使用 gRPC 协议进行远程执行。您可以尝试使用 bazel-buildfarm 进行远程执行,该开源项目旨在提供分布式远程执行平台。
本页在提及不同的环境类型或平台时,使用以下术语:
- 宿主平台 - Bazel 的运行平台。
- 执行平台 - Bazel 操作的运行位置。
- 目标平台 - 构建输出(和某些操作)的运行位置。
概览
将 Bazel 构建配置为远程执行时,您必须遵循本页中所述的准则,以确保构建能够远程执行且不会出错。这是由于远程执行的性质所致,具体而言:
隔离的 build 操作。构建工具不会保留状态,并且依赖项无法在构建工具之间泄露。
多样化的执行环境。本地 build 配置并不总适用于远程执行环境。
本页介绍了在为远程执行实现自定义 Bazel build 和测试规则时可能出现的问题,以及如何避免这些问题。其中涵盖以下主题:
通过工具链规则调用构建工具
Bazel 工具链规则是一种配置提供程序,用于告知构建规则要使用哪些构建工具(例如编译器和链接器),以及如何使用规则创建者定义的参数对这些工具进行配置。借助工具链规则,构建和测试规则可以以可预测的预配置方式调用构建工具,该方式与远程执行兼容。例如,使用工具链规则,而不是通过 PATH
、JAVA_HOME
或其他可能未在远程执行环境中设置为等效值(或根本未设置为等效值)的本地变量调用构建工具。
目前,针对 Scala、Rust 和 Go 的 Bazel 构建和测试规则已提供工具链规则,并且正在为其他语言和工具(例如 bash)开发新的工具链规则。如果您的规则使用的工具没有工具链规则,请考虑创建工具链规则。
管理隐式依赖项
如果构建工具可以访问跨构建操作的依赖项,则这些操作在远程执行时会失败,因为每个远程构建操作都是独立于其他操作执行的。某些构建工具会跨构建操作保留状态,并访问未明确包含在工具调用中的依赖项,这会导致远程执行的构建操作失败。
例如,当 Bazel 指示有状态编译器在本地构建 foo 时,编译器会保留对 foo 的构建输出的引用。然后,当 Bazel 指示编译器构建依赖于 foo 的 bar 时,如果未在 BUILD 文件中明确声明该依赖项以将其包含在编译器调用中,只要这两个操作使用相同的编译器实例执行(本地执行时通常如此),该操作就会成功执行。但是,由于在远程执行场景中,每个 build 操作都会执行单独的编译器实例,因此编译器状态和 bar 对 foo 的隐式依赖项将丢失,并且 build 将失败。
为帮助检测和消除这些依赖项问题,Bazel 0.14.1 提供了本地 Docker 沙盒,其对依赖项的限制与远程执行相同。使用沙盒来识别和解决与依赖项相关的构建错误,为远程执行做好构建准备。如需了解详情,请参阅使用 Docker 沙盒排查 Bazel 远程执行问题。
管理平台专用二进制文件
通常,在宿主平台上构建的二进制文件无法在任意远程执行平台上安全执行,因为依赖项可能不匹配。例如,随 Bazel 提供的 SingleJar 二进制文件以主机平台为目标平台。不过,对于远程执行,必须在构建代码的过程中编译 SingleJar,以便其以远程执行平台为目标平台。(请参阅目标选择逻辑。)
请勿将 build 所需的构建工具的二进制文件与源代码一起分发,除非您确定这些二进制文件会在您的执行平台中安全运行。请改为执行以下操作之一:
分发或外部引用该工具的源代码,以便为远程执行平台构建该工具。
如果工具足够稳定,请将其预安装到远程执行环境(例如工具链容器)中,并使用工具链规则在 build 中运行该工具。
管理配置式 WORKSPACE 规则
Bazel 的 WORKSPACE
规则可用于探测主机平台,以查找构建所需的工具和库。对于本地构建,主机平台也是 Bazel 的执行平台。如果 build 明确依赖于本地 build 工具和工件,那么如果远程执行平台与主机平台不相同,则 build 将在远程执行期间失败。
WORKSPACE
规则执行的以下操作与远程执行不兼容:
构建二进制文件。如果与主机平台不同,在
WORKSPACE
规则中执行编译操作会导致二进制文件与远程执行平台不兼容。安装
pip
软件包。通过WORKSPACE
规则安装的pip
软件包需要在托管平台上预安装其依赖项。专为托管平台构建的此类软件包如果与托管平台不同,则与远程执行平台不兼容。对本地工具或工件进行符号链接。指向通过
WORKSPACE
规则创建并安装在主机平台上的工具或库的符号链接会导致远程执行平台上的构建失败,因为 Bazel 无法找到它们。请改用标准 build 操作创建符号链接,以便通过 Bazel 的runfiles
树访问符号链接的工具和库。请勿使用repository_ctx.symlink
将目标文件符号链接到外部代码库目录之外的位置。更改主机平台。避免在 Bazel
runfiles
树之外创建文件、创建环境变量和执行类似操作,因为这些操作可能会在远程执行平台上出现意外行为。
如需帮助查找潜在的非密封行为,您可以使用 Workspace 规则日志。
如果外部依赖项会执行取决于宿主平台的特定操作,您应在 WORKSPACE
和 build 规则之间拆分这些操作,如下所示:
平台检查和依赖项枚举。这些操作可以安全地通过
WORKSPACE
规则在本地执行,这些规则可以检查已安装的库、下载必须构建的软件包,以及准备编译所需的工件。对于远程执行,这些规则还必须支持使用预检查工件来提供通常在主机平台检查期间获取的信息。借助预检查工件,Bazel 可以像描述本地依赖项一样描述这些依赖项。为此,请使用条件语句或--override_repository
标志。生成或编译特定于目标平台的工件和平台变更。 这些操作必须通过常规 build 规则执行。为外部依赖项生成特定于目标的工件时,必须在构建期间执行操作。
如需更轻松地生成预检查工件以进行远程执行,您可以使用 WORKSPACE
规则发出生成的文件。您可以在每个新的执行环境(例如每个工具链容器内)上运行这些规则,并检查远程执行 build 的输出以供参考。
例如,对于 Tensorflow 针对 cuda
和 python
的规则,WORKSPACE
规则会生成以下 BUILD files
。对于本地执行,系统会使用通过检查主机环境生成的文件。对于远程执行,环境变量上的条件语句可让规则使用已签入代码库的文件。
BUILD
文件声明了可在本地和远程运行的 genrules
,并执行之前通过 repository_ctx.symlink
执行的必要处理,如此处所示。