Clean the source (nix)

Most experienced, or even intermediate, Nix users probably already know to clean their sources. However, I can't recall any Nix tutorials explaining how or why you should "clean the source". Simply, you should filter a source tree to remove version control files and build results. This prevents those files from contaminating the tree imported by the Nix builder, and this leads to more repeatable builds.

Why is filtering the source files important? Well, consider the following example. This is a very simple, stripped down, derivation to build a Go program. Note that we import the entire file tree from the current directory for the attribute src.

{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation rec {
  pname = "hello";
  version = "0.1.0";
  src = ./.;   # <-- Import everything

  configurePhase = ''
    export GOCACHE="$TMPDIR/go-cache"
    export GOPATH="$TMPDIR/go"
  '';
  buildPhase = ''
    ${pkgs.go}/bin/go build -o ${pname} .
  '';
  installPhase = ''
    mkdir -p $out/bin
    cp ${pname} $out/bin/
  '';
}

Example 1:  Repeat invocations of nix-build rebuild.

More than just the fact that each invocation of nix-build does a complete build of the derivation, note that the hash for the derivation changes after every build. That hash changes when any of the inputs to the derivation changes. The reason for the repeated rebuilds is that Nix believes that the build's dependencies have changed.

The derivation's hash is based on all of the inputs to the derivation, and those inputs include a recursive evaluation of the source files found in ./.. The key observation is that each invocation of nix-build creates a symbolic link, called result, in the current directory. This changes the contents of the source directory, which changes the file tree imported by the line src = ./.. The source tree is changing between invocations!

Fortunately, filtering the symbolic links, as well as some other files, is quite easy. A good function, cleanSource exists in nixpkgs.

- src = ./.;
+ src = pkgs.lib.cleanSource ./.;

Example 2:  Repeat invocations of nix-build rebuild with a clean source tree.

Summary

Omitting cleanSource will not break your derivation, but it does make it much more difficult to get repeatable builds. In particular, if you are doing in-tree builds, symbolic links created by nix-build will change the source tree after every build. A simple change to the derivation eliminates much of the cruft in your source directory, and helps achieve repeatable builds.

Join the discussion...