A C++ toolchain from 357 bytes, in Bazel | Farid Zakaria’s Blog
Skip to content
I have been fascinated and amazed by stage0 for a while now ever since I learnt about it via Guix using it to provide twenty two thousand packages source-bootstrapped from the 357-byte seed.
What is stage0?
It is a chain of compilers and assemblers that can be built from source, starting from a 357-byte program that can eventually build a recent GCC.11Once you can reach a recent-enough GCC, you can build any C/C++ program and beyond easily.
Since then, NixOS and other distributions have also adopted the same approach to minimize their binary seed which makes it possible to onboard new architectures and platforms much simpler.
What’s always frustrated me as a Bazel (& Buck) user is the reliance on prebuilt toolchains even for things that should be built from source easily like protoc.
Bazel has given up trying to provide a hermetic C++ toolchain and the upstream rules_cc ruleset just points you elsewhere:
Configuring a hermetic toolchain makes your build more deterministic. rules_cc itself does not yet offer a hermetic toolchain distribution
I had attempted to provide a stage0 hermetic C++ toolchain in October 2024 via https://github.com/fzakaria/stage0-bazel. I made substantial process through the bootstrap process but I did not make it far enought to be usabale.
To be honest, I was also a little disheartened that no one else in the community thought it was the greatest thing since slice bread. Everyone seems to be content with using prebuilt toolchains as they go deeper into MODULE.bzl madness.
I had put it aside for a while, but I have been thinking about it again recently. The steps are mechanical and the process imitates existing distributions, so this became a perfect project for me to throw at an LLM to finish.22Consider this the disclosure that I used an LLM to help me write the remainder of the toolchain.
You can now leverage the toolchain to build cc_binary in Bazel and have it compiled by a toolchain whose entire ancestry is in the repository from that same 357-byte seed . 🎆
How complete is this toolchain?
I pointed the toolchain at Abseil and GoogleTest straight from the Bazel Central Registry without any patches .
bazel_dep(name = "stage0-bazel", version = "0.1.0")<br>bazel_dep(name = "abseil-cpp", version = "20260107.1")<br>bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
register_toolchains(<br>"@stage0-bazel//toolchain:clang",<br>"@stage0-bazel//toolchain:cc",
We then can build and run their testsuite to provide a sanity check that the toolchain is working correctly.
$ bazel test --target_pattern_file=absl-tests.txt<br>Executed 236 out of 236 tests: 236 tests pass
We use a --target_pattern_file to filter tests that require google_benchmark. Abseil marks google_benchmark as a dev_dependency, and Bzlmod drops dev dependencies of non-root modules.
That is us building Abseil and GoogleTest, from the registry, unpatched, compiled by a toolchain that began as 357 bytes of hex.
bootstrap
seed
hex0
357 bytes
stage0
hex1 → hex2 → M0 → cc_x86<br>M2-Planet, kaem, M1
stage0 -->
seed->stage0
mes
GNU Mes
mescc
mes -->
stage0->mes
tccmes
tcc-mes
tccmes -->
mes->tccmes
tcc
tinycc
self-hosted
tcc -->
tccmes->tcc
musl
musl
musl -->
tcc->musl
bin
binutils
bin -->
musl->bin
gcc46
GCC 4.6.4
gcc46 -->
bin->gcc46
gcc10
GCC 10.4.0
C++17
gcc10 -->
gcc46->gcc10
extras
tar, findutils<br>Linux UAPI headers
extras -->
gcc10->extras
llvm
clang 22.1.8<br>lld 22.1.8
llvm -->
gcc10->llvm
llvm -->
extras->llvm
absl
Abseil + GoogleTest<br>236 tests pass
absl -->
llvm->absl
How can I be so sure this is a hermetic toolchain?
The toolchain includes an audit report that uses Bazel’s aspects to inspect every action in the build graph and verify that it only executes programs built by the toolchain itself. The report is generated by running bazel build //:trust-report and will fail if any action executes a program outside of the Bazel output tree.33We also set BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 to disable Bazel’s built-in C++ host toolchain detection.
The report is two lines long:
Bootstrap trust report
Every action in the checked graph runs a program built by this<br>repository, except for these audited seed binaries:
external/+_repo_rules+hex0-seeds/POSIX/x86/hex0-seed<br>/nix/store/…-bash-interactive-5.3p3/bin/bash
Unfortunately, since genrule runs a shell it takes as an absolute system path that is also listed as a seed binary. sh_toolchain’s path attribute is a string, and the shell is not a declared input of the action, so no artifact this repository built can provide it.
Building toolchains from bootstrap seeds was never a priority for companies like Google where they control the entire build environment.<br>However we seemed to have adopted the same approach as Bazel and similar build systems have become more popular in the open-source community. We should strive to make our builds more...