blob: d001840816332ad29e5b3631d4342c738e6cb491 [file] [view]
# 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
- **Self-Compiling Go (DooD)**: The repository rule downloads the upstream `bazel-toolchains` source repository and dynamically compiles `rbe_configs_gen` inside a sibling `golang:1.21` Docker container via the host's Docker socket (Docker-out-of-Docker). This eliminates any local Go compiler installation requirements.
- **On-Demand Auto-Detection**: The compiled Go binary launches your target toolchain container, mounts your running host Bazel binary, auto-detects the compiler and JDK runtimes inside the container sandbox, and extracts the generated C++ and Java toolchain configurations directly into Bazel's `output_base`.
- **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. 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}" "$@"
```