Bazel
Bazel for C/C++ projects: BUILD files, Bzlmod dependencies, toolchain registration, remote execution, dependency graph queries, and sandbox debugging. Grounded against Bazel 9.2.0, where Bzlmod is always enabled, all WORKSPACE logic is removed, and every C++ rule loads from @rules_cc.
Contract
| Field | Bound contract |
|---|---|
| Trigger | The task writes or debugs Bazel BUILD files, cc_library/cc_binary/cc_test rules, Bzlmod dependencies, toolchain registration, remote execution, sandbox failures, or bazel query/cquery/aquery dependency graphs. |
| Authority | Reversible local: writes only BUILD files, MODULE.bazel, .bzl files, and Bazel output directories (bazel-out, bazel-bin); rollback is version control plus bazel clean. No remote mutation. |
| Side effect | Local builds write to the Bazel output base; remote execution and remote cache flags contact a user-supplied endpoint. |
| Done | The requested targets build or test with bazel build/bazel test, or the blocker is reported with the failing command and output. |
Inputs
- Project layout (required): the source tree and where BUILD files should live.
- Bazel version (required if not inferrable): run
bazel version. Bazel 9 removed WORKSPACE and the native C++ rules; instructions below assume Bazel 9. - External dependencies (optional): library names; versions come from the Bazel Central Registry.
- Remote execution endpoint (optional): a user-supplied remote execution or cache service URL.
Procedure
- Lay out the workspace. Bazel 9 uses
MODULE.bazelas the only dependency file; there is no WORKSPACE. Put aBUILDfile in each package directory. Done when:MODULE.bazelexists at the root and each package has aBUILDfile.
my-project/
├── MODULE.bazel
├── BUILD
├── src/
│ ├── BUILD
│ └── main.cc
└── lib/
├── BUILD
├── mylib.cc
└── mylib.h
- Write BUILD rules. Load every C++ rule from
@rules_cc; the nativecc_library/cc_binary/cc_testrules were removed from Bazel 9. Done when: every target builds withbazel build.
# lib/BUILD
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
cc_library(
name = "mylib",
srcs = ["mylib.cc"],
hdrs = ["mylib.h"],
copts = ["-Wall", "-Wextra", "-std=c++23"],
visibility = ["//visibility:public"],
deps = [
"@abseil-cpp//absl/strings",
"//util:helpers",
],
)
cc_test(
name = "mylib_test",
srcs = ["mylib_test.cc"],
deps = [
":mylib",
"@googletest//:gtest_main",
],
)
# src/BUILD
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "main",
srcs = ["main.cc"],
deps = ["//lib:mylib"],
linkopts = ["-lpthread"],
)
bazel build //src:main
bazel build //...
bazel test //lib:mylib_test
bazel run //src:main -- arg1 arg2
# Binary lands at bazel-bin/src/main
- Declare dependencies in
MODULE.bazelwithbazel_dep. Versions come from the Bazel Central Registry; the pins below are the versions Bazel 9.2.0 itself depends on. Done when:bazel mod graphresolves without errors.
# MODULE.bazel
module(name = "my_project", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "abseil-cpp", version = "20250814.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel mod graph # full resolved dependency graph
bazel mod deps # direct and indirect module deps
bazel mod tidy # fix up MODULE.bazel declarations
- Query the dependency graph. Done when: the query answers the question asked.
bazel query "deps(//src:main)" # transitive deps
bazel query "rdeps(//..., //lib:mylib)" # reverse deps
bazel query "somepath(//src:main, //lib:mylib)" # dependency path
bazel cquery "deps(//src:main)" --output=files # configuration-aware
bazel cquery "//lib:mylib" --output=build # effective rule
bazel aquery "//src:main" # action graph: flags, inputs, outputs
- Register toolchains with platforms. Define a
platformwith constraint values, atoolchainbinding a toolchain target to a toolchain type, thenregister_toolchainsinMODULE.bazel. The C++ toolchain configuration API lives in rules_cc undercc/toolchains; seereferences/bazel-cpp-toolchain.md. Done when:bazel build --platforms=//platforms:<name>selects the registered toolchain.
# platforms/BUILD
platform(
name = "linux_x86_64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
)
# MODULE.bazel
register_toolchains("//toolchains:my_cc_toolchain")
- Configure remote execution or caching when the user supplies an endpoint. Done when: the build runs against the endpoint or the flag is rejected and reported.
bazel build //... \
--remote_executor=grpc://build.example.com:50051 \
--remote_instance_name=main
# Cache only, no remote execution
bazel build //... --remote_cache=grpc://cache.example.com:9092
- Debug sandbox failures. Done when: the failing action is identified and its missing input or disallowed write is fixed.
bazel build //src:main --sandbox_debug # show sandbox inputs/outputs
bazel build //src:main --verbose_failures --sandbox_debug
bazel build //src:main --spawn_strategy=local # bypass sandbox to isolate it
bazel build //src:main --subcommands # print each command run
Common sandbox causes: "No such file or directory" means a missing data or srcs entry; "Permission denied" means a write outside the sandbox, fixed by routing outputs through declared rule outputs.
Failure and recovery
bazel buildfails on a missing load: addload("@rules_cc//cc:defs.bzl", ...)for the rule used; Bazel 9 has no native C++ rules.bazel_depversion not found: runbazel mod graphto see the error, then pick a version listed in the Bazel Central Registry.- Sandbox error persists after adding inputs: reproduce with
--spawn_strategy=local; if the local run passes, the sandbox is missing a declared input. - Remote execution unreachable: drop
--remote_executorand rebuild locally; report the endpoint failure rather than retrying blindly. - Query returns empty: check the target pattern with
bazel query "//..."first; an empty pattern means the package path is wrong. - Migration from WORKSPACE: run the Bzlmod migration path (
bazel mod tidyafter declaring deps); do not recreate WORKSPACE, Bazel 9 ignores it.
Output
Working BUILD files and MODULE.bazel declarations, a verified bazel build/bazel test invocation, and for debugging tasks the identified failing action with its fix. For toolchain work, a registered toolchain plus platform pair verified by --platforms.