Skip to content

Latest commit

 

History

140 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

overlay: is Nix's dynamic-derivations feature viable yet?

Nix gained a real feature for splitting one derivation's build into many independently-cacheable pieces, resolved at build time instead of eval time: builtins.outputOf plus the builder-rpc-v0/nix store submit-output protocol (NixOS/nix#15793, merged 2026-07-21, not yet in a stable release). In principle this lets a C/C++ package's few thousand translation units become a few thousand separately-cached Nix derivations — patch one file, only that file (and whatever links against it) rebuilds, at any scale, without import-from-derivation's eval-time cost.

That's the promise. This repo exists to find out whether it holds up against real, unmodified nixpkgs packages — not toy examples — and to report the answer honestly, wins and losses both.

What we're testing

Four independent libraries have each built their own layer on top of this primitive, without coordinating with each other. Testing all four against the same real packages tells us whether the underlying Nix feature is viable, not just whether one library's design happens to work:

  • nixgg — Go-based shim, splits a package's build into per-translation-unit derivations. openssl/hello/mosh/zstd outputs.
  • dyn-drvs — the same idea in pure Nix (accelerate.mkAcceleratedStdenv). dyndrv-* outputs.
  • nix-ninja — a drop-in ninja replacement that turns any meson/cmake project's real build graph into dynamic derivations. nixninja-* outputs.
  • drowse — a different angle: defers a whole package's evaluation into a nested Nix instance, avoiding IFD rather than splitting a build. drowse-* outputs.

Every package here is real, unmodified (or minimally patched) nixpkgs — openssl, freetype, zstd, mosh, giflib, tree, and more. Full per-package results, exact error text, and root causes: benchmarks/RESULTS.md. Live benchmark trend: https://nix-dyn-drv.github.io/overlay/benchmarks/dashboard/.

What we found

It works, dramatically, on the right shape of package. nixgg's mechanism rebuilds only 2 of openssl's 2213 translation units on a one-line patch — that's the headline case for why this feature exists.

It's not free, and not universal yet. dyn-drvs' per-TU splitting costs ~80ms of registration overhead per derivation; on a package with many small, fast-compiling files (freetype), that overhead outweighs the savings — measured directly here at ~6.7x slower than a plain build, not faster. And surveying more real packages surfaced a series of distinct bugs in dyn-drvs, mostly concentrated in one pattern: every package that failed used either cmake's generated build system or autotools' automake dependency-file idiom; every package that passed cleanly used a plain, hand-written Makefile. That's a real, current limit on viability, not a fluke of any one package — see benchmarks/RESULTS.md for the full list. protobuf (cmake, ~221 TUs, real gtest/abseil-cpp deps) confirms one of those bugs — a freshly-linked executable losing its execute bit — at much higher stakes than its original small-Makefile discovery (libb64): protobuf's own build re-executes its just-linked protoc as a code generator, so the missing exec bit takes down the whole build, not just an optional self-test.

Two more independent mechanisms confirm the underlying feature is sound, even where one library's implementation has gaps. nix-ninja (a separate implementation, written in Rust) also builds a real package cleanly via the same primitive. drowse proves out the other half of the story — using dynamic derivations to avoid IFD during evaluation, not just to split a build.

This repo tracks a moving target, not a snapshot. A daily job (nixpkgs-update.yml) bumps nixpkgs-unstable and rebuilds every proven package against it, auto-merging if nothing regressed. Viability has to hold up against nixpkgs as it actually changes, not just the revision this repo happened to start from.

Every bug found in dyn-drvs during this work has been reported upstream with a minimal repro and root cause — several are already fixed as of this writing, and this repo re-tests against each fix. That feedback loop is as much the point of this repo as the benchmark numbers: proving viability means finding the real gaps, not just citing the best-case demo.

Quickstart

$ ./try-it-out/run-nix.sh build --impure --no-link --print-out-paths .#dyndrv-freetype

run-nix.sh fetches a pinned NixOS/nix build and runs it against a local store, since the system daemon doesn't support builder-rpc-v0 yet. Same script builds nixninja-argp/drowse-hello. nixgg packages (openssl, hello, mosh, zstd) build with plain nix build. CI uses run-nix-ci.sh, which points the same Nix at the real /nix/store — safe there since each job gets its own VM.

About

Overlay of nixpkgs that uses dynamic derivations

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages