| --- |
| layout: documentation |
| title: Adapting Bazel Rules for Remote Execution |
| --- |
| |
| # Adapting Bazel Rules for Remote Execution |
| |
| Remote execution allows Bazel to execute actions on a separate platform, such as |
| a datacenter. A [gRPC protocol](https://github.com/googleapis/googleapis/blob/master/google/devtools/remoteexecution/v1test/remote_execution.proto) |
| is currently in development. You can try remote execution with [bazel-buildfarm](https://github.com/bazelbuild/bazel-buildfarm), |
| an open-source project that aims to provide a distributed remote execution |
| platform. This document is intended for Bazel users writing custom build and |
| test rules who want to understand the requirements for Bazel rules in |
| the context of remote execution. |
| |
| This document uses the following terminology when referring to different |
| environment types or *platforms*: |
| |
| * **Host platform** - where Bazel runs. |
| * **Execution platform** - where Bazel actions run. |
| * **Target platform** - where the build outputs (and some actions) run. |
| |
| ## Overview |
| |
| When configuring a Bazel build for remote execution, you must follow the |
| guidelines described in this document to ensure the build executes remotely |
| error-free. This is due to the nature of remote execution, namely: |
| |
| * **Isolated build actions.** Build tools do not retain state and dependencies |
| cannot leak between them. |
| |
| * **Diverse execution environments.** Local build configuration is not always |
| suitable for remote execution environments. |
| |
| This document describes the issues that can arise when implementing custom Bazel |
| build and test rules for remote execution and how to avoid them. It covers the |
| following topics: |
| |
| * [Invoking build tools through toolchain rules](#invoking-build-tools-through-toolchain-rules) |
| * [Managing dependencies](#managing-dependencies) |
| * [Managing platform-dependent binaries](#managing-platform-dependent-binaries) |
| * [Managing configure-style WORKSPACE rules](#managing-configure-style-workspace-rules) |
| |
| ## Invoking build tools through toolchain rules |
| |
| A Bazel toolchain rule is a configuration provider that tells a build rule what |
| build tools, such as compilers and linkers, to use and how to configure them |
| using parameters defined by the rule's creator. A toolchain rule allows build |
| and test rules to invoke build tools in a predictable, preconfigured manner |
| that's compatible with remote execution. For example, use a toolchain rule |
| instead of invoking build tools via the `PATH`, `JAVA_HOME`, or other local |
| variables that may not be set to equivalent values (or at all) in the remote |
| execution environment. |
| |
| Toolchain rules currently exist for Bazel build and test rules for |
| [Scala](https://github.com/bazelbuild/rules_scala/blob/master/scala/scala_toolch |
| ain.bzl), |
| [Rust](https://github.com/bazelbuild/rules_rust/blob/master/rust/toolchain.bzl), |
| and [Go](https://github.com/bazelbuild/rules_go/blob/master/go/toolchains.rst), |
| and new toolchain rules are under way for other languages and tools such as |
| [bash](https://docs.google.com/document/d/e/2PACX-1vRCSB_n3vctL6bKiPkIa_RN_ybzoAccSe0ic8mxdFNZGNBJ3QGhcKjsL7YKf-ngVyjRZwCmhi_5KhcX/pub). |
| If a toolchain rule does not exist for the tool your rule uses, consider |
| [creating a toolchain rule](/toolchains.html#creating-a-toolchain-rule). |
| |
| ## Managing implicit dependencies |
| |
| If a build tool can access dependencies across build actions, those actions will |
| fail when remotely executed because each remote build action is executed |
| separately from others. Some build tools retain state across build actions and |
| access dependencies that have not been explicitly included in the tool |
| invocation, which will cause remotely executed build actions to fail. |
| |
| For example, when Bazel instructs a stateful compiler to locally build _foo_, |
| the compiler retains references to foo's build outputs. When Bazel then |
| instructs the compiler to build _bar_, which depends on _foo_, without |
| explicitly stating that dependency in the BUILD file for inclusion in the |
| compiler invocation, the action executes successfully as long as the same |
| compiler instance executes for both actions (as is typical for local execution). |
| However, since in a remote execution scenario each build action executes a |
| separate compiler instance, compiler state and _bar_'s implicit dependency on |
| _foo_ will be lost and the build will fail. |
| |
| To help detect and eliminate these dependency problems, Bazel 0.14.1 offers the |
| local Docker sandbox, which has the same restrictions for dependencies as remote |
| execution. Use the sandbox to prepare your build for remote execution by |
| identifying and resolving dependency-related build errors. See [Troubleshooting Bazel Remote Execution with Docker Sandbox](/remote-execution-sandbox.html) |
| for more information. |
| |
| ## Managing platform-dependent binaries |
| |
| Typically, a binary built on the host platform cannot safely execute on an |
| arbitrary remote execution platform due to potentially mismatched dependencies. |
| For example, the SingleJar binary supplied with Bazel targets the host platform. |
| However, for remote execution, SingleJar must be compiled as part of the process |
| of building your code so that it targets the remote execution platform. (See the |
| [target selection logic](https://github.com/bazelbuild/bazel/blob/130aeadfd660336572c3da397f1f107f0c89aa8d/tools/jdk/BUILD#L115).) |
| |
| Do not ship binaries of build tools required by your build with your source code |
| unless you are sure they will safely run in your execution platform. Instead, do |
| one of the following: |
| |
| * Ship or externally reference the source code for the tool so that it can be |
| built for the remote execution platform. |
| |
| * Pre-install the tool into the remote execution environment (for example, a |
| toolchain container) if it's stable enough and use toolchain rules to run it |
| in your build. |
| |
| ## Managing `configure`-style WORKSPACE rules |
| |
| Bazel's `WORKSPACE` rules can be used for probing the host platform for tools |
| and libraries required by the build, which, for local builds, is also Bazel's |
| execution platform. If the build explicitly depends on local build tools and |
| artifacts, it will fail during remote execution if the remote execution platform |
| is not identical to the host platform. |
| |
| The following actions performed by `WORKSPACE` rules are not compatible with |
| remote execution: |
| |
| * **Building binaries.** Executing compilation actions in `WORKSPACE` rules |
| results in binaries that are incompatible with the remote execution platform |
| if different from the host platform. |
| |
| * **Installing `pip` packages.** `pip` packages installed via `WORKSPACE` |
| rules require that their dependencies be pre-installed on the host platform. |
| Such packages, built specifically for the host platform, will be |
| incompatible with the remote execution platform if different from the host |
| platform. |
| |
| * **Symlinking to local tools or artifacts.** Symlinks to tools or libraries |
| installed on the host platform created via `WORKSPACE` rules will cause the |
| build to fail on the remote execution platform as Bazel will not be able to |
| locate them. Instead, create symlinks using standard build actions so that |
| the symlinked tools and libraries are accessible from Bazel's `runfiles` |
| tree. Do not use [`repository_ctx.symlink`](skylark/lib/repository_ctx.html#symlink) |
| to symlink target files outside of the external repo directory. |
| |
| * **Mutating the host platform.** Avoid creating files outside of the Bazel |
| `runfiles` tree, creating environment variables, and similar actions, as |
| they may behave unexpectedly on the remote execution platform. |
| |
| To help find potential non-hermetic behavior you can use [Workspace rules log](/workspace-log.md). |
| |
| If an external dependency executes specific operations dependent on the host |
| platform, we recommend splitting those operations between `WORKSPACE` and build |
| rules as follows: |
| |
| * **Platform inspection and dependency enumeration.** These operations are |
| safe to execute locally via `WORKSPACE` rules, which can check which |
| libraries are installed, download packages that must be built, and prepare |
| required artifacts for compilation. For remote execution, these rules must |
| also support using pre-checked artifacts to provide the information that |
| would normally be obtained during host platform inspection. Pre-checked |
| artifacts allow Bazel to describe dependencies as if they were local. Use |
| conditional statements or the `--override_repository` flag for this. |
| |
| * **Generating or compiling target-specific artifacts and platform mutation**. |
| These operations must be executed via regular build rules. Actions that |
| produce target-specific artifacts for external dependencies must execute |
| during the build. |
| |
| To more easily generate pre-checked artifacts for remote execution, you can use |
| `WORKSPACE` rules to emit generated files. You can run those rules on each new |
| execution environment, such as inside each toolchain container, and check the |
| outputs of your remote execution build in to your source repo to reference. |
| |
| For example, for Tensorflow's rules for [`cuda`](https://github.com/tensorflow/tensorflow/blob/master/third_party/gpus/cuda_configure.bzl) |
| and [`python`](https://github.com/tensorflow/tensorflow/blob/master/third_party/py/python_configure.bzl), |
| the `WORKSPACE` rules produce the following [`BUILD files`](https://github.com/tensorflow/tensorflow/tree/master/third_party/toolchains/cpus/py). |
| For local execution, files produced by checking the host environment are used. |
| For remote execution, a [conditional statement](https://github.com/tensorflow/tensorflow/blob/master/third_party/py/python_configure.bzl#L304) |
| on an environment variable allows the rule to use files that are checked into |
| the repo. The `BUILD` files declare [`genrules`](https://github.com/tensorflow/tensorflow/blob/master/third_party/py/python_configure.bzl#L84\) |
| that can run both locally and remotely, and perform the necessary processing |
| that was previously done via `repository_ctx.symlink` as shown [here](https://github.com/tensorflow/tensorflow/blob/d1ba01f81d8fa1d0171ba9ce871599063d5c7eb9/third_party/gpus/cuda_configure.bzl#L730). |