Hedronite Lesson · Polyglot-Dev / Nix · Mon 2026-09-28 · T7

A Rust crate as a derivation — buildRustPackage

Read cargo-tops.nix line by line. Name src, cargoLock, and what lands in $out.

Lesson Class: Asr (Nix T7 Env & CI · Rust roads)
Focus: Call Package Shape · Source Lock Walk · Out Path Contract · cargoLock · buildAndTestSubdir
Done-criteria: read buildRustPackage line by line; name src, cargoLock/cargoHash, $out
Grounding: rust-tops nix/cargo-tops.nix + overlay.nix · nixpkgs manual #rust
Note: Not Duha Beat B re-author · not writers · not musl · not flake checks · next #17 writers
Call Package Shape
{ lib, rustPlatform }: then rustPlatform.buildRustPackage; overlay callPackage.
Source Lock Walk
src + cargoLock.lockFile + buildAndTestSubdir select the member build.
Out Path Contract
mainProgram cargo-tops on $out/bin; doCheck gates acceptance.
The derivation is the package; invent no attribute names the corpus file does not carry.

<!-- hal:authoritative:yaml -->

*Read a rustPlatform.buildRustPackage expression line by line. Name what lands in $out.*

§I — Frame

Asr session 16. First live fire of the Nix Rust-roads block (Topics #16 to #20). Topic T7 Env & CI. Home: . Primary corpus file: ~/projects/rust-tops/nix/cargo-tops.nix (vault mirror under foundry/rust-tops/repo/rust-tops/nix/). Live attribute reference: https://nixos.org/manual/nixpkgs/stable/#rust.

Sessions 01–15 closed the language-to-stdenv spine. Session 15 chose buildInputs vs propagatedBuildInputs. Today the unmarked row asks a different question: how does a Rust workspace crate become a store path through rustPlatform.buildRustPackage.

Duha Beat B today already packaged a channels crate as a teaching shell (lesson_class: duha, topic T1). That bundle did not consume this Asr cursor. Asr owns the line-by-line read of the protocol crate’s own derivation.

Done-criteria from the syllabus: you can read a Rust package derivation line by line (src, cargoLock/cargoHash, buildInputs, and what lands in $out).

Not writers. Not musl cross. Not flake devShell/checks. Not Pill 20 re-teach.

§II — Call Package Shape (named technique)

Call Package Shape: the file is a function of { lib, rustPlatform }, then a single rustPlatform.buildRustPackage { … } attrset. The overlay wires it with final.callPackage ./cargo-tops.nix { }.

From nix/overlay.nix:

cargo-tops = final.callPackage ./cargo-tops.nix { };

From nix/cargo-tops.nix (full file is short; quote the spine):

{
  lib,
  rustPlatform,
}:

rustPlatform.buildRustPackage {
  pname = "cargo-tops";
  version = "0.1.3";

  src = lib.cleanSource ../.;

  cargoLock.lockFile = ../Cargo.lock;

  buildAndTestSubdir = "crates/cargo-tops";

  doCheck = true;

  meta = {
    description = "init / check / gate for the Rust-TOPS protocol";
    homepage = "https://github.com/Hedronite/rust-tops";
    license = with lib.licenses; [ mit asl20 ];
    mainProgram = "cargo-tops";
  };
}

Musashi: the derivation is the package. callPackage fills lib and rustPlatform from the final package set. You do not invent attribute names; you read the ones present.

If-then-thus: if the file starts as { lib, rustPlatform }:, then the build helper comes from nixpkgs Rust support, thus the body is a specialized mkDerivation, not a hand-rolled bash builder.

§III — Source Lock Walk (named technique)

Source Lock Walk: name how source and Cargo deps enter the sandbox.

FieldIn cargo-tops.nixRole
pname / versioncargo-tops / 0.1.3Store name stem
srclib.cleanSource ../.Workspace root, filtered
cargoLock.lockFile../Cargo.lockVendored/locked crate graph for the build
buildAndTestSubdircrates/cargo-topsWhich workspace member builds
doChecktrueRun crate tests in checkPhase

Two locking styles exist in nixpkgs Rust packaging: cargoLock (point at a lock file; common for workspace crates in-repo) and cargoHash / cargoSha256 (hash of the fetched cargo deps when you are not shipping the lock walk the same way). This corpus file uses cargoLock.lockFile. When you see cargoHash in another flake, it is the alternate fixed-output path for Cargo dependencies. Do not mix the two styles in one package without reading the manual section for that helper.

lib.cleanSource drops VCS and other noise from src so the store path stays stable when junk files change.

If-then-thus: if buildAndTestSubdir points at crates/cargo-tops, then Cargo builds that member against the workspace lock, thus $out holds that member’s binary, not every workspace crate.

§IV — Out Path Contract (named technique)

Out Path Contract: say what the realized store path contains after a successful build.

buildRustPackage runs the Rust build inside stdenv. With meta.mainProgram = "cargo-tops", the primary CLI is expected on $out/bin/cargo-tops. Libraries and intermediate target artifacts do not define the package identity for this binary crate; the installable program does.

doCheck = true means the checkPhase runs the crate’s tests before the output is accepted. A red test fails the derivation. That is the same fail-closed idea Bend encodes for laws, applied to Cargo’s test runner inside Nix.

Session 15’s dep walk still applies when you add native libraries: put compile-time C deps in buildInputs / nativeBuildInputs as the Rust section and stdenv rules require. This particular cargo-tops.nix lists none beyond what rustPlatform already injects.

If-then-thus: if the build succeeds and mainProgram is set, then lib.getExe pkgs.cargo-tops resolves the CLI, thus consumers and flake packages can treat the derivation as the tool.

§V — Dualfire: paving Rust roads

Asr Nix seats T7 so Rust crates become reproducible store objects. Reading cargo-tops.nix is not tourism; it is how the protocol kit itself is packaged. Overlay + callPackage + buildRustPackage is the road from workspace member to packages.${system}.cargo-tops.

Keep the Duha/Asr split clear: Duha Beat B may package today’s lesson crate under lesson_class: duha. Asr advances NIX-SYLLABUS rows under lesson_class: asr and topic: T7.

§VI — Done-criteria check

Say aloud without looking:

  1. Call Package Shape: { lib, rustPlatform }: then rustPlatform.buildRustPackage.
  2. Source Lock Walk: src, cargoLock.lockFile (or cargoHash elsewhere), buildAndTestSubdir.
  3. Out Path Contract: binary under $out/bin (here cargo-tops via mainProgram); doCheck gates acceptance.
  4. Overlay wire: final.callPackage ./cargo-tops.nix { }.

§VII — Common mistakes

  1. Treating Duha’s channels packaging shell as this Asr Topics #16 ship.
  2. Inventing nixpkgs attribute names instead of quoting the corpus file and the manual.
  3. Confusing cargoLock.lockFile with cargoHash and “fixing” both.
  4. Assuming every workspace crate lands in $out when buildAndTestSubdir names one member.
  5. Jumping to flake checks (row #19) or writers (row #17) before this read is fluent.
  6. Re-opening Pill 20 as if hooks were today’s unmarked topic.

§VIII — Boundaries

§IX — Close

Session 15 closed the Pills spine. Session 16 opens Rust roads: Call Package Shape, Source Lock Walk, Out Path Contract on cargo-tops.nix. Next unmarked: session 17, pkgs.writers + cargo script.

Related:

Open nix/cargo-tops.nix. Speak each field once. Leave the keyboard.