Packaging a channels crate derivation, devShell, flake check
Beat A taught spawn and send. Beat B puts that binary behind a store path, a shell, and a check.
<!-- hal:authoritative:yaml -->
Beat A taught spawn and send. Beat B puts that binary behind a store path, a shell, and a check.
§I - Frame
Duha Beat B, Nix packaging session 01. Beat A (Rust session 18) taught thread::spawn, join, and mpsc channels. 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.
No prior lesson_class: duha Nix bundle exists on disk, so the Duha-Nix packaging track starts here at session 01. 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.buildRustPackageline by line (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 channels 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 channels lesson, the same skeleton fits a single-binary crate whose main only spawns, sends, and joins. Swap pname, point src at that crate, keep a real Cargo.lock in tree. If the project vendors differently, nixpkgs also accepts cargoHash (a fixed-output hash over the fetched crates.io graph) instead of pointing at cargoLock.lockFile; pick one pin style and keep it honest. No new Nix language is required beyond the derivation attrs you already met in Pills 6–8 and the buildInputs discipline from Pill 20.
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 channels 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 .#channels-demo (or whatever you named the package). Mixing those two jobs is how people ship a toolchain as a runtime dependency by accident.
§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 channels package itself in checks proves the derivation evaluates and builds. A tighter check can runCommand the binary and assert it prints a known channel line, 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 concurrency crate.
§V - Proof and close
- Copy the
buildRustPackageskeleton, rename it for a channels 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 channels 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.