Packaging a Mutex/Arc crate derivation, devShell, flake check
Beat A taught lock and Arc. Beat B puts that binary behind a store path, a shell, and a check.
<!-- hal:authoritative:yaml -->
Beat A taught lock and Arc. Beat B puts that binary behind a store path, a shell, and a check.
§I - Frame
Duha Beat B, Nix packaging session 02. Beat A (Rust session 19) taught Mutex::lock, Arc::clone across threads, and why Rc is neither Send nor Sync. This shell packages a small crate that uses those moves: one derivation that builds the binary, one devShell that holds the toolchain, one checks entry that nix flake check can run.
Session 01 packaged yesterday's channels crate. Same three packaging moves, new crate noun. Asr owns the Pills cursor and the later Rust-roads rows (#16 crate-as-derivation, #19 flake devShell/checks). Those Done cells stay unmarked today. This bundle does not consume a Pill.
Three moves land by the end:
- The Crate Derivation: read
rustPlatform.buildRustPackagefor a Mutex/Arc binary (src, lock hash, what lands in$out). - The Dev Shell: name
devShells.defaultas the editable toolchain, not the release store path. - The Flake Check: say what
nix flake checkbuilds from thechecksattribute set.
Done-criteria: Can sketch a buildRustPackage for a shared-state binary, open a Rust devShell, and name one flake checks entry that proves the build.
§II - The Crate Derivation
A derivation is still a build spec: inputs in, store path out. For Rust, nixpkgs wraps that as rustPlatform.buildRustPackage. The rust-tops corpus shows the shape in nix/cargo-tops.nix (read-only teach from the repo; no bot build cadence):
{
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";
mainProgram = "cargo-tops";
};
}
Read it the way NIX-SYLLABUS row #16 asks: src is the crate tree; cargoLock.lockFile (or cargoHash when you vendor differently) pins the crate graph; buildAndTestSubdir selects a workspace member; doCheck runs cargo test in the sandbox; the binary lands under $out (usually $out/bin/<mainProgram>).
For today's shared-state lesson, the same skeleton fits a single-binary crate whose main only builds an Arc<Mutex<i32>>, spawns ten incrementers, joins, and prints the result. Swap pname to something like mutex-arc-demo, point src at that crate, keep a real Cargo.lock in tree. No native C deps appear in Listing 16-15, so you do not need extra buildInputs for this demo. If the project vendors differently, nixpkgs also accepts cargoHash instead of cargoLock.lockFile; pick one pin style and keep it honest.
Gray-area crates and optional native deps stay out of the base image story for HedronOS later; here the rule is smaller: the flake inputs and the lock hash must explain every byte that enters the sandbox.
§III - The Dev Shell
A release derivation is not a workspace. While you edit the Mutex/Arc crate you want rustc, cargo, clippy, and rustfmt on PATH without baking them into the binary's runtime closure. That is devShells:
devShells = forAllSystems (
system:
let
pkgs = pkgsFor system;
in
{
default = pkgs.mkShell {
packages = [
pkgs.rustc
pkgs.cargo
pkgs.clippy
pkgs.rustfmt
];
};
}
);
The rust-tops flake.nix uses that pattern (plus cargo-tops itself on the shell). Enter with nix develop. Build the release path separately with nix build .#mutex-arc-demo (or whatever you named the package). Mixing those two jobs is how people ship a toolchain as a runtime dependency by accident.
Thread-safety lives in the Rust types (Arc, Mutex), not in the Nix shell. The shell only gives you a pinned compiler so cargo run and cargo test match what the derivation will see.
§IV - The Flake Check
packages is what you install. checks is what CI (and you) ask Nix to build for proof. From the same corpus flake:
checks = forAllSystems (
system:
let
pkgs = pkgsFor system;
in
{
cargo-tops = self.packages.${system}.cargo-tops;
# ... other proofs ...
}
);
nix flake check builds every attribute under checks for the current system. Putting the Mutex/Arc package itself in checks proves the derivation evaluates and builds. A tighter check can runCommand the binary and assert it prints Result: 10, still without standing up a second lesson store of tests outside the flake.
Three outputs, three jobs: packages ships, devShells edits, checks proves. That is the packaging shell for Beat A's shared-state crate.
§V - Proof and close
- Copy the
buildRustPackageskeleton, rename it for a Mutex/Arc binary, and say whatcargoLock.lockFilepins. - Open a
mkShellwithrustc/cargoand state why those packages are notbuildInputsof the release derivation. - Add the package to
checksand say whatnix flake checkwill build. - Name which Asr NIX-SYLLABUS rows teach the same moves later (#16, #19) and confirm this Duha bundle did not mark them.
Done-criteria: Can sketch a buildRustPackage for a shared-state binary, open a Rust devShell, and name one flake checks entry that proves the build.
Asr keeps the next unmarked Pill / Rust-roads row. Duha Beat B only packages today's T1 crate.