Skip to main content

Toolchain Management

RBS provides a hermetic toolchain system that automatically downloads, caches, and manages all external tools and runtimes your project needs. No manual installation required — RBS handles everything.

Why Hermetic Toolchains?

  • Reproducibility: Every developer and CI server uses the exact same tool versions.
  • No System Dependencies: No need to install Java, Python, Node.js, or any other tool system-wide.
  • Version Isolation: Different projects can use different versions of the same tool without conflict.
  • Automatic Caching: Tools are downloaded once and cached for future use.

How It Works

  1. You declare toolchains in WORKSPACE.rbs.
  2. RBS automatically downloads the correct version for your platform.
  3. Tools are cached in .rbs/toolchains/.
  4. Build rules use the managed toolchains — never system-installed tools.
.rbs/toolchains/
├── java/
│   └── jdk-17.0.11/          # Managed JDK
├── nodejs/
│   └── node-v20.11.0/        # Managed Node.js
├── python/
│   └── python-3.11.7/        # Managed Python
└── custom/
    └── protoc-25.1/           # Custom managed tool

Declaring Toolchains

Language Toolchains

RBS provides built-in toolchain rules for common languages:
# Java/JVM Toolchain
load("@rbs//java/toolchain.rbs", "java_toolchain")
java_toolchain(name = "java", version = "17.0.11")

# Node.js Toolchain
load("@rbs//nodejs/toolchain.rbs", "nodejs_toolchain")
nodejs_toolchain(name = "nodejs", version = "20.11.0")

# Python Toolchain
load("@rbs//python/toolchain.rbs", "python_toolchain")
python_toolchain(name = "python", version = "3.11.7")

# Go Toolchain
load("@rbs//go/toolchain.rbs", "go_toolchain")
go_toolchain(name = "go", version = "1.21.5")

# Kotlin Toolchain
load("@rbs//kotlin/toolchain.rbs", "kotlin_toolchain")
kotlin_toolchain(name = "kotlin", version = "1.9.22")

Custom Toolchains

For tools not covered by built-in rules, use the low-level primitives:

native.http_file — Download a Single File

# Download a single executable
native.http_file(
    name = "protoc",
    url = select({
        "//platforms:darwin_arm64": "https://github.com/protocolbuffers/protobuf/releases/download/v25.1/protoc-25.1-osx-aarch_64.zip",
        "//platforms:linux_amd64": "https://github.com/protocolbuffers/protobuf/releases/download/v25.1/protoc-25.1-linux-x86_64.zip",
    }),
    sha256 = select({
        "//platforms:darwin_arm64": "abc123...",
        "//platforms:linux_amd64": "def456...",
    }),
    executable = True,
)

native.http_archive — Download and Extract an Archive

# Download and extract a tar.gz or zip archive
native.http_archive(
    name = "terraform",
    url = select({
        "//platforms:darwin_arm64": "https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_darwin_arm64.zip",
        "//platforms:linux_amd64": "https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_linux_amd64.zip",
    }),
    sha256 = select({
        "//platforms:darwin_arm64": "abc...",
        "//platforms:linux_amd64": "def...",
    }),
    strip_prefix = "",           # Strip leading directory from archive
    build_file = None,           # Optional BUILD.rbs for the extracted contents
)

native.tool_binary — Declare a Tool from an Archive

# Declare an executable within a downloaded archive
native.tool_binary(
    name = "terraform_bin",
    archive = "@terraform",           # Reference to http_archive
    binary_path = "terraform",        # Path to executable within the archive
    tool_type = "terraform",          # Tool type identifier
)

register_toolchain — Register for Use in Rules

# Register the toolchain so rules can reference it
register_toolchain(
    name = "terraform_toolchain",
    tool = "@terraform_bin",
    type = "terraform",
)

Using Toolchains in Rules

In Built-in Rules

Built-in rules automatically use the appropriate registered toolchain:
load("@rbs//java/rules.rbs", "java_binary")

# Automatically uses the java_toolchain declared in WORKSPACE.rbs
java_binary(
    name = "server",
    srcs = glob(["src/**/*.java"]),
    main = "com.example.Server",
)

In Custom Rules

Access toolchains via ctx.tools:
def _my_rule_impl(ctx):
    dirs = ctx.bin.create_dirs()
    
    # Copy the Java toolchain into the target's output
    java_home = ctx.tools.copy(
        toolchain = "java",
        destination = dirs.toolchains + "/java",
    )
    
    # Use the toolchain binary
    ctx.actions.run(
        executable = java_home + "/bin/java",
        arguments = ["-jar", "app.jar"],
    )

Platform-Aware Downloads

RBS automatically selects the correct download URL based on the current platform using select():
NODEJS_URLS = select({
    "//platforms:darwin_arm64": "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-arm64.tar.gz",
    "//platforms:darwin_amd64": "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-x64.tar.gz",
    "//platforms:linux_arm64": "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-arm64.tar.gz",
    "//platforms:linux_amd64": "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.gz",
    "//platforms:windows_amd64": "https://nodejs.org/dist/v20.11.0/node-v20.11.0-win-x64.zip",
})

Available Platform Labels

PlatformLabel
macOS ARM64//platforms:darwin_arm64
macOS x64//platforms:darwin_amd64
Linux ARM64//platforms:linux_arm64
Linux x64//platforms:linux_amd64
Windows x64//platforms:windows_amd64

Toolchains vs External Dependencies

It’s important to understand the difference between toolchains and external dependencies:
AspectToolchainsExternal Dependencies
WhatBuild tools (compilers, runtimes, linters)Language packages (libraries, frameworks)
ExamplesJDK, Node.js, Python, protocSpring Boot, Express, Django, React
Defined withjava_toolchain, http_archive, tool_binaryjava_repository, nodejs_repository, py_repository
Stored in.rbs/toolchains/.rbs/external-deps/
Used byBuild rules (compilation, execution)Application code (imports, runtime)

Example: Both Together

# TOOLCHAIN: The Java runtime itself
load("@rbs//java/toolchain.rbs", "java_toolchain")
java_toolchain(name = "java", version = "17.0.11")

# EXTERNAL DEPENDENCIES: Java libraries your code uses
load("@rbs//java/dependencies.rbs", "java_repository")
java_repository(
    name = "spring_boot_web",
    artifact = "org.springframework.boot:spring-boot-starter-web",
    version = "3.2.0",
)
load("@rbs//java/rules.rbs", "java_binary")

java_binary(
    name = "server",
    srcs = glob(["src/**/*.java"]),
    main = "com.example.Server",
    # Uses the java_toolchain (JDK) to compile
    # Uses the spring_boot_web dependency at runtime
    deps = ["@external://spring_boot_web"],
)

Caching and Offline Builds

Cache Location

All toolchains are cached in .rbs/toolchains/:
.rbs/toolchains/
├── java/
│   └── jdk-17.0.11/
│       ├── bin/
│       ├── lib/
│       └── ...
├── nodejs/
│   └── node-v20.11.0/
│       ├── bin/
│       ├── lib/
│       └── ...
└── custom/
    └── protoc-25.1/
        └── bin/protoc

Offline Mode

Once all toolchains are downloaded, RBS can run without network access:
# First run: downloads toolchains (requires network)
rbs build //...

# Subsequent runs: fully offline
rbs build //...   # Uses cached toolchains

Cache Management

# View workspace info including toolchain status
rbs workspace

# Clean all caches (forces re-download)
rm -rf .rbs/toolchains/

Integrity Verification

Use SHA256 checksums to verify downloaded toolchains:
native.http_archive(
    name = "terraform",
    url = "https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_darwin_arm64.zip",
    sha256 = "e4add092a54ff6febd3325d1e0c109c9e590dc6c38f8bb7f9632e4e6bcca99d4",
)
If the checksum doesn’t match, RBS will refuse to use the download, protecting against tampered artifacts.