Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The buck2 build system

Why buck2

k23 is not a typical Rust project. We produce many different artifacts:

  • The kernel build for a custom Rust target and with from-source-rebuilt core and alloc crates
  • The loader binary that is built with different Rust flags and for a different Rust target
  • The full disk image(s) that is a combination of both binaries, with an initial ramdisk and possibly drivers/apps
  • Additionally we also have many different kinds of tests (unittest, fuzz, loom, wasm-spec, selftests, etc) that all require different modes and apply only to subsets of libraries.

Cargo’s build model (one --target, one profile, one feature resolution per workspace) is unfortunately not well equipped to handle this. k23 needs a build system that is flexible, can deal with the same source node appearing multiple times with different configuration, and where post-processing steps are easy to express.

That’s what buck2 gives us. Complex tooling (rust, c++, mdbook, qemu, python) is wired in as ordinary build rules. The build graph is hermetic and content-addressed. Buck2 can schedule much more optimal builds across the entire build graph and aggressively cache results along the way. Because buck2 isn’t Rust-specific, a full “image” (kernel + loader + ramdisk + apps) and QEMU runner can be declared as an elegant chaining of rules.

High-level components

Tree layout

k23/
├── sys/             non-standalone subsystems (only make sense inside k23)
│   ├── loader/      bootloader binary
│   ├── kernel/      kernel binary
│   └── async/       kasync — the async runtime
├── lib/             standalone, potentially-publishable libraries
│                    (range-tree, wavltree, cpu-local, spin, fdt, …)
├── third-party/     reindeer-generated BUCK rules; the one source of truth
│                    for every non-first-party dep
├── tests/           handwritten .wast fixtures consumed by the kernel test
│                    harness (//:wast_tests)
├── platforms/       target platforms (riscv64, aarch64, x86_64) bundling
│                    constraint values
├── manual/          the mdbook you are reading (//manual:manual)
├── build/           the buck2 build infrastructure itself (see below)
├── fuzz/            running corpus (gitignored) + committed crash repros
│                    in fuzz/artifacts/
├── bench/           criterion baselines; gitignored; cached on main in CI
└── buck-out/        buck2's everything; gitignored; `buck2 clean` clears it

Build infrastructure (build/)

Everything that defines how k23 is built (as opposed to what gets built) lives in build/:

build/
├── BUCK               declares kcfg options, target JSON, and the named
│                      transitions (loader, kernel, kernel-tests,
│                      loader-tests, rust_bootstrap)
├── constraints/       constraint enums (opt-level, debuginfo, strip,
│                      rust-std, env, sanitizer)
├── toolchains/        toolchain rules (rust, cxx, qemu, mdbook, python, …)
│                      plus flake.bzl, which exposes nix-flake packages
├── targets/           Rust target-spec JSON files
├── transitions.bzl    the generic configuration-transition rule
├── kcfg.bzl           typed buckconfig wrapper + kcfg_docs rule that
│                      auto-generates the config reference in this manual
├── qemu.bzl           qemu_binary — wraps a kernel ELF into a QEMU command
├── split_debuginfo.bzl  rule that splits a rust_binary into stripped binary
│                        + debuginfo artifact
├── bench.bzl          rust_benchmark macro (criterion)
├── loom.bzl           rust_loom_test macro
└── fuzz.bzl           rust_fuzz macro (libfuzzer + persistent corpus)

Cargo to buck2 cheat sheet

Cargo Commandk23 Equivalent
cargo check / cargo buildjust check
cargo build -p foojust check //lib/foo:foo
cargo test / cargo test -p foojust unittests / just unittests //lib/foo:foo
cargo benchjust benchmark
cargo fuzz run targetjust fuzz
cargo clippyjust clippy
cargo fmt --check / cargo fmtjust check-fmt / just fmt
cargo docjust doc
edit Cargo.toml / cargo updateedit third-party/Cargo.toml, then just buckify
[package]rust_library / rust_binary in a BUCK file
[dependencies]deps = [...] attribute of a rust_library/rust_binary rule
[features]features = [...] attribute of a rust_library/rust_binary rule
profiles (debug/release)constraints (opt-level/debuginfo/strip) + named modifier aliases in PACKAGE
--target=…constraints (prelude//cpu:riscv64, …) bundled into a platform under platforms/
RUSTFLAGS=-Cfoorustc_flags = ["-Cfoo"] attribute of a rust_library/rust_binary rule
cfg(test)a dedicated rust_test target; can carry its own deps
cfg(loom)rust_test with rustc_flags = ["--cfg=loom"] and labels = ["loom"]