Adding a Third-Party Dependency
Even though we use buck2 and not Cargo to build k23 we nonetheless use 3rd party crates from crates.io. There are plenty of high-quality legitimately useful libraries available and we want to use them.
The tight integration between crates.io and Cargo (a good thing!) requires a bit of finagling which we do using reindeer maintained by meta. It takes a Cargo.toml file, resolves all the crates and generates a BUCK file that lets us reference these
crates from throughout our buck2 project.
TL;DR
- add the crate to
third-party/Cargo.toml - run
reindeer buckify - depend on it from a first-party crate via
//third-party:<crate-name> - commit
third-party/Cargo.toml,third-party/Cargo.lock, andthird-party/BUCK
The reindeer-clean job will complain if the Cargo.toml and BUCK file are out of sync.
The fields you’ll touch
third-party/Cargo.toml— the master manifest reindeer reads[dependencies]for plain cratesdefault-features = falseis the norm — most of our deps need to beno_std-friendlyfeatures = [...]only for what you actually need- mark optional with
optional = trueif the crate is only pulled in by some downstream feature
third-party/Cargo.lock— auto-managed; commit it as-isthird-party/BUCK— generated, large, do not hand-editthird-party/fixups/<crate>/fixups.toml— optional per-crate overrides for the rare cases where reindeer needs hints (build script behavior, env vars, conditional features). Look at existing examples (getrandom,rustix,serde) before writing onethird-party/deny.toml— license allowlist; cargo-deny CI checks against this
Adding a crate
- Add to manifest
# third-party/Cargo.toml
[dependencies]
foo = { version = "0.4", default-features = false, features = ["bar"] }
If you’re adding a dev or build dependency (that will be run on the host and needs access to std) you should mark it EITHER as optional = true and add it to the default feature OR enable it’s std-requiring feature in the default feature.
- Update
Cargo.lock
reindeer update
This will update the Cargo.lock file, reusing the Cargo/crates.io resolution logic. The updated lockfile is required by the next step.
- Regenerate buck rules
reindeer buckify
This will read the lockfile and generate the third-party/BUCK file. This file contains buck2 target declarations corresponding to the dependencies you added in step 1. Note that these buck2 targets directly fetch the libraries from crates.io, so you dont actually need Cargo installed at all.
- Use it in a
BUCK
deps = [
"//third-party:foo",
...
]
You can then reference 3rd party libraries by their buck2 path. The name of the target is the same as in the Cargo.toml manifest.
- Verify
To verify your changes are correct, you may run just check //path/to/consumer:target or just preflight //path/to/consumer:target to run all checks.
If cargo-deny complains about the 3rd party crates’ license you may extend third-party/deny.toml if the license is MIT compatible or pick a different crate (it’s probably best to discuss this with the maintainers and community first in any case).
Reindeer fixups
In some situations reindeer needs human help to correctly generate the dependency graph. These hints are called “fixups” and live in third-party/fixups/<crate>/fixups.toml files. You will need a fixup when:
- the crate has a required buildscript =>
reindeerdoes not build/run buildscripts by default. You have to explicitly opt-in by settingbuildscript.run = true - the crate reads env vars set at compile time => you may need to declare them manually or set
cargo_env = truefor the “common” cargo env vars - the crate has complex
cfg(...)dependencies or features => you will need to spell them out, see the reindeer manual for help.
buck2-fixups is a community maintained list of fixups for common crates.io dependencies. It’s always worth a look.
See third-party/fixups/getrandom/fixups.toml and third-party/fixups/serde/fixups.toml for examples of complex fixups.
Updating an existing dependency
Updating an existing dep is as easy as bumping its version in third-party/Cargo.toml and running reindeer update followed by reindeer buckify.
Removing a dependency
Removing a dependency means deleting it from third-party/Cargo.toml, deleting its corresponding third-party/fixups/<crate>/fixups.toml if present and run reindeer buckify to synchronize the third-party/BUCK file.
Git dependencies
You should prefer crates.io releases since git dependencies complicate and slow down the build. But if its unavoidable,
pin a branch or rev in Cargo.toml and add the host to third-party/deny.toml [sources] allow-git.
For example, we currently pull in JonasKruckenberg/wasmtime (the cranelift no_std fork) as a git dependency.