nix debug spurrious rebuilds

2023-09-27 · 2 min read

I have a Rust workspace full of cargo crates (and other things). Using ipetkov/crane, the workspace dependencies should only rebuild if the Cargo.toml or Cargo.lock files change. Sadly that wasn't the case--touching any source file would cause a full rebuild from scratch, which takes ages.

That's where gabriella439/nix-diff comes to the rescue: point it at two nix derivations and it'll tell you exactly what's different between them. We can use this to debug our rebuild problem.

# Add `jq` and `nix-diff` to our shell temporarily.
$ nix shell nixpkgs#jq nixpkgs#nix-diff

# Build the package twice, with an unrelated file change in between.
$ nix derivation show .#my-pkg | jq -r 'keys[0]' > before_drv
$ echo "// bump" >> ./file/causing/rebuild
$ nix derivation show .#my-pkg | jq -r 'keys[0]' > after_drv

# Let's see the difference:
$ nix-diff "$(cat before_drv)" "$(cat after_drv)"

- /nix/store/5gcg2l4bnmi3plszwrcihyshbzbd4kz1-my-pkg-0.1.0.drv:{out}
+ /nix/store/rybdvgqwrb6hjwys18yirchq1d20c8rp-my-pkg-0.1.0.drv:{out}
 The input source named `source` differs
 The input derivation named `my-pkg-deps-0.1.0` differs
  - /nix/store/v523hpxdbzxwwlkgzb3hdhbclxqh7bm4-my-pkg-deps-0.1.0.drv:{out}
  + /nix/store/9a5044x65ff0jyddpzwx9gbagns4klfa-my-pkg-deps-0.1.0.drv:{out}
   The input source named `source` differs
   The environments do not match:
        -isystem /nix/store/9c7h6f3ibkd4p528z3f1ydbm6lr4znpn-clang-16.0.6-lib/lib/clang/16/include -isystem
      + /nix/store/lm1p696bwrkppi746wc4hmqvf117zqsa-source/libc-shim/include
      - /nix/store/hf4arhzcg3g511yzlq9lspgi9s51k5ms-source/libc-shim/include

Of course... I have an absolute reference to the full source in an env variable. That'll do it. Putting the sub-directory into its own tiny nix store path seems to do the trick. Thanks nix-diff!