A C++ toolchain from 357 bytes in Bazel
摘要
作者受 stage0 项目启发,尝试在 Bazel 中提供从 357 字节种子程序构建的 C++ 工具链。他先介绍了 stage0 是什么,并指出 Bazel 官方 rules_cc 不提供封闭式工具链的现状。随后他提到自己 2024 年曾尝试但未完成,近期借助 LLM 完成了剩余工作。文章展示了用该工具链无补丁构建 Abseil 和 GoogleTest 并运行测试的结果,还介绍了通过 Bazel aspects 生成的审计报告来验证工具链的封闭性,最后讨论了 genrule 中 shell 路径作为种子二进制的问题,并呼吁构建系统社区更重视可复现性与封闭性。
荐读理由
照它的做法,你也能在 Bazel 里用 357 字节种子自举出全链路可审计的 C++ 工具链,Abseil 和 GoogleTest 免补丁直接编译,构建可复现性立马上一个台阶
原文
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")
bazel_dep(name = "abseil-cpp", version = "20260107.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
register_toolchains(
"@stage0-bazel//toolchain:clang",
"@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
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.
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
repository, except for these audited seed binaries:
external/+_repo_rules+hex0-seeds/POSIX/x86/hex0-seed
/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. 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 reproducible and hermetic, and this is a step in that direction.
这条对你有帮助吗?