| # Dynamic RBE Toolchain Generator (`bazel_ci_rules`) |
| |
| This folder contains the unified, GCS-free, and Remote Build Execution (RBE) toolchain generator rule (`rbe_config`). |
| |
| It automates the generation of RBE toolchain configurations dynamically on-the-fly, eliminating the maintenance overhead of pre-generating and uploading configurations to Google Cloud Storage (GCS) buckets when new Bazel versions are released. |
| |
| --- |
| |
| ## 1. Design & Architectural Highlights |
| |
| - **Dual Execution Modes (`docker` vs `host`)**: Supports both standard Docker-out-of-Docker generation (default when `RBE_CONFIG_CONTAINER` is unset) and direct host environment generation (automatically enabled when `RBE_CONFIG_CONTAINER` is set). |
| - **Direct Host Toolchain Detection**: When `RBE_CONFIG_CONTAINER` is exported, `rbe_configs_gen` compiles natively on the host using `go build` and runs with `--exec_mode=host`, executing C++ and Java auto-detection (`@@rules_cc...`, `java -version`) against the host filesystem without Docker. The target container image is authoritative from the `RBE_CONFIG_CONTAINER` environment variable. |
| - **Decoupled Presets Rollout**: Standard environment configurations are stored in a public JSON file (`rules/rbe_presets.json`) on the `master` branch of this repository. At runtime, the repository rule dynamically downloads this file. If the CI maintainers update a container image tag or environment parameter on `master`, **all projects immediately receive the update without modifying their pinned ruleset commit hashes!** |
| |
| --- |
| |
| ## 2. Bzlmod Usage (`MODULE.bazel`) |
| |
| To configure RBE toolchains using Bzlmod (Bazel 8.0.0+), load and use the `rbe_config_extension` module extension: |
| |
| ### Option A: Using Standard Presets (Recommended) |
| Simply pass the name of a standard preset (e.g. `"ubuntu"`): |
| ```bazel |
| bazel_dep(name = "bazel_ci_rules", version = "2.0.0") |
| |
| # Load and instantiate the RBE config module extension |
| rbe = use_extension("@bazel_ci_rules//:rbe_config.bzl", "rbe_config_extension") |
| |
| rbe.config( |
| name = "rbe_ubuntu", # Standard target repository name |
| preset = "ubuntu", # Request the generic latest Ubuntu preset |
| ) |
| |
| use_repo(rbe, "rbe_ubuntu") |
| ``` |
| |
| ### Option B: Using Custom Specifications |
| If you are using a custom compiler container or customized settings, specify the target image and C++ compiler environment directly inside the tag: |
| ```bazel |
| bazel_dep(name = "bazel_ci_rules", version = "2.0.0") |
| |
| rbe = use_extension("@bazel_ci_rules//:rbe_config.bzl", "rbe_config_extension") |
| |
| rbe.config( |
| name = "rbe_ubuntu", |
| container = "gcr.io/my-custom/image:latest", |
| cpp_env = { |
| "CC": "gcc", |
| "ABI_LIBC_VERSION": "glibc_2.39", |
| "ABI_VERSION": "gcc", |
| "BAZEL_COMPILER": "gcc", |
| "BAZEL_HOST_SYSTEM": "i686-unknown-linux-gnu", |
| "BAZEL_TARGET_CPU": "k8", |
| "BAZEL_TARGET_LIBC": "glibc_2.39", |
| "BAZEL_TARGET_SYSTEM": "x86_64-unknown-linux-gnu", |
| "CC_TOOLCHAIN_NAME": "linux_gnu_x86" |
| } |
| ) |
| |
| use_repo(rbe, "rbe_ubuntu") |
| ``` |
| |
| --- |
| |
| ## 3. Legacy WORKSPACE Usage |
| |
| If your repository has not migrated to Bzlmod yet, load and call the `rbe_config` macro inside your `WORKSPACE` file: |
| |
| ### Option A: Using Standard Presets |
| ```python |
| load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") |
| |
| http_archive( |
| name = "bazel_ci_rules", |
| strip_prefix = "bazel_ci_rules-2.0.0", # Pre-packaged release prefix |
| url = "https://github.com/bazelbuild/continuous-integration/releases/download/rules-2.0.0/bazel_ci_rules-2.0.0.tar.gz", # Official 2.0.0 tarball release |
| ) |
| |
| load("@bazel_ci_rules//:rbe_config.bzl", "rbe_config") |
| |
| rbe_config( |
| name = "rbe_ubuntu", |
| preset = "ubuntu", # Request the standard 'ubuntu' preset dynamically |
| ) |
| ``` |
| |
| ### Option B: Using Custom Specifications |
| ```python |
| load("@bazel_ci_rules//:rbe_config.bzl", "rbe_config") |
| |
| rbe_config( |
| name = "rbe_ubuntu", |
| spec = { |
| "container": "gcr.io/my-custom/image:latest", |
| "cpp_env": { |
| "CC": "gcc", |
| "CC_TOOLCHAIN_NAME": "linux_gnu_x86", |
| # ... |
| } |
| } |
| ) |
| ``` |
| |
| --- |
| |
| ## 4. Running Builds on Remote Execution (RBE) |
| |
| Once your workspace is configured with the `rbe_ubuntu` repository, you can compile and test your targets remotely on GCP by passing the standard Remote Execution flags: |
| |
| ```bash |
| bazel build \ |
| --extra_toolchains=@rbe_ubuntu//config:cc-toolchain \ |
| --extra_execution_platforms=@rbe_ubuntu//config:platform \ |
| --host_platform=@rbe_ubuntu//config:platform \ |
| --platforms=@rbe_ubuntu//config:platform \ |
| --javabase=@rbe_ubuntu//java:jdk \ |
| --host_javabase=@rbe_ubuntu//java:jdk \ |
| --remote_executor=remotebuildexecution.googleapis.com \ |
| --remote_instance_name=projects/YOUR_GCP_PROJECT/instances/YOUR_RBE_INSTANCE \ |
| --google_default_credentials \ |
| //path/to:your_target |
| ``` |
| |
| ### Flag Descriptions: |
| - `--extra_toolchains=@rbe_ubuntu//config:cc-toolchain`: Registers the dynamically auto-detected C++ compiler toolchain. |
| - `--extra_execution_platforms` / `--platforms`: Configures Bazel to execute actions and target outputs inside the container's platform environment. |
| - `--javabase` / `--host_javabase=@rbe_ubuntu//java:jdk`: Resolves Java compilations and host execution using the JDK detected inside the container. |
| - `--remote_executor`: The gRPC endpoint of the Google Remote Build Execution service. |
| - `--remote_instance_name`: The RBE instance mapped specifically to your Google Cloud Platform (GCP) project. |
| - `--google_default_credentials`: Authenticates your remote requests natively using your Google Application Default Credentials (ADC) or system gcloud login. |
| |
| --- |
| |
| ## 5. Execution Modes (`docker` vs `host`) |
| |
| `rbe_config` supports two execution modes for toolchain auto-detection, automatically selected based on your environment: |
| |
| ### 5.1 Docker Mode (`--exec_mode=docker`, Default) |
| When `RBE_CONFIG_CONTAINER` is **unset** (e.g., on developer workstations running macOS or Linux): |
| - **Go Compilation**: `rbe_configs_gen` is compiled inside a sibling `golang:1.21` Docker container using Docker-out-of-Docker (`/var/run/docker.sock`). No local Go compiler is required. |
| - **Toolchain Detection**: `rbe_configs_gen` pulls and runs the target toolchain container image (from `preset` or `spec["container"]`), mounts host Bazel, and runs C++ and Java auto-detection inside the sandboxed container. |
| |
| ### 5.2 Host Mode (`--exec_mode=host`, Automatic in Container CI) |
| When the **`RBE_CONFIG_CONTAINER`** environment variable is exported (e.g., in containerized CI workers or Buildkite pipelines): |
| - **Automatic Activation**: `rbe_config` automatically switches to `host` mode. |
| - **Go Compilation**: `rbe_configs_gen` is compiled natively on the host using `go build` (requires Go installed in `PATH`; no Docker required). |
| - **Direct Host Detection**: `rbe_configs_gen` runs with `--exec_mode=host`, discovering C++ compilers (`gcc`/`clang`) and JDK runtimes directly from the host filesystem without invoking Docker. |
| - **Host Container Authority**: In `host` mode, because detection happens against the host filesystem, the RBE execution platform image in `@rbe_ubuntu//config:platform` is **authoritatively determined by `RBE_CONFIG_CONTAINER`** (overriding any requested preset container image to prevent ABI or toolchain discrepancies). |
| |
| --- |
| |
| ## 6. Advanced: Bazel Version & Binary Resolution Strategy |
| |
| To generate toolchain configurations, `rbe_config` must determine which Bazel version to target and potentially mount a host Bazel binary inside the compiler container. It resolves this using the following **four-tier precedence lookup strategy**: |
| |
| 1. **`RBE_CONFIG_BAZEL_PATH` Environment Variable**: *(Highest Precedence)* Explicitly forces the generator to use this host Bazel binary (passes `--host_bazel_path` to the generator). |
| 2. **`BAZEL_REAL` Environment Variable**: Forces the generator to use this real host Bazel binary (usually set by wrappers like Bazelisk) and passes it as `--host_bazel_path`. |
| 3. **`native.bazel_version` Detection**: Automatically detects the version string of the running Bazel (e.g., `"9.1.0"` or `"8.4.2"`). If found, it passes `--bazel_version` to `rbe_configs_gen`, which allows the generator to fetch the matching Bazel release inside the container cleanly without copying host binaries. |
| 4. **System `PATH` scan**: *(Fallback)* If no explicit path is set and version detection fails (e.g., on untagged development builds), it scans the host `PATH` for `bazel` and copies it into the container. |
| |
| If you run custom development layouts or want to force a specific Bazel binary override, choose one of the following methods: |
| |
| ### Method 1: Shell Export (Explicit Override) |
| Export the path to your executable in your active terminal session before running the build: |
| ```bash |
| export RBE_CONFIG_BAZEL_PATH=/path/to/your/custom_bazel |
| bazel build //... |
| ``` |
| |
| ### Method 2: Using a `tools/bazel` Wrapper (Recommended) |
| Creating a wrapper script at **`tools/bazel`** at the root of your workspace is the cleanest, zero-configuration way. Bazel and Bazelisk automatically execute this wrapper, which can capture the real running binary path (`BAZEL_REAL`). Our rule will automatically pick up `BAZEL_REAL` and prioritize it over `native.bazel_version`: |
| ```bash |
| #!/bin/bash |
| # tools/bazel |
| # This wrapper script is executed automatically by Bazelisk |
| |
| # BAZEL_REAL will be automatically detected and prioritized by rbe_config |
| exec "${BAZEL_REAL}" "$@" |
| ``` |