Chapter 19: rust, how crates reach the build

In an ordinary Rust project, cargo reads Cargo.toml, downloads the crates and builds everything. Here buck2 builds everything (the C bot included), and buck2 does not read Cargo.toml. Something has to translate. That something is reindeer.

flowchart LR
  toml["Cargo.toml (hand-written)"] --> lock["Cargo.lock (resolved)"]
  lock --> reindeer["reindeer buckify"]
  fix["fixups/ and reindeer.toml"] --> reindeer
  reindeer --> buck["BUCK (generated): one target per crate"]
  buck --> code["first-party code: //third-party/rust:<crate>"]

Cargo.toml here is not a real package anybody builds with cargo. It is a list of every third-party crate the project uses, with versions and features, and a comment on most of them saying why. reindeer resolves it, and writes BUCK: about four hundred http_archive downloads (crates are fetched at build time, pinned by Cargo.lock, not vendored into git) and a rust_library for each. First-party code then depends on, say, //third-party/rust:egui.

Three crates are not from crates.io at all:

  • jgenesis (jgenesis/), the emulator, as a git subtree of jsgroth/jgenesis (GPL-3.0) from upstream d19ee94f. This project uses its snes-core, snes-config and jgenesis-common crates as path dependencies. It carries two commits of ours: "jgenesis: expose work RAM from snes-core" (SnesEmulator::wram()) and "jgenesis: let a host write work RAM" (wram_mut(), for the bot's cheats).
  • jevcrates (jevcrates/), a git submodule: jev-protocol, jev-client and jev-http, the code every Jev project here shares. It came out of this repository (as packages/jev and packages/jev-http) on 2026-10-01. Its URL in .gitmodules is relative (../jevcrates.git), so it resolves beside wherever this repository was cloned from, which is why git clone --recurse-submodules from the lmjtfy site works.
  • rustls-rustcrypto, from its git repository's master branch.

Aside: why so much effort to avoid C? jev-http speaks HTTP/2 over rustls with the pure-Rust RustCrypto provider, and replay keyframes are compressed with ruzstd, a pure-Rust zstd. Every C dependency is a build script finding a system library, and every one of those is a way for a build to fail on a machine that is not this one. alsa-sys (sound) is the one crate that links a system library at build time; the window's wayland, xkbcommon, X11, Vulkan and GL are opened at startup instead.

Try it. After changing Cargo.toml:

nix develop -c sh -c 'cd third-party/rust && reindeer --third-party-dir . buckify'

then git diff --stat third-party/rust/BUCK to see what moved.

For the people who maintain it

In this folder

PathWhat
fixups/Chapter 20: per-crate answers reindeer cannot infer, chiefly whether a crate's build.rs must run.
top/Chapter 21: the empty crate root the manifest needs in order to be a package.
Cargo.tomlThe dependency list. The only file here edited by hand to add or bump a crate.
Cargo.lockThe resolution. Committed.
BUCKGENERATED by reindeer. Never edited by hand.
reindeer.tomlReindeer's settings: cargo_env = true, so every crate gets the CARGO_PKG_* variables cargo would set.
README.mdThis chapter.
CLAUDE.mdWhat breaks the generated graph, for agents.
jgenesis/The emulator, a git subtree. No chapter: upstream's own docs describe it, and a subtree pull would be the only thing keeping ours honest.
jevcratesThe Jev crates, a git submodule with a guide of its own. No chapter here.

← Previous: Chapter 18, patches/ · Up: third-party · Next: Chapter 20, fixups/ →