Skip to content
nixcloudPublic
forked from rust-lang/cargo

About

The Rust package manager

Resources

Stars

46 stars

Watchers

1 watching

Forks

Β 
Β 

Latest commit

Β 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

cargo (libnix)

⚠️ This is an unofficial fork of Cargo, not endorsed by the Rust Project. It is intended as a PR to contribute a nix backend to the official cargo, and to get feedback on the work from the nix community.

cargo (libnix) is cargo with nix as its build backend. You keep using cargo build, cargo run and cargo test as usual, but every crate is built by nix:

  • reproducible: each crate is built in the nix sandbox, with exactly the system libraries you declare
  • shared: a crate built once (same version, features and dependencies) is reused by every project and, with a binary cache, every machine
  • deployable: the same build can be exported as nix code, so your project builds with nix alone, from a flake or nixpkgs

asciicast

Quick start πŸš€

  1. Install nix with the nix-daemon (the "multi-user" installation) from nixos.org/download; on NixOS you already have it. Then enable flakes:

    mkdir -p ~/.config/nix
    echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
  2. Go to a Rust project (a directory with a Cargo.toml):

    cd my-rust-project
  3. Enter the cargo (libnix) shell:

    nix develop github:nixcloud/libfenix

    libfenix is fenix's Rust toolchain with cargo (libnix) as its cargo, all prebuilt: nothing is compiled, the shell starts after a download. It has cargo, the matching rustc, clippy, rustfmt and nix-prefetch-git, and sets CARGO_BACKEND=nix. Check that you got it:

    $ cargo --version
    cargo 1.99.0 +libnix v2
    
  4. Build:

    $ cargo build
    …
    Using 'nix' backend to build crates
             Nix Creating nix build system for 2 cargo units.
      Evaluating /…/my-rust-project/target/debug/nix/cargo_build_caller.nix
       Evaluated target in 2.5s
        Planning 3 derivation(s) to build, 0 path(s) to fetch (0.0 MiB)
       Compiling my-rust-project (bin)
        Finished 3 built, 0 fetched, 0 fresh, 0 failed, 0 skipped in 3.6s
    $ readlink target/debug/my-rust-project
    /nix/store/…-my-rust-project-0_1_0-bin-…/bin/my-rust-project
    

    The binary is a link into /nix/store. cargo run and cargo test work the same way.

Without CARGO_BACKEND=nix this cargo behaves exactly like the upstream cargo. To go back, leave the shell with exit, or run a single command with CARGO_BACKEND=legacy cargo build. Editors (rust-analyzer) need CARGO_BACKEND=legacy for now, see Troubleshooting.

Which version? The shell above is cargo (libnix) 1.99 with Rust 1.99.0, the right choice for most projects. If your project has to stay on Rust 1.87, use nix develop github:nixcloud/libfenix#rust-1_87.

Without libfenix: nix develop github:nixcloud/cargo is the same cargo (libnix) and rustc, built from source from the release's nix build system (the first time takes a while), without clippy, rustfmt or other targets; cargo +1.87 build in it runs the 1.87 release. Without entering a shell: CARGO_BACKEND=nix nix run github:nixcloud/cargo -- build. See Using cargo (libnix) in your project.

Troubleshooting 🩺

error: experimental Nix feature 'nix-command' is disabled: flakes aren't enabled yet, see step 1 of the Quick start.

A *-sys crate fails to build, e.g. openssl-sys: "Could not find directory of OpenSSL installation": in the nix sandbox a crate only sees the system libraries you give it. Create a Cargo.dependencies.nix next to your Cargo.toml:

{ pkgs }:
with pkgs;
{
  deps = {
    "openssl-sys" = [ pkg-config openssl ];
  };
  envs = { };
}

and run cargo build again. Other common ones are libsqlite3-sys ([ pkg-config sqlite ]) and libz-sys ([ pkg-config zlib ]); see Injecting dependencies.

error: cargo 'check' is not supported yet, or your editor shows errors everywhere: rust-analyzer reads the workspace with cargo metadata, which works, but it also runs cargo check to run the build scripts (OUT_DIR, generated code, cfgs), to build the proc-macros it expands and for the diagnostics on save; the nix backend doesn't support cargo check yet. Let the editor use the normal cargo backend and its own target directory, e.g. in VS Code's settings.json:

{
  "rust-analyzer.cargo.extraEnv": { "CARGO_BACKEND": "legacy" },
  "rust-analyzer.check.extraEnv": { "CARGO_BACKEND": "legacy" },
  "rust-analyzer.cargo.targetDir": true
}

The separate target directory matters: a legacy build in target/ replaces the links into /nix/store with ordinary files.

A git dependency fails with nix-prefetch-git: command not found: the devshell has it; in your own flake or shell.nix, add pkgs.nix-prefetch-scripts.

Rebuilding after a change is slower than with normal cargo: the nix backend has no incremental compilation yet, so a crate you change is compiled from scratch (#3). Your dependencies are not rebuilt, they come from /nix/store.

Anything else: open an issue, not on the upstream cargo tracker.

Limitations

  • no IDE support in nix mode: cargo check isn't supported yet, and rust-analyzer needs it to run build scripts, build proc-macros and show diagnostics (cargo metadata works). Let the editor use the legacy backend with its own target directory, see Troubleshooting; cargo build, run and test stay on nix
  • Linux only, developed and tested on x86_64-linux; Darwin is untested
  • not supported yet in nix mode: cargo check, doc, bench, package, incremental builds, registries other than crates.io
  • cross-compilation (--target) in nix mode builds and runs for musl and wasm32 targets on the branches, not yet in the releases (in legacy mode it works with a libfenix toolchain), but the outputs aren't linked to target/<triple>/<profile> yet (wasm-pack and trunk don't find them) and glibc targets other than the host need a cross C toolchain, see Supported subcommands and features
  • cargo (libnix) needs a rustc of its own version; libfenix and this flake bring the matching one

The full list is in Supported subcommands and features.

Using cargo (libnix) in your project

There are two ways to get cargo (libnix) and its rustc:

  • a) libfenix (recommended): fenix's toolchains with cargo (libnix) as their cargo, prebuilt, nothing is compiled. With clippy, rustfmt, rust-src, rust-analyzer and the rust-std of other targets, so cross compiling (e.g. wasm32) works as with fenix (in nix mode from the next releases on, see Supported subcommands and features). libfenix is meant to be temporary: once cargo (libnix) gains traction, it is to be merged into upstream fenix
  • b) this flake: cargo (libnix) built from source from the nix build system of its release, with the dist rustc of the same version; for building it yourself, nix run github:nixcloud/cargo or pinning a release's build system. Only cargo and rustc, and its rustc 1.99 can't cross compile (see Flake outputs)

pankat-rs is an example project: its flake.nix takes its toolchain from libfenix and pankat-wasm.nix builds its wasm frontend with it and wasm-pack.

a) libfenix

In a flake, the devshell of the Quick start for your project:

{
  inputs.libfenix.url = "github:nixcloud/libfenix";

  outputs = { self, libfenix }: {
    devShells.x86_64-linux.default = libfenix.devShells.x86_64-linux.rust-1_99;   # or rust-1_87; CARGO_BACKEND=nix
  };
}

Or with a toolchain of your own, e.g. with the std of wasm32 for wasm-pack (libfenix is libfenix's package set, import libfenix { inherit system pkgs; } in a flake):

pkgs.mkShell {
  packages = [
    (libfenix.combine [
      libfenix.rust-1_99.defaultToolchain                       # rustc, cargo (libnix), clippy, rustfmt
      libfenix.targets.wasm32-unknown-unknown.rust-1_99.rust-std
    ])
    pkgs.nix-prefetch-scripts                                   # for git dependencies
  ];
  shellHook = "export CARGO_BACKEND=nix";
}

Without flakes: libfenix = import (builtins.fetchTarball "https://github.com/nixcloud/libfenix/archive/<commit>.tar.gz") { }; (it takes system and pkgs). The rest of the API (toolchainOf, fromToolchainFile, withComponents, the overlay) is fenix's, see the libfenix README.

b) This flake: in a flake

Add cargo (libnix) with its rustc to your project's devshell:

{
  description = "myproject";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
    cargo-libnix.url = "github:nixcloud/cargo";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils, cargo-libnix }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs { inherit system; };
      in {
        devShells.default = pkgs.mkShell {
          packages = [
            cargo-libnix.packages.${system}.rust-1_99   # or rust-1_87
            pkgs.nix-prefetch-scripts
          ];
          shellHook = ''
            export CARGO_BACKEND=nix
          '';
        };
      });
}

Then nix develop in your project gives you the same shell as the Quick start. Use rust-1_87 instead of rust-1_99 to stay on Rust 1.87.

rust-1_99 follows the 1.99 line (currently libnix-1.99.0+v2); to stay on one release, use cargo-libnix-1_99_0_plus_v2 and its .rustc, or pin the flake input to a commit of this branch.

b) This flake: traditional nix code

A shell.nix that builds cargo (libnix) from the build system of release libnix-1.99.0+v2 with a pinned rustc/cargo 1.99.0 from fenix:

{ pkgs ? import <nixpkgs> { } }:
let
  fenix = import (builtins.fetchTarball {
    url = "https://github.com/nix-community/fenix/archive/6ed03ef4c8ec36d193c18e06b9ecddde78fb7e42.tar.gz";
    sha256 = "1mjmp9c5x3j6jqy50ymr07nxl4nrlp3iininxhnzc1ragdrg8pxn";
  }) { inherit pkgs; };
  toolchain = fenix.toolchainOf {
    channel = "1.99.0";
    sha256 = "sha256-zm3dyIY2T414ZRR3EhLOvptzG6gta4WZUcawzMUWtqI=";
  };
  # since 1.90 rustc links with its own lld, which bypasses nix's ld wrapper (no rpath: the binaries miss their
  # libraries, e.g. libssl.so.3); -C linker-features=-lld links with the cc's ld again
  rustc = pkgs.symlinkJoin {
    name = "rustc-1.99.0-nix-ld";
    paths = [ toolchain.rustc ];
    nativeBuildInputs = [ pkgs.makeWrapper ];
    postBuild = ''
      for b in rustc rustdoc; do wrapProgram $out/bin/$b --add-flags "-C linker-features=-lld"; done
    '';
  };
  # the generated build system of cargo (libnix) and the cargo sources it builds
  buildsystem = builtins.fetchTarball {
    url = "https://github.com/nixcloud/cargo/releases/download/libnix-1.99.0%2Bv2/libnix-1.99.0+v2.tar.bz2";
    sha256 = "sha256:0v10kxslf5140wd5b14anc79n9c0lkz5yv2y0ni1vxxbzzmadh35";
  };
  cargo-src = builtins.fetchTarball {
    url = "https://github.com/nixcloud/cargo/archive/refs/tags/libnix-1.99.0%2Bv2.tar.gz";
    sha256 = "sha256:1p7llhf5kl2yqr5zcdya81nfr63lsn3iysyc8r827rabz4b8lfg4";
  };
  cargo-libnix = (import (buildsystem + "/derivations/default.nix") {
    inherit pkgs rustc;
    project_root = cargo-src;
    external_crate_dependencies = import (buildsystem + "/Cargo.dependencies.nix") { inherit pkgs; };
    cargo = toolchain.cargo;
  }).cargo-0_100_0-bin-463824861cef8abc;
in
pkgs.mkShell {
  packages = [ cargo-libnix rustc pkgs.nix-prefetch-scripts ];
  shellHook = "export CARGO_BACKEND=nix";
}

For a NixOS configuration, put cargo-libnix and rustc into environment.systemPackages instead and set CARGO_BACKEND=nix in environment.variables.

b) Flake outputs

The flake.nix of this branch serves the newest release of each supported toolchain; 1.99 is the default:

output contents
packages.default, devShells.default cargo libnix-1.99.0+v2 and rustc 1.99.0; cargo +1.87 … runs the 1.87 release instead (like rustup's +toolchain)
packages.rust-1_99, devShells.rust-1_99 cargo libnix-1.99.0+v2 and rustc 1.99.0
packages.rust-1_87, devShells.rust-1_87 cargo libnix-1.87.0+v5 and rustc 1.87.0
packages.cargo-libnix-1_99, packages.cargo-libnix-1_87 only cargo, the newest release of that line; its rustc is .rustc
packages.cargo-libnix-1_99_0_plus_v2, packages.cargo-libnix-1_87_0_plus_v5 (and the earlier …_plus_v1, …_plus_v4) only cargo, exactly that release
overlays.default cargo-libnix (= default), cargo-libnix_1_99, cargo-libnix_1_87 (= rust-1_99, rust-1_87)

cargo (libnix) only works with a rustc of its own version, so use rust-1_99 / rust-1_87 (cargo and rustc in one package), or cargo-libnix-* together with its .rustc, never a rustc from elsewhere. The rustc 1.99.0 is wrapped with -C linker-features=-lld: since 1.90 rustc links with its own lld, which bypasses nix's linker wrapper (no rpath, the binaries miss their libraries). That flag is stable on x86_64-unknown-linux-gnu only, so this rustc fails with any other --target (… is unstable on the … target); for cross compiling use a toolchain from libfenix.

Injecting dependencies

Crates that link against system libraries (openssl-sys, libsqlite3-sys, …) or need environment variables at build time get them from a Cargo.dependencies.nix next to Cargo.toml / Cargo.lock. Without the file, crates are built with the toolchain only; a *-sys crate whose build script can't find its library is the typical sign that an entry is missing.

  1. create Cargo.dependencies.nix in the workspace root

  2. add buildInputs per crate in deps and environment variables per crate in envs; the name and version are the ones in Cargo.lock. The version is optional, but once a crate is keyed by version, deps must list every version of it in Cargo.lock:

    { pkgs }:
    with pkgs;
    {
      deps = {
        "openssl-sys" = [ pkg-config openssl ];       # every version
        "libsqlite3-sys" = {
          "0.30.1" = [ pkg-config sqlite ];           # only this version
        };
      };
      envs = {
        "proc-macro-test" = {
          "PROC_MACRO_TEST_LOCATION" = "";
        };
      };
    }
  3. run cargo build; the inputs and variables are added to every derivation of that crate (build script, build script run, compilation)

More examples are in Cargo.dependencies.nix (the one of cargo itself) and in tests-libnix.

Reference

Supported subcommands and features 🧰

With CARGO_BACKEND=nix, the build-related subcommands go through nix; all others are plain cargo. Details and the state of development are in README-DEV.md.

subcommand nix mode notes
build βœ… every unit is built in nix; build write-nix-buildsystem exports the build system for flakes and nixpkgs
run βœ… binaries and examples; std::env::current_exe() is the binary in the store
test βœ… unit, integration and doc tests; the tests run outside of nix, the sources at env!("CARGO_MANIFEST_DIR") are read-only
clean βœ… partial the target directory or a profile, together with the store paths it rooted; clean -p and clean gc are refused
check ❌ planned refused with "not supported yet"; rust-analyzer needs it (build script outputs, proc-macros, diagnostics), so IDEs need CARGO_BACKEND=legacy for now, see Troubleshooting
install, uninstall βœ… binaries stay in the store, rooted in <root>/nix/install, <root>/bin links to them; uninstall deletes the store paths nothing else uses
doc, rustdoc, rustc, bench, package, vendor ❌ refused with "not supported yet"
new, init, add, remove, update, fetch, generate-lockfile, tree, metadata, fmt, publish, … unchanged vanilla cargo, the backend is not involved
fix ❓ untested, it builds in check mode internally
feature state notes
crates.io dependencies βœ… downloaded and built in /nix/store, shared between projects and machines
path dependencies, workspaces, -p, --features βœ…
build scripts (build.rs), proc macros βœ… build scripts run in the nix sandbox
cargo: directives of build scripts ⚠️ parsed by build-rs-libnix; cargo:: syntax, generic metadata, rustc-flags and the target-specific rustc-link-arg-* are not supported yet, see build scripts and build-rs-libnix
system libraries, environment variables βœ… per crate in Cargo.dependencies.nix, see Injecting dependencies
git dependencies βœ… needs nix-prefetch-git in PATH
vendored sources, source replacement βœ…
--offline βœ… crates.io is served from the store tarballs of Cargo.lock, so the build system can be generated inside a nix build (IFD)
--release βœ… profile options (lto, codegen-units, panic, …) are passed on, but untested
GC roots βœ… per selection of units in target/<profile>/nix/gc, the 8 most recent are kept; cargo clean removes them
cargo-like output βœ… nix logs are shown in cargo's format
other registries ❌ only crates.io; others fail when the build system is generated
incremental builds / fingerprints ❌ changed crates are always compiled from scratch, so iterating is slower than with cargo (legacy) (#3)
cross-compilation, --target (e.g. wasm) ⚠️ on the branches rust-1.99.0-libnix and rust-1.87.0-libnix, not yet in the releases libnix-1.99.0+v2 and libnix-1.87.0+v5 (whose units build without the std of other targets): the units build with the rust-std of each --target: x86_64, i686, aarch64, armv7 and riscv64 musl, wasm32-wasip1 and wasm32-unknown-unknown build and run, also with a C library compiled by a build script, see tests-libnix/cross; pankat-rs' wasm frontend (151 units) builds for wasm32-unknown-unknown. Open: the outputs are linked to target/<profile> instead of target/<triple>/<profile>, cdylibs not at all, so wasm-pack and trunk don't find them yet (pankat-wasm.nix runs wasm-pack in legacy mode with a libfenix toolchain); musl targets other than x86_64 need CARGO_TARGET_<TRIPLE>_LINKER=rust-lld; glibc targets other than the host need a cross C toolchain, which isn't provided yet. In legacy mode (CARGO_BACKEND=legacy) cross compiling works with a libfenix toolchain as with fenix; the rustc 1.99 of this flake can't cross compile, see Flake outputs
platforms ❓ developed and tested on x86_64-linux only, Darwin is untested
toolchain ⚠️ rustc and cargo must have the same version (currently 1.99.0); rustc β‰₯ 1.90 links with its bundled lld, which bypasses nix's linker wrapper: in nix mode the units of libnix-1.99.0+v2 are built with a wrapped rustc passing -C linker-features=-lld; on the branch rust-1.99.0-libnix (the next release) with a rustc whose bundled gcc-ld/ld.lld goes through nixpkgs' ld-wrapper.sh instead (as rust-overlay and libfenix do), which also works with --target

The tables describe the 1.99 line (libnix-1.99.0+v2). The 1.87 line (libnix-1.87.0+v5) has the same subcommands and features and needs no wrapped rustc; see the README of rust-1.87.0-libnix for its state.

How it works

cargo resolves the dependencies and plans the build as usual. In nix mode it then writes one nix derivation per unit (a crate with its features and profile, a build script, a build script run, a test harness) to target/<profile>/nix, builds them with nix build and links the results into target/<profile>, so target/debug/<bin> is a symlink into /nix/store.

  • every crate is built in the nix sandbox, with its system libraries from Cargo.dependencies.nix
  • crates.io downloads and crate builds are store paths, shared between projects and machines (and between cargo build, cargo run and cargo test)
  • the same nix code can be exported with cargo build write-nix-buildsystem and built by nix alone, from a flake or nixpkgs, without calling cargo again
  • build scripts run in the nix sandbox too; their cargo: directives are parsed by build-rs-libnix, see build scripts and build-rs-libnix

Requirements

  • nix with the nix-command and flakes features, a running nix-daemon and the sandbox enabled (the defaults on NixOS)
  • Linux; developed and tested on x86_64-linux
  • rustc of the same version as the cargo (1.87.0 or 1.99.0); every package of the flake brings the matching one
  • nix-prefetch-git in PATH for git dependencies (part of the devshell)

Regression tests

Each branch's test.nix builds the projects of #18 at pinned revisions and runs their binaries: the 1.99 line passes all 31, among them grok-build; the 1.87 line passes 30 of 31 (union needs a nightly rustc). Details are in the README of rust-1.99.0-libnix.

Exporting the build system

cargo build writes the nix code to target/<profile>/nix on every run. cargo build write-nix-buildsystem writes the same code to a directory of your choice, without building, so your project can be built by nix alone: from a flake, nixpkgs or another project.

   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  flake.nix      β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  nix build system          β”‚
β”‚  β€’ cargo_build_caller.nix  β”‚
β”‚  β€’ derivations/default.nix β”‚
β”‚  β€’ derivations/ ...        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  project source code  β”‚
β”‚  β€’ src/               β”‚
β”‚  β€’ Cargo.toml / ...   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The project's own crates either come from a directory (the default, the project_root argument) or from a tarball given with --url and --hash. For a release of your project, e.g. tag v1.0.0 on GitHub:

  1. tag the release and push it

  2. get the hash of the source tarball:

    nix-prefetch-url https://github.com/<owner>/<project>/archive/refs/tags/v1.0.0.tar.gz
  3. write the build system:

    CARGO_BACKEND=nix cargo build --release write-nix-buildsystem \
      --out-dir ./nix \
      --url https://github.com/<owner>/<project>/archive/refs/tags/v1.0.0.tar.gz \
      --hash <hash from step 2>
  4. test it: nix build --file nix/cargo_build_caller.nix target -L

  5. publish it: commit the directory to your repository, or attach a tarball of it to the release (as cargo (libnix) does, see its releases)

The build system has two entry points:

  1. cargo_build_caller.nix is self-contained: it pins its own nixpkgs and the rustc/cargo of its release, 1.99.0 or 1.87.0 (from fenix, without rust-src, which would end up in the runtime closure of every binary), so build artifacts can be shared between machines, and reads Cargo.dependencies.nix from project_root. It takes system and project_root (default: its own directory)
  2. derivations/default.nix is for flakes and other nix code: you pass pkgs, rustc, cargo, external_crate_dependencies and project_root yourself, see traditional nix code and the flake.nix of this branch

Both return an attribute set with a derivation per unit (e.g. cargo-0_100_0-bin-463824861cef8abc) and target, which links all root units like target/<profile>.

Branches and releases 🌿

cargo (libnix) does not follow every upstream cargo release, only selected Rust toolchains. Each supported toolchain is a patch stack on top of upstream's release of that toolchain, in a branch of its own.

branch what it is
master-libnix this README and the flake.nix serving the releases; no code
rust-1.99.0-libnix the main line, on the newest supported toolchain: new features, README-DEV.md, test.nix; pull requests go here
rust-1.87.0-libnix maintenance branch of the 1.87 line
master upstream cargo, untouched

The naming follows upstream: cargo has a release branch per toolchain (rust-1.87.0, rust-1.99.0) and tags the cargo crate version (0.88.0 is the cargo of Rust 1.87.0, 0.100.0 the one of Rust 1.99.0).

Releases are tags named after the toolchain plus a libnix revision: libnix-1.87.0+v1, libnix-1.87.0+v2, … Each release has a tarball of its generated nix build system on the releases page. The tags stay valid no matter what happens to the branches; flakes and nix code should pin a tag, a release tarball or a commit of master-libnix, not a version branch.

Releasing

  1. tag the release on its branch, e.g. libnix-1.99.0+v2, and attach the tarball of cargo build --release write-nix-buildsystem
  2. on master-libnix, add a release { … } for it to flake.nix and point its line in lines to it (and defaultLine, for a new toolchain); nix flake check runs every line's cargo with its rustc

Supporting a new toolchain

  1. base on upstream's release of that toolchain (the cargo crate tag, e.g. 0.100.0, or the rust-<version> branch), not on an upstream master commit, so the upstream backports are included
  2. rebase the libnix patch stack of the current main line onto it, in a new rust-<version>-libnix branch; squashing the stack into a few logical patches first (nix backend, daemon/runner, templates, write-nix-buildsystem, crate store/GC roots, flake) keeps the conflicts manageable and git diff <upstream tag>.. readable
  3. once cargo builds itself with CARGO_BACKEND=nix and the example projects work, the new branch is the main line and the previous one a maintenance branch
  4. tag the first release of the new line, e.g. libnix-1.99.0+v1, and add it to master-libnix as the default

Maintenance branches

rust-<version>-libnix branches of older toolchains only get fixes (cherry-picked), no new features; a fix released there gets the next tag of that line, e.g. libnix-1.87.0+v5.

Similar projects πŸ“š

project how it differs from cargo (libnix)
crane nix library that runs cargo inside a few derivations (dependencies, then the workspace); you write the nix code
naersk like crane: builds the dependencies and the project in two derivations from Cargo.lock
cargo2nix a separate tool generates a Cargo.nix with a derivation per crate, regenerated when Cargo.lock changes
crate2nix like cargo2nix, based on nixpkgs' buildRustCrate, can also generate during evaluation (IFD)

cargo (libnix) is cargo itself: the derivation per unit comes from cargo's own build plan on every cargo build, during development as well, with no extra tool, nix code or generation step.

Motivation πŸ’‘

This project is xkcd 927.

Commercial support & consulting 🀝

cargo (libnix) is open source and stays that way. If your team wants to use it, or wants Nix builds for Rust in general, I'm available for paid work:

  • adoption: move your Rust workspace to cargo (libnix), write the Cargo.dependencies.nix for your *-sys crates and get your project building in the nix sandbox
  • CI and caching: binary caches, remote builders and shared store paths, so crates built once are never built again on another machine or pipeline
  • exporting: ship your project as a flake or nixpkgs package with write-nix-buildsystem, pinned and reproducible
  • toolchains and platforms: port cargo (libnix) to the Rust version you're on, or to Darwin
  • funded features: cargo check, incremental builds (#3), cross-compilation and wasm. If you need one of these, sponsoring it is the fastest way to get it upstream-ready
  • Nix in general: NixOS deployments, build systems for C/C++ projects, reproducible toolchains

All work is clearly separate from the Rust Project; this is not official Cargo support.

About me

I'm Joachim Schiele, a freelance software engineer based in Germany. I've worked on Nix for over a decade: at Daimler I spent almost three years researching distributed deployment with Nix/NixOS, and before that my diploma thesis covered multi-platform software package management. I founded nixcloud GmbH and have worked on several projects with NLnet, including this one.

Outside Nix, I build web apps in Rust/Leptos and Go backends. I've also done C++ and CMake build systems for embedded and industrial software, and run Linux infrastructure at scale (5000 RHEL machines at Bosch).

πŸ“§ js@lastlog.de: tell me what you're building and where your builds hurt, and I'll reply with how I can help.

Project resources

Sponsors

This project was sponsored by nlnet

nlnet

Legal πŸ“

This is the email response of the Rust Foundation to https://internals.rust-lang.org/t/new-rust-backend-libnix/23848

Your fork falls under the explicit allowance in the Rust trademark policy to "host a fork of the code for the purpose of making changes, additions, or deletions that will be submitted as proposed improvements to the Rust Project". The policy does not restrict the size, depth, or architectural significance of the changes. Adding a new build backend is permitted by this allowance.

You're entitled to publicly host and distribute a fork, in source form, under the Cargo name, provided that: it is clearly presented as unofficial, it is not marketed or promoted as an official or endorsed version of Cargo, and it is positioned as work intended for upstream contribution rather than as a competing product. The measures you mention (namespaced repository, clear disclaimers, experimental framing) are sufficient and appropriate for this, and we appreciate the care you've taken to do that!

Providing commercial support or consulting around this work does not, in itself, violate the trademark policy. The requirement is simply that such services must not be presented as official Cargo support, and must not imply endorsement by the Rust Project or the Rust Foundation. You would only need to seek explicit written permission if you intended to market, brand, or distribute your fork as a product in its own right using the Cargo name, or in a way that a reasonable user could interpret as official Cargo. In those circumstances I think the Rust Foundation's board would most likely want you to choose a new name for it to avoid any confusion.

I hope that's helpful.

Abi Broom Director of Operations Rust Foundation

About

The Rust package manager

Resources

Stars

46 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages