···11+# pkgs.checkpointBuildTools {#sec-checkpoint-build}22+33+`pkgs.checkpointBuildTools` provides a way to build derivations incrementally. It consists of two functions to make checkpoint builds using Nix possible.44+55+For hermeticity, Nix derivations do not allow any state to carry over between builds, making a transparent incremental build within a derivation impossible.66+77+However, we can tell Nix explicitly what the previous build state was, by representing that previous state as a derivation output. This allows the passed build state to be used for an incremental build.88+99+To change a normal derivation to a checkpoint based build, these steps must be taken:1010+ - apply `prepareCheckpointBuild` on the desired derivation1111+ e.g.:1212+```nix1313+checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);1414+```1515+ - change something you want in the sources of the package. (e.g. using a source override)1616+```nix1717+changedVBox = pkgs.virtualbox.overrideAttrs (old: {1818+ src = path/to/vbox/sources;1919+}2020+```2121+ - use `mkCheckpointedBuild changedVBox buildOutput`2222+ - enjoy shorter build times2323+2424+## Example {#sec-checkpoint-build-example}2525+```nix2626+{ pkgs ? import <nixpkgs> {} }: with (pkgs) checkpointBuildTools;2727+let2828+ helloCheckpoint = checkpointBuildTools.prepareCheckpointBuild pkgs.hello;2929+ changedHello = pkgs.hello.overrideAttrs (_: {3030+ doCheck = false;3131+ patchPhase = ''3232+ sed -i 's/Hello, world!/Hello, Nix!/g' src/hello.c3333+ '';3434+ });3535+in checkpointBuildTools.mkCheckpointBuild changedHello helloCheckpoint3636+```
+69
pkgs/build-support/checkpoint-build.nix
···11+{ pkgs }:22+rec {33+ /* Prepare a derivation for local builds.44+ *55+ * This function prepares checkpoint builds by provinding,66+ * containing the build output and the sources for cross checking.77+ * The build output can be used later to allow checkpoint builds88+ * by passing the derivation output to the `mkCheckpointBuild` function.99+ *1010+ * To build a project with checkpoints follow these steps:1111+ * - run prepareIncrementalBuild on the desired derivation1212+ * e.G `incrementalBuildArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);`1313+ * - change something you want in the sources of the package( e.G using source override)1414+ * changedVBox = pkgs.virtuabox.overrideAttrs (old: {1515+ * src = path/to/vbox/sources;1616+ * }1717+ * - use `mkCheckpointedBuild changedVBox buildOutput`1818+ * - enjoy shorter build times1919+ */2020+ prepareCheckpointBuild = drv: drv.overrideAttrs (old: {2121+ outputs = [ "out" ];2222+ name = drv.name + "-checkpointArtifacts";2323+ # To determine differences between the state of the build directory2424+ # from an earlier build and a later one we store the state of the build2525+ # directory before build, but after patch phases.2626+ # This way, the same derivation can be used multiple times and only changes are detected.2727+ # Additionally Removed files are handled correctly in later builds.2828+ preBuild = (old.preBuild or "") + ''2929+ mkdir -p $out/sources3030+ cp -r ./* $out/sources/3131+ '';3232+3333+ # After the build the build directory is copied again3434+ # to get the output files.3535+ # We copy the complete build folder, to take care for3636+ # Build tools, building in the source directory, instead of3737+ # having a build root directory, e.G the Linux kernel.3838+ installPhase = ''3939+ runHook preCheckpointInstall4040+ mkdir -p $out/outputs4141+ cp -r ./* $out/outputs/4242+ runHook postCheckpointInstall4343+ '';4444+ });4545+4646+ /* Build a derivation based on the checkpoint output generated by4747+ * the `prepareCheckpointBuild function.4848+ *4949+ * Usage:5050+ * let5151+ * checkpointArtifacts = prepareCheckpointBuild drv5252+ * in mkCheckpointedBuild drv checkpointArtifacts5353+ */5454+ mkCheckpointedBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: {5555+ # The actual checkpoint build phase.5656+ # We compare the changed sources from a previous build with the current and create a patch5757+ # Afterwards we clean the build directory to copy the previous output files (Including the sources)5858+ # The source difference patch is applied to get the latest changes again to allow short build times.5959+ preBuild = (old.preBuild or "") + ''6060+ set +e6161+ diff -ur ${previousBuildArtifacts}/sources ./ > sourceDifference.patch6262+ set -e6363+ shopt -s extglob dotglob6464+ rm -r !("sourceDifference.patch")6565+ ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/outputs/* .6666+ patch -p 1 -i sourceDifference.patch6767+ '';6868+ });6969+}