Skip to main content

Module generation

Module generation 

Source
Expand description

The generation of an executable: which revision of the crate it was built from.

A version string is not a generation. Two builds that both call themselves majordomus-cli 0.4.0 can carry different models, and a derivation run with the wrong one is silent: generate writes what its own model says and exits 0. It happened on 2026-09-10 — a binary built from an older revision of this crate was handed to scripts/derive through MAJORDOMUS_BIN and rewrote a pristine origin/master from that older model. 16,625 lines removed across ten artifacts, docs/generated/changelog.json alone losing 9,724 of them, exit 0, no diagnostic. It was caught because somebody read git diff --stat before committing.

So the executable carries a digest of the sources it was built from, computed here at build time (build.rs compiles this module too, through #[path]) and again at run time over the crate of the repository being derived. Equal means this executable is that tree’s executable, whatever either of them calls itself; the comparison is a property of the code rather than of a string a human types into a manifest.

Nothing outside std and sha2 may be used in this file, and nothing lives here that only the crate needs. A build script cannot depend on the crate it is building, so the one definition of the digest has to compile on both sides — which is also why it is one definition and not two that could drift — and an item here that the build script does not reach is dead code in that compilation and fails -D warnings. The diagnostics that render a generation belong with the command that prints them.

use majordomus_cli::generation::crate_generation;

// one crate directory, taken twice: what `build.rs` compiles into the executable, and
// what `majordomus generate` recomputes over the tree it is asked to derive
let tree = tempfile::tempdir().unwrap();
std::fs::create_dir_all(tree.path().join("src")).unwrap();
std::fs::write(tree.path().join("src/lib.rs"), "pub fn f() {}\n").unwrap();
let built_from = crate_generation(tree.path()).unwrap();
assert_eq!(crate_generation(tree.path()), Some(built_from.clone()));

// the sources move, and the executable built from the old ones is no longer this
// tree's executable — which is the whole question, and the one a version cannot answer
std::fs::write(tree.path().join("src/lib.rs"), "pub fn g() {}\n").unwrap();
assert_ne!(crate_generation(tree.path()), Some(built_from));

Constants§

GENERATION_INPUTS
The inputs a generation is taken over: the crate’s manifest, its resolved dependency graph, its build script and its sources.

Functions§

crate_generation
The generation of the crate rooted at crate_dir: a hex SHA-256 over every file of GENERATION_INPUTS, each framed by the length and bytes of its crate-relative path and by its own length, so that no rename, concatenation or reordering can produce the digest of a different tree.