RepoAnthropicAnthropicpublished May 19, 2026seen 6d

anthropics/cargo-nix-plugin

Rust

Open original ↗

Captured source

source ↗
published May 19, 2026seen 6dcaptured 11hhttp 200method plain

anthropics/cargo-nix-plugin

Description: A Nix plugin that resolves Cargo workspaces natively and allows for crate level builds

Language: Rust

License: Apache-2.0

Stars: 28

Forks: 9

Open issues: 6

Created: 2026-05-19T17:07:44Z

Pushed: 2026-05-30T18:47:01Z

Default branch: main

Fork: no

Archived: no

README:

cargo-nix-plugin

A Nix plugin that resolves Cargo workspaces natively, replacing the generated Cargo.nix file from crate2nix with a single builtins.resolveCargoWorkspace primop.

What It Does

  • Resolves Cargo workspaces at native speed — directly from Cargo.lock and

the sparse registry index, no cargo binary required (or, optionally, from pre-generated cargo metadata JSON)

  • Pre-evaluates cfg() target expressions for the requested platform
  • Returns a Nix attrset compatible with buildRustCrate
  • Eliminates the crate2nix generate step and the 50K-100K line Cargo.nix

Install

Add the plugin to your Nix configuration:

# nix.conf or via --option — point at the directory so the right
# extension (.so/.dylib) is picked up automatically
plugin-files = /path/to/cargo-nix-plugin/lib/nix/plugins

Or use the flake output:

{
inputs.cargo-nix-plugin.url = "github:anthropics/cargo-nix-plugin";
}

Usage

Default (lockfile resolve)

Just point at your workspace root:

cargoNix = cargo-nix-plugin.lib {
inherit pkgs;
src = ./.; # must contain Cargo.toml + Cargo.lock
};

The plugin reads Cargo.lock plus the sparse registry index directly — no cargo binary, no crate sources at eval time. On first use it fetches each crate's index entry (a few hundred bytes) into $CARGO_HOME and reuses it thereafter.

If your environment already redirects cargo to a mirror, the resolver follows the same configuration — CARGO_REGISTRIES_CRATES_IO_INDEX or [source.crates-io] replace-with in .cargo/config.toml — so no plugin-specific setup is required:

# .cargo/config.toml — honoured by both cargo and the plugin
[source.crates-io]
replace-with = "mirror"
[source.mirror]
registry = "sparse+https://artifactory.example/api/cargo/crates/index/"

If every index lookup fails (e.g. egress to index.crates.io is blocked and no mirror is configured), evaluation fails loudly rather than silently producing derivations with missing features.

Explicit metadata

Alternatively, pre-generate cargo's resolution and pass it in:

cargo metadata --format-version 1 --locked > metadata.json

Then pass it explicitly:

cargoNix = cargo-nix-plugin.lib {
inherit pkgs;
metadata = builtins.readFile ./metadata.json;
cargoLock = builtins.readFile ./Cargo.lock;
src = ./.;
};

A helper is also available:

nix run .#generate-metadata -- > metadata.json

Warming the index cache out of band

In the rare case where the evaluating host has *no* reachable index at all, cargo-nix-prefetch can populate $CARGO_HOME ahead of time on a connected host (it observes the same mirror precedence as the plugin):

nix run .#cargo-nix-prefetch -- --manifest-path ./Cargo.toml
nix run .#cargo-nix-prefetch -- --manifest-path ./Cargo.toml --check # verify

Use --output DIR to write into a fresh directory instead of the ambient $CARGO_HOME, then point the resolver at it explicitly:

nix run .#cargo-nix-prefetch -- --manifest-path ./Cargo.toml --output ./.cargo-index
cargoNix = cargo-nix-plugin.lib {
inherit pkgs;
src = ./.;
cargoHome = ./.cargo-index; # pre-warmed by cargo-nix-prefetch
};

The same shape works wrapped in a fixed-output derivation if you want the cache pinned by hash rather than checked in.

Git dependencies

git+… entries in Cargo.lock are fetched at eval time with builtins.fetchGit { url; rev; allRefs = true; submodules = true; } so the resolver can read each crate's Cargo.toml (the registry index has no record of them). Submodules are pulled to match cargo, which always recurses them for git deps. When the upstream repo is a Cargo workspace, the resolver locates the right member and passes its sub-directory to buildRustCrate as workspace_member.

Override gitSources when fetchGit can't reach the repo (private auth, vendored fixture), to pin a narHash/use a FOD fetcher, or to skip submodules for a repo that doesn't need them:

cargoNix = cargo-nix-plugin.lib {
inherit pkgs;
src = ./.;
gitSources = {
# key = "${url}#${rev}" with git+ and ?query stripped — exactly what
# appears in Cargo.lock after `git+` and before `?`, plus `#REV`.
"https://github.com/Byron/gitoxide#abcdef…" = pkgs.fetchgit {
url = "git@github.com:Byron/gitoxide";
rev = "abcdef…";
hash = "sha256-…";
};
};
};

A git+ source without a pinned #rev is rejected; Cargo.lock always pins one.

Debug logging

The resolver stays quiet on the happy path so eval output isn't drowned in progress noise. Set CARGO_NIX_DEBUG=1 to surface the informational logs (mirror selection, index prefetch timings, per-crate retry attempts) on stderr. Warnings about misconfiguration and hard errors are always printed regardless of this flag.

Example

The plugin must be loaded by the same Nix version it was compiled against (see [Compatibility](#compatibility)). Evaluate with the plugin loaded via --option:

PLUGIN=$(nix build .#cargo-nix-plugin --print-out-paths)
NIX=$(nix build nixpkgs#nixVersions.nix_2_34 --print-out-paths | grep -v man)

$NIX/bin/nix-instantiate --eval \
--option plugin-files "$PLUGIN/lib/nix/plugins" \
-E '(import ./lib { pkgs = import {}; src = ./.; }).workspaceMembers'

Or permanently in nix.conf / ~/.config/nix/nix.conf (only if your system Nix matches the plugin's build version):

plugin-files = /path/to/cargo-nix-plugin/lib/nix/plugins

flake.nix

{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
cargo-nix-plugin.url = "github:anthropics/cargo-nix-plugin";
};

outputs = { self, nixpkgs, cargo-nix-plugin }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; };

cargoNix = cargo-nix-plugin.lib {
inherit pkgs;
src = ./.;
};
in {
packages.x86_64-linux.default = cargoNix.rootCrate.build;
};
}

Clippy

The wrapper provides cached clippy checks via cargoNix.clippy. Dependencies are compiled once with rustc and cached in the Nix store; only workspace members are re-checked with clippy-driver. This means running clippy on a large workspace is as fast as compiling just your local…

Excerpt shown — open the source for the full document.

Notability

notability 2.0/10

Routine repo with low stars, not a major release.