···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 derivation
1111+ e.g.:
1212+```nix
1313+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+```nix
1717+changedVBox = pkgs.virtualbox.overrideAttrs (old: {
1818+ src = path/to/vbox/sources;
1919+}
2020+```
2121+ - use `mkCheckpointedBuild changedVBox buildOutput`
2222+ - enjoy shorter build times
2323+2424+## Example {#sec-checkpoint-build-example}
2525+```nix
2626+{ pkgs ? import <nixpkgs> {} }: with (pkgs) checkpointBuildTools;
2727+let
2828+ 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.c
3333+ '';
3434+ });
3535+in checkpointBuildTools.mkCheckpointBuild changedHello helloCheckpoint
3636+```
+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 builds
88+ * 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 derivation
1212+ * 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 times
1919+ */
2020+ prepareCheckpointBuild = drv: drv.overrideAttrs (old: {
2121+ outputs = [ "out" ];
2222+ name = drv.name + "-checkpointArtifacts";
2323+ # To determine differences between the state of the build directory
2424+ # from an earlier build and a later one we store the state of the build
2525+ # 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/sources
3030+ cp -r ./* $out/sources/
3131+ '';
3232+3333+ # After the build the build directory is copied again
3434+ # to get the output files.
3535+ # We copy the complete build folder, to take care for
3636+ # Build tools, building in the source directory, instead of
3737+ # having a build root directory, e.G the Linux kernel.
3838+ installPhase = ''
3939+ runHook preCheckpointInstall
4040+ mkdir -p $out/outputs
4141+ cp -r ./* $out/outputs/
4242+ runHook postCheckpointInstall
4343+ '';
4444+ });
4545+4646+ /* Build a derivation based on the checkpoint output generated by
4747+ * the `prepareCheckpointBuild function.
4848+ *
4949+ * Usage:
5050+ * let
5151+ * checkpointArtifacts = prepareCheckpointBuild drv
5252+ * in mkCheckpointedBuild drv checkpointArtifacts
5353+ */
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 patch
5757+ # 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 +e
6161+ diff -ur ${previousBuildArtifacts}/sources ./ > sourceDifference.patch
6262+ set -e
6363+ shopt -s extglob dotglob
6464+ rm -r !("sourceDifference.patch")
6565+ ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/outputs/* .
6666+ patch -p 1 -i sourceDifference.patch
6767+ '';
6868+ });
6969+}