Skip to main content
Bazel is NativeLink’s primary build system, providing hermetic builds, strict dependency management, and remote execution capabilities. This guide covers the complete Bazel development workflow.

Why Bazel?

NativeLink uses Bazel because it provides:
  • Hermetic Builds - Reproducible builds with explicit dependencies
  • Remote Execution - Distributed builds using NativeLink itself
  • Strict Caching - Efficient incremental builds
  • Multi-Language - Unified build system for Rust and other languages
  • CI Parity - Same build system used in production and CI

Setup

Prerequisites

1

Install Bazelisk

Bazelisk automatically downloads and manages the correct Bazel version:
# Via Nix (if using Nix environment)
nix profile install nixpkgs#bazelisk

# Via Homebrew (macOS)
brew install bazelisk

# Via npm
npm install -g @bazel/bazelisk

# Verify installation
bazel --version
2

Install Clang/LLVM

NativeLink requires Clang for C++ compilation:
# Ubuntu/Debian
sudo apt install clang lld

# Arch Linux
sudo pacman -S clang lld

# macOS
xcode-select --install

# Via Nix
nix profile install nixpkgs#clang
3

Configure Environment

Set the C compiler:
export CC=clang
Add to your shell profile (.bashrc, .zshrc, etc.) for persistence.

Building

Basic Build Commands

1

Build Main Binary

bazel build //:nativelink

# Binary location
./bazel-bin/nativelink --help
2

Build All Targets

bazel build //...
3

Build Specific Crate

bazel build //nativelink-config
bazel build //nativelink-store
bazel build //nativelink-worker
4

Build with Debug Info

bazel build --config=debug //:nativelink

Build Configurations

NativeLink defines several build configurations in .bazelrc:
# Build with debug symbols and no optimizations
bazel build --config=debug //:nativelink

Platform-Specific Builds

# Default Linux build (uses musl for static linking)
bazel build //:nativelink

# The build uses blake3 hash function
# See .bazelrc:16

Testing

Running Tests

1

Run All Tests

# Run all tests in the workspace
bazel test //...

# Tests automatically run rustfmt and clippy checks
# See .bazelrc:167-169
2

Run Unit Tests

# Run unit test suite
bazel test :unit_tests

# This runs tests for:
# - nativelink-config
# - nativelink-error
# - nativelink-macro
# - nativelink-scheduler
# - nativelink-service
# - nativelink-store
# - nativelink-util
# - nativelink-worker
# See BUILD.bazel:77-90
3

Run Documentation Tests

# Run doctests for all crates
bazel test :doctests

# See BUILD.bazel:92-105
4

Run Specific Tests

# Test specific crate
bazel test //nativelink-store:unit_test

# Run with verbose output
bazel test //nativelink-store:unit_test --test_output=all

Test Output

Bazel shows errors by default. Configure test output:
# Show all output
bazel test //... --test_output=all

# Show errors only (default)
bazel test //... --test_output=errors

# Show no output
bazel test //... --test_output=summary
Test results are in bazel-testlogs/.

Code Quality

Formatting

1

Check Formatting

Formatting is automatically checked during bazel test:
# Tests include format checks
bazel test //...
2

Fix Formatting

# Run rustfmt on all Rust files
bazel run --config=rustfmt @rules_rust//:rustfmt

Linting

1

Run Clippy

Clippy runs automatically with tests:
# Clippy checks are included in test runs
bazel test //...

# See .bazelrc:168 for clippy output group configuration
2

Clippy Configuration

Clippy settings are in:
  • clippy.toml - Clippy-specific configuration
  • Cargo.toml - Workspace lints (synced to .bazelrc)
  • .bazelrc:102-161 - Clippy flags

Rust Lints

NativeLink enforces strict Rust lints. The lints in Cargo.toml are synchronized to Bazel via generate-bazel-rc:
# Lints are automatically applied
# See .bazelrc:56-162 for the complete list

# Key denied lints:
# - future_incompatible
# - unsafe_op_in_unsafe_fn
# - unused_imports
# - clippy::todo
# - clippy::unwrap_used (currently allowed, being fixed)

Documentation

Building Documentation

1

Build All Docs

# Build documentation for all crates
bazel build :docs

# Output in bazel-bin/*/doc/
2

Build Specific Crate Docs

bazel build //nativelink-config:docs
bazel build //nativelink-store:docs
bazel build //nativelink-worker:docs
3

Run Doc Tests

bazel test :doctests

Opening Documentation

# Build and open in browser (use helper script or open manually)
bazel build :docs
open bazel-bin/nativelink/doc/nativelink/index.html

Remote Execution

NativeLink supports building itself with remote execution:

Remote Cache

# Use NativeLink as remote cache
bazel build --config=self_test //:nativelink

# Uses cache at grpc://127.0.0.1:50051
# See .bazelrc:47

Remote Execution

# Execute builds remotely
bazel build --config=self_execute //:nativelink

# Uses executor at grpc://127.0.0.1:50052
# See .bazelrc:49-51

Local Remote Execution (LRE)

When using the Nix development shell, LRE configurations are automatically generated:
# LRE config is in lre.bazelrc
# Generated by the flake module
# See .bazelrc:203 and flake.nix:416-425

IDE Integration

rust-analyzer Setup

1

Generate Project Configuration

bazel run @rules_rust//tools/rust_analyzer:gen_rust_project
This creates rust-project.json in the repository root.
2

Configure rust-analyzer

For VS Code, create .vscode/settings.json:
{
  "rust-analyzer.linkedProjects": ["rust-project.json"]
}
3

Auto-Generate on Open (VS Code)

Create .vscode/tasks.json:
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Generate rust-project.json",
      "command": "bazel",
      "args": ["run", "@rules_rust//tools/rust_analyzer:gen_rust_project"],
      "options": {
        "cwd": "${workspaceFolder}"
      },
      "group": "build",
      "problemMatcher": [],
      "presentation": {
        "reveal": "never",
        "panel": "dedicated"
      },
      "runOptions": {
        "runOn": "folderOpen"
      }
    }
  ]
}
Regenerate rust-project.json whenever you add new files or dependencies.

Advanced Usage

Custom Bazelrc Files

NativeLink loads several .bazelrc files:
# Main configuration
.bazelrc

# Environment-specific (auto-generated by Nix)
lre.bazelrc        # Local remote execution config
darwin.bazelrc     # macOS-specific settings
nixos.bazelrc      # NixOS-specific paths
nativelink.bazelrc # NativeLink cache access

# User customization (not committed)
user.bazelrc       # Your personal settings
See .bazelrc:203-215 for import order.

Build Performance

# Bazel automatically caches build results
# Clear cache if needed:
bazel clean

# Remove all build outputs:
bazel clean --expunge

Debugging Builds

# Verbose output
bazel build //... -s

# Show command lines
bazel build //... --subcommands

# Explain why target was rebuilt
bazel build //... --explain=explain.txt

# Show dependency graph
bazel query --output=graph //... > graph.dot

Troubleshooting

Ensure Clang is installed and in PATH:
export CC=clang
bazel build //:nativelink
Fix formatting:
bazel run --config=rustfmt @rules_rust//:rustfmt
Then run tests again:
bazel test //...
Clear the test cache:
bazel clean --expunge
bazel test //...
Clean build artifacts:
# Remove build outputs
bazel clean

# Remove everything including downloaded dependencies
bazel clean --expunge

# Check disk usage
bazel info output_base
du -sh $(bazel info output_base)
Regenerate the rust-project.json:
bazel run @rules_rust//tools/rust_analyzer:gen_rust_project
Restart your IDE after regeneration.

Bazel Resources

Bazel Documentation

Official Bazel documentation and guides

rules_rust

Rust rules for Bazel

Bazelisk

Bazel version manager

Buildbuddy

Bazel build and test UI

Next Steps

Development with Cargo

Use Cargo for faster iteration

Development with Nix

Reproducible development environment

Build docs developers (and LLMs) love