1{
2 lib,
3 buildPackages,
4 config,
5}:
6
7let
8 # rudimentary support for cross-compiling
9 # see: https://github.com/NixOS/nixpkgs/pull/279487#discussion_r1444449726
10 inherit (buildPackages)
11 mktemp
12 rsync
13 ;
14
15 /*
16 Build a derivation based on the checkpoint output generated by
17 * the `prepareCheckpointBuild` function.
18 *
19 * Usage:
20 * let
21 * checkpointArtifacts = prepareCheckpointBuild drv;
22 * in mkCheckpointBuild drv checkpointArtifacts
23 */
24 mkCheckpointBuild =
25 drv: checkpointArtifacts:
26 drv.overrideAttrs (old: {
27 # The actual checkpoint build phase.
28 # We compare the changed sources from a previous build with the current and create a patch.
29 # Afterwards we clean the build directory and copy the previous output files (including the sources).
30 # The source difference patch is then applied to get the latest changes again to allow short build times.
31 preBuild = (old.preBuild or "") + ''
32 set +e
33 sourceDifferencePatchFile=$(${mktemp}/bin/mktemp)
34 diff -ur ${checkpointArtifacts}/sources ./ > "$sourceDifferencePatchFile"
35 set -e
36 shopt -s dotglob
37 rm -r *
38 ${rsync}/bin/rsync \
39 --checksum --times --atimes --chown=$USER:$USER --chmod=+w \
40 -r ${checkpointArtifacts}/outputs/ .
41 patch -p 1 -i "$sourceDifferencePatchFile"
42 rm "$sourceDifferencePatchFile"
43 '';
44 });
45in
46
47rec {
48 inherit mkCheckpointBuild;
49 /*
50 Prepare a derivation for local builds.
51 *
52 * This function prepares checkpoint builds by storing
53 * the build output and the sources for cross checking.
54 * The build output can be used later to allow checkpoint builds
55 * by passing the derivation output to the `mkCheckpointBuild` function.
56 *
57 * To build a project with checkpoints, follow these steps:
58 * - run `prepareCheckpointBuild` on the desired derivation, e.g.
59 * checkpointArtifacts = prepareCheckpointBuild virtualbox;
60 * - change something you want in the sources of the package,
61 * e.g. using source override:
62 * changedVBox = pkgs.virtuabox.overrideAttrs (old: {
63 * src = path/to/vbox/sources;
64 * };
65 * - use `mkCheckpointBuild changedVBox checkpointArtifacts`
66 * - enjoy shorter build times
67 */
68 prepareCheckpointBuild =
69 drv:
70 drv.overrideAttrs (old: {
71 outputs = [ "out" ];
72 name = drv.name + "-checkpointArtifacts";
73 # To determine differences between the state of the build directory
74 # from an earlier build and a later one we store the state of the build
75 # directory before build, but after patch phases.
76 # This way, the same derivation can be used multiple times and only changes are detected.
77 # Additionally, removed files are handled correctly in later builds.
78 preBuild = (old.preBuild or "") + ''
79 mkdir -p $out/sources
80 cp -r ./* $out/sources/
81 '';
82
83 # After the build, the build directory is copied again
84 # to get the output files.
85 # We copy the complete build folder, to take care of
86 # build tools that build in the source directory, instead of
87 # having a separate build directory such as the Linux kernel.
88 installPhase = ''
89 runHook preCheckpointInstall
90 mkdir -p $out/outputs
91 cp -r ./* $out/outputs/
92 runHook postCheckpointInstall
93 unset postPhases
94 '';
95
96 dontFixup = true;
97 doInstallCheck = false;
98 doDist = false;
99 });
100}
101// lib.optionalAttrs config.allowAliases {
102 mkCheckpointedBuild = lib.warn "`mkCheckpointedBuild` is deprecated, use `mkCheckpointBuild` instead!" mkCheckpointBuild;
103}