Skip to content

brittonr/unit2nix

Repository files navigation

unit2nix

Per-crate Nix builds from Cargo's unit graph.

On a 457-crate workspace:

What changed unit2nix rebuilds Crane rebuilds
Edit a local crate 31 457
Bump serde 133 457
Bump tokio 70 457
Bump a leaf dep 41 457

How it works

cargo build --unit-graph ─┐
cargo metadata ───────────┼─→ unit2nix ─→ build-plan.json
Cargo.lock checksums ─────┤
nix-prefetch-git ─────────┘   (git deps only)

The Nix side reads the JSON and calls buildRustCrate for each crate.

Quick start

nix flake init -t github:brittonr/unit2nix
nix run github:brittonr/unit2nix
nix build

The build plan embeds a Cargo.lock hash. If Cargo.toml or Cargo.lock changes, re-run unit2nix — Nix eval fails on hash mismatch.

Flake integration

Manual mode (checked-in JSON, no IFD)

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    unit2nix.url = "github:brittonr/unit2nix";
  };

  outputs = { nixpkgs, unit2nix, ... }:
    let
      pkgs = nixpkgs.legacyPackages.x86_64-linux;
      ws = unit2nix.lib.x86_64-linux.buildFromUnitGraph {
        inherit pkgs;
        src = ./.;
        resolvedJson = ./build-plan.json;
      };
    in {
      packages.x86_64-linux.default = ws.workspaceMembers."my-crate".build;
    };
}

Auto mode (IFD, nothing checked in)

ws = unit2nix.lib.x86_64-linux.buildFromUnitGraphAuto {
  inherit pkgs;
  src = ./.;
};

Plan is generated at eval time via import-from-derivation. Works everywhere except Hydra.

flake-parts

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    unit2nix.url = "github:brittonr/unit2nix";
  };

  outputs = inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      imports = [ inputs.unit2nix.flakeModules.default ];
      systems = [ "x86_64-linux" "aarch64-linux" ];

      unit2nix = {
        enable = true;
        src = ./.;
        resolvedJson = ./build-plan.json;  # null for auto mode
        defaultPackage = "my-bin";
      };
    };
}

Overlay

pkgs = import nixpkgs {
  system = "x86_64-linux";
  overlays = [ unit2nix.overlays.default ];
};
ws = pkgs.unit2nix.buildFromUnitGraph {
  src = ./.;
  resolvedJson = ./build-plan.json;
};

Return value

Both buildFromUnitGraph and buildFromUnitGraphAuto return:

Attribute Description
workspaceMembers.<name>.build Built workspace member
rootCrate.build Root package (single-package projects)
allWorkspaceMembers symlinkJoin of all members
test.check.<name> #[test] runner (requires --include-dev or --workspace)
clippy.allWorkspaceMembers Clippy all members
builtCrates.crates.<pkgId> Every crate derivation by package ID

-sys crate overrides

ring, openssl-sys, libgit2-sys, tikv-jemalloc-sys, and other common -sys crates are handled by built-in overrides plus nixpkgs' defaultCrateOverrides. Run unit2nix --check-overrides to see what's covered.

For project-specific crates:

buildFromUnitGraph {
  inherit pkgs src;
  resolvedJson = ./build-plan.json;
  extraCrateOverrides = {
    my-custom-sys = attrs: {
      nativeBuildInputs = [ pkgs.pkg-config ];
      buildInputs = [ pkgs.some-library ];
    };
  };
}

Cross-compilation

Cargo resolves different dep trees per target, so each target gets its own plan:

unit2nix --target aarch64-unknown-linux-gnu -o build-plan-aarch64.json
buildFromUnitGraph {
  pkgs = pkgs.pkgsCross.aarch64-multiplatform;
  src = ./.;
  resolvedJson = ./build-plan-aarch64.json;
}

Testing

For workspaces, use --workspace to capture dev-dependencies for all members:

cargo unit2nix --workspace
nix build .#test.check.my-crate

For single-crate projects, --include-dev is sufficient:

cargo unit2nix --include-dev
nix build .#test.check.my-crate

Git dependencies

Git deps are prefetched at generation time so builds stay pure. In auto mode, place a crate-hashes.json at the root (same format as crate2nix):

{
  "https://github.com/user/repo?branch=main#crate-name@1.0.0": "sha256-..."
}

CLI

unit2nix [OPTIONS]

  --manifest-path <PATH>    Path to Cargo.toml [default: ./Cargo.toml]
  --features <FEATURES>     Comma-separated features to enable
  --all-features            Enable all features
  --no-default-features     Disable default features
  --bin <NAME>              Build a specific binary target
  -p, --package <NAME>      Build a specific package
  --members <NAMES>         Workspace members to include (comma-separated)
  --target <TRIPLE>         Cross-compilation target
  --include-dev             Include dev-dependencies (for nix test support)
  --workspace               Resolve all workspace members + dev-deps (implies --include-dev)
  -o, --output <FILE>       Output file [default: build-plan.json]
  --stdout                  Write to stdout
  --force                   Regenerate even if inputs haven't changed
  --check-overrides         Report -sys crate override coverage
  --json                    Machine-readable output (with --check-overrides)
  --no-check                Skip override check after generation

Also available as cargo unit2nix.

vs crate2nix

unit2nix crate2nix
Resolver Cargo itself (unit graph) Reimplemented in Rust
Platform filtering Cargo does it cfg() evaluator in Nix
Cross-compilation One JSON per target One JSON, filtered at eval
Stability Nightly (needs --unit-graph) Stable Rust

unit2nix requires nightly Cargo for --unit-graph.

Tested on

Project Crates Notes
ripgrep 34 Pure Rust, zero overrides
fd 59 jemalloc covered by built-ins
bat 168 libgit2, libz, onig auto-covered
nushell 519 sqlite, ring auto-covered
Private workspace 457 Production build

Development

cargo test           # unit tests
nix flake check      # sample builds, clippy, overlay/module smoke tests,
                     # override coverage, fd/bat/ripgrep/nushell validation,
                     # cross-compilation, NixOS VM integration tests

Requirements

  • Nightly Rust (--unit-graph is unstable)
  • Nix with flakes

License

MIT

About

Per-crate Nix builds from Cargo's unit graph.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors