Customizing builds

All derivations, whether they are configured through buildPackage, cargoBuild, or even mkCargoDerivation, eventually delegate to mkDerivation which is defined by nixpkgs.

At its heart, mkDerivation builds up a big bash script which is executed by the builder. Inputs are added to the execution $PATH, libraries are added to include paths, and all other variables are set as shell variables. But these scripts also come with a small framework for running various different phases. Many of these phases also come with their own hooks which are shell functions which can be subscribed to execute before and/or after a particular phase has run.

Although build phases and their hooks allow for easily extending and customizing the build instructions for a particular derivation, it can become difficult to identify exactly where a bit of logic should execute. The following are a good set of resources to consult when in doubt:

  1. The nixpkgs manual for describing the default set of build phases and their hooks
  2. The crane API reference for additional hooks it introduces
  3. Setting NIX_DEBUG to a non-zero value will cause the builder to print out various variables and commands it will run (increasing values will increase the verbosity).
  4. When all else fails source for the generic build scripts themselves can be useful

All that out of the way, here's a quick example of how to use the build phases and hooks to customize a particular build:

craneLib.buildPackage {
  src = craneLib.cleanCargoSource (craneLib.path ./.);

  # Define a list of function names to execute before the `configurePhase` runs
  preConfigurePhases = [
    "foo"
    "bar"
  ];

  # Define the functions themselves
  foo = ''
    # double the amount of rust test threads we can use
    # Note that crane will set these defaults as a `postPatchHook` which
    # should have already run by the time the preConfigurePhases are called
    export RUST_TEST_THREADS=$((RUST_TEST_THREADS * 2))
  '';

  bar = ''
    # decrement by one test thread if running in release mode
    if [[ "${CARGO_PROFILE}" == "release" ]]; then
      export RUST_TEST_THREADS=$((RUST_TEST_THREADS - 2))
    fi
  '';

  # Lastly, add postInstall to install additional items after
  # the default installPhase has run and installed the package binaries
  postInstall = ''
    echo "hello world" > $out/hello.txt
    # also install the README.md for good measure
    cp README.md $out/
  '';
}