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
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
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
Configure Environment
Set the C compiler: Add to your shell profile (.bashrc, .zshrc, etc.) for persistence.
Building
Basic Build Commands
Build Main Binary
bazel build //:nativelink
# Binary location
./bazel-bin/nativelink --help
Build Specific Crate
bazel build //nativelink-config
bazel build //nativelink-store
bazel build //nativelink-worker
Build with Debug Info
bazel build --config=debug //:nativelink
Build Configurations
NativeLink defines several build configurations in .bazelrc:
Debug Build
Release Build
Nightly Toolchain
Address Sanitizer
Thread Sanitizer
# Build with debug symbols and no optimizations
bazel build --config=debug //:nativelink
# Default Linux build (uses musl for static linking)
bazel build //:nativelink
# The build uses blake3 hash function
# See .bazelrc:16
# macOS builds require native compilation
bazel build //:nativelink
# macOS-specific config is in darwin.bazelrc
# Generated by the darwin flake module
# Windows builds use special configuration
bazel build --config=windows //:nativelink
# Enables runfiles and symlinks
# See .bazelrc:193-196
Testing
Running Tests
Run All Tests
# Run all tests in the workspace
bazel test //...
# Tests automatically run rustfmt and clippy checks
# See .bazelrc:167-169
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
Run Documentation Tests
# Run doctests for all crates
bazel test :doctests
# See BUILD.bazel:92-105
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
Check Formatting
Formatting is automatically checked during bazel test: # Tests include format checks
bazel test //...
Fix Formatting
# Run rustfmt on all Rust files
bazel run --config=rustfmt @rules_rust//:rustfmt
Linting
Run Clippy
Clippy runs automatically with tests: # Clippy checks are included in test runs
bazel test //...
# See .bazelrc:168 for clippy output group configuration
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
Build All Docs
# Build documentation for all crates
bazel build :docs
# Output in bazel-bin/*/doc/
Build Specific Crate Docs
bazel build //nativelink-config:docs
bazel build //nativelink-store:docs
bazel build //nativelink-worker:docs
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
Generate Project Configuration
bazel run @rules_rust//tools/rust_analyzer:gen_rust_project
This creates rust-project.json in the repository root.
Configure rust-analyzer
For VS Code, create .vscode/settings.json: {
"rust-analyzer.linkedProjects" : [ "rust-project.json" ]
}
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.
Disk Cache
Parallel Builds
Memory Usage
# Bazel automatically caches build results
# Clear cache if needed:
bazel clean
# Remove all build outputs:
bazel clean --expunge
# Control parallelism
bazel build --jobs=8 //...
# Auto-detect based on CPU cores (default)
bazel build //...
# Limit memory usage
bazel build --local_ram_resources=HOST_RAM * 0.5 //...
# Or set in user.bazelrc:
# build --local_ram_resources=8192
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
Build fails with 'Cannot find C++ compiler'
Ensure Clang is installed and in PATH: export CC = clang
bazel build //:nativelink
rustfmt or clippy checks fail
Fix formatting: bazel run --config=rustfmt @rules_rust//:rustfmt
Then run tests again:
Tests fail with cache poisoning
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 )
rust-analyzer not finding dependencies
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