From 3f4e3a862f20370d36a59a9534788099225d3484 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Wed, 6 Apr 2022 16:14:42 +0200 Subject: [PATCH 01/16] Checkpointedbuilds: add derivation override functions One can use this functions to allow incremental builds of derivations --- pkgs/build-support/build-incremental.nix | 37 ++++++++++++++++++++++++ pkgs/top-level/all-packages.nix | 2 ++ 2 files changed, 39 insertions(+) create mode 100644 pkgs/build-support/build-incremental.nix diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/build-incremental.nix new file mode 100644 index 000000000000..b2a4fd461709 --- /dev/null +++ b/pkgs/build-support/build-incremental.nix @@ -0,0 +1,37 @@ +{ pkgs }: +rec { + /* Prepare a derivation for local builds. + * + * This function adds an additional outout for a derivation, + * containing the build output. + * The build output can be used later to allow incremental builds + * by passing the `buildOut` output to the `mkIncrementalBuild` function. + */ + prepareIncrementalBuild = drv: drv.overrideAttrs (old: { + outputs = (old.outputs or [ "out" ]) ++ [ "buildOut" ]; + installPhase = pkgs.lib.optionalString (!(builtins.hasAttr "outputs" old)) '' + mkdir -p $out + '' + (old.installPhase or "") + '' + mkdir -p $buildOut + cp -r ./* $buildOut/ + ''; + }); + + /* Build a derivation incrementally based on the output generated by + * the `prepareIncrementalBuild function. + * + * Usage: + * let + * buildOutput = (prepareIncrementalBuild drv).buildOut + * in mkIncrementalBuild drv buildOutput + */ + mkIncrementalBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: { + prePatch = '' + for file in $(diff -r ./ ${previousBuildArtifacts} --brief | grep "Files" |sed 's/^Only in \([^:]*\): /\1\//' | sed 's/^Files \(.*\) and .* differ/\1/') + do + touch $file + done + ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/* . + '' + (old.prePatch or ""); + }); +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 908f0f608fdc..9ff07d888b7f 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -429,6 +429,8 @@ with pkgs; camunda-modeler = callPackage ../applications/misc/camunda-modeler { }; + inncrementalBuildTools = callPackage ../build-support/build-incremental.nix {}; + caroline = callPackage ../development/libraries/caroline { }; cartridges = callPackage ../applications/misc/cartridges { }; From 8beb56244d6baf34bc5fbf010e79243fe59b6394 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Thu, 7 Apr 2022 14:47:56 +0200 Subject: [PATCH 02/16] checkpointedBuilds: add usage example based on virtualbox --- pkgs/build-support/build-incremental.nix | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/build-incremental.nix index b2a4fd461709..b8402b8aadff 100644 --- a/pkgs/build-support/build-incremental.nix +++ b/pkgs/build-support/build-incremental.nix @@ -2,10 +2,20 @@ rec { /* Prepare a derivation for local builds. * - * This function adds an additional outout for a derivation, + * This function adds an additional output for a derivation, * containing the build output. * The build output can be used later to allow incremental builds * by passing the `buildOut` output to the `mkIncrementalBuild` function. + * + * To build a project incrementaly follow these steps: + * - run prepareIncrementalBuild on the desired derivation + * e.G `buildOutput = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox).buildOut;` + * - change something you want in the sources of the package( e.G using source override) + * changedVBox = pkgs.virtuabox.overrideAttrs (old: { + * src = path/to/vbox/sources; + * } + * - use `mkIncrementalBuild changedVBox buildOutput` + * - enjoy shorter build times */ prepareIncrementalBuild = drv: drv.overrideAttrs (old: { outputs = (old.outputs or [ "out" ]) ++ [ "buildOut" ]; From 1cd6b7fdc37cdc7a4a31c87aa31e4ebf5ed930c8 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Thu, 7 Apr 2022 16:27:49 +0200 Subject: [PATCH 03/16] checkpointedBuilds: rename buildOut to checkpointedBuildArtifacts --- pkgs/build-support/build-incremental.nix | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/build-incremental.nix index b8402b8aadff..6cdf07f0b801 100644 --- a/pkgs/build-support/build-incremental.nix +++ b/pkgs/build-support/build-incremental.nix @@ -9,7 +9,7 @@ rec { * * To build a project incrementaly follow these steps: * - run prepareIncrementalBuild on the desired derivation - * e.G `buildOutput = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox).buildOut;` + * e.G `incrementalBuildArtifacts = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox).incrementalBuildArtifacts;` * - change something you want in the sources of the package( e.G using source override) * changedVBox = pkgs.virtuabox.overrideAttrs (old: { * src = path/to/vbox/sources; @@ -18,12 +18,12 @@ rec { * - enjoy shorter build times */ prepareIncrementalBuild = drv: drv.overrideAttrs (old: { - outputs = (old.outputs or [ "out" ]) ++ [ "buildOut" ]; + outputs = (old.outputs or [ "out" ]) ++ [ "incrementalBuildArtifacts" ]; installPhase = pkgs.lib.optionalString (!(builtins.hasAttr "outputs" old)) '' mkdir -p $out '' + (old.installPhase or "") + '' - mkdir -p $buildOut - cp -r ./* $buildOut/ + mkdir -p $incrementalBuildArtifacts + cp -r ./* $incrementalBuildArtifacts/ ''; }); @@ -32,8 +32,8 @@ rec { * * Usage: * let - * buildOutput = (prepareIncrementalBuild drv).buildOut - * in mkIncrementalBuild drv buildOutput + * incrementalBuildArtifacts = (prepareIncrementalBuild drv).incrementalBuildArtifacts + * in mkIncrementalBuild drv incrementalBuildArtifacts */ mkIncrementalBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: { prePatch = '' From 17e88c2890068e944c61dbaf7493ae12417b8944 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Thu, 7 Apr 2022 16:28:20 +0200 Subject: [PATCH 04/16] checkpointedBuild: add checkpointed build test based on pkgs hello --- pkgs/test/default.nix | 2 + pkgs/test/incrementalBuild/default.nix | 57 +++++++++++++++++ .../hello-additionalFile.patch | 62 +++++++++++++++++++ .../incrementalBuild/hello-removeFile.patch | 62 +++++++++++++++++++ pkgs/test/incrementalBuild/hello.patch | 26 ++++++++ 5 files changed, 209 insertions(+) create mode 100644 pkgs/test/incrementalBuild/default.nix create mode 100644 pkgs/test/incrementalBuild/hello-additionalFile.patch create mode 100644 pkgs/test/incrementalBuild/hello-removeFile.patch create mode 100644 pkgs/test/incrementalBuild/hello.patch diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index 29dc4b5192ec..8ca6c4faf56e 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -113,6 +113,8 @@ with pkgs; install-shell-files = callPackage ./install-shell-files {}; + incremental-build = callPackage ./incrementalBuild {}; + kernel-config = callPackage ./kernel.nix {}; ld-library-path = callPackage ./ld-library-path {}; diff --git a/pkgs/test/incrementalBuild/default.nix b/pkgs/test/incrementalBuild/default.nix new file mode 100644 index 000000000000..cff1efee9a12 --- /dev/null +++ b/pkgs/test/incrementalBuild/default.nix @@ -0,0 +1,57 @@ +{ hello, buildIncremental, runCommandNoCC, texinfo, stdenv, rsync }: +let + baseHello = buildIncremental.prepareIncrementalBuild hello; + patchedHello = hello.overrideAttrs (old: { + buildInputs = [ texinfo ]; + src = runCommandNoCC "patch-hello-src" { } '' + mkdir -p $out + cd $out + tar xf ${hello.src} --strip-components=1 + patch -p1 < ${./hello.patch} + ''; + }); + incrementalBuiltHello = buildIncremental.mkIncrementalBuild patchedHello baseHello.incrementalBuildArtifacts; + + incrementalBuiltHelloWithCheck = incrementalBuiltHello.overrideAttrs (old: { + doCheck = true; + checkPhase = '' + echo "checking if unchanged source file is not recompiled" + [ "$(stat --format="%Y" lib/exitfail.o)" = "$(stat --format="%Y" ${baseHello.incrementalBuildArtifacts}/lib/exitfail.o)" ] + ''; + }); + + baseHelloRemoveFile = buildIncremental.prepareIncrementalBuild (hello.overrideAttrs (old: { + patches = [ ./hello-additionalFile.patch ]; + })); + + preparedHelloRemoveFileSrc = runCommandNoCC "patch-hello-src" { } '' + mkdir -p $out + cd $out + tar xf ${hello.src} --strip-components=1 + patch -p1 < ${./hello-additionalFile.patch} + ''; + + patchedHelloRemoveFile = hello.overrideAttrs (old: { + buildInputs = [ texinfo ]; + src = runCommandNoCC "patch-hello-src" { } '' + mkdir -p $out + cd $out + ${rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${preparedHelloRemoveFileSrc}/* . + patch -p1 < ${./hello-removeFile.patch} + ''; + }); + + incrementalBuiltHelloWithRemovedFile = buildIncremental.mkIncrementalBuild patchedHelloRemoveFile baseHelloRemoveFile.incrementalBuildArtifacts; +in +stdenv.mkDerivation { + name = "patched-hello-returns-correct-output"; + buildCommand = '' + touch $out + + echo "testing output of hello binary" + [ "$(${incrementalBuiltHelloWithCheck}/bin/hello)" = "Hello, incremental world!" ] + echo "testing output of hello with removed file" + [ "$(${incrementalBuiltHelloWithRemovedFile}/bin/hello)" = "Hello, incremental world!" ] + ''; +} + diff --git a/pkgs/test/incrementalBuild/hello-additionalFile.patch b/pkgs/test/incrementalBuild/hello-additionalFile.patch new file mode 100644 index 000000000000..31fbe034909e --- /dev/null +++ b/pkgs/test/incrementalBuild/hello-additionalFile.patch @@ -0,0 +1,62 @@ +diff --git a/Makefile.in b/Makefile.in +index 0805eda..77b000c 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -227,7 +227,7 @@ am_lib_libhello_a_OBJECTS = lib/c-ctype.$(OBJEXT) \ + lib/quotearg.$(OBJEXT) lib/strnlen1.$(OBJEXT) \ + lib/unistd.$(OBJEXT) lib/wctype-h.$(OBJEXT) \ + lib/xmalloc.$(OBJEXT) lib/xalloc-die.$(OBJEXT) \ +- lib/xstrndup.$(OBJEXT) ++ lib/xstrndup.$(OBJEXT) src/additionalFile.$(OBJEXT) + lib_libhello_a_OBJECTS = $(am_lib_libhello_a_OBJECTS) + am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(infodir)" \ + "$(DESTDIR)$(man1dir)" +@@ -1380,7 +1380,7 @@ lib_libhello_a_SOURCES = lib/c-ctype.h lib/c-ctype.c lib/c-strcase.h \ + lib/gettext.h lib/localcharset.h lib/localcharset.c \ + lib/progname.h lib/progname.c lib/quotearg.c lib/strnlen1.h \ + lib/strnlen1.c lib/unistd.c lib/wctype-h.c lib/xmalloc.c \ +- lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c ++ lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c src/additionalFile.c + lib_libhello_a_LIBADD = $(gl_LIBOBJS) + lib_libhello_a_DEPENDENCIES = $(gl_LIBOBJS) + EXTRA_lib_libhello_a_SOURCES = lib/stripslash.c lib/error.c \ +diff --git a/src/additionalFile.c b/src/additionalFile.c +new file mode 100644 +index 0000000..34d683d +--- /dev/null ++++ b/src/additionalFile.c +@@ -0,0 +1,6 @@ ++#include "config.h" ++#include "system.h" ++ ++int somefunc() { ++ return 0; ++} +diff --git a/src/hello.c b/src/hello.c +index 453962f..df67de2 100644 +--- a/src/hello.c ++++ b/src/hello.c +@@ -57,7 +57,11 @@ main (int argc, char *argv[]) + #endif + + /* Having initialized gettext, get the default message. */ +- greeting_msg = _("Hello, world!"); ++ if (somefunc() == 0) { ++ greeting_msg = _("Hello, world!"); ++ } else { ++ greeting_msg = _("Hello, incremental world!"); ++ } + + /* Even exiting has subtleties. On exit, if any writes failed, change + the exit status. The /dev/full device on GNU/Linux can be used for +diff --git a/src/system.h b/src/system.h +index d1acac2..935b955 100644 +--- a/src/system.h ++++ b/src/system.h +@@ -42,4 +42,6 @@ + /* Check for errors on write. */ + # include "closeout.h" + ++int somefunc(); ++ + #endif /* HELLO_SYSTEM_H */ diff --git a/pkgs/test/incrementalBuild/hello-removeFile.patch b/pkgs/test/incrementalBuild/hello-removeFile.patch new file mode 100644 index 000000000000..99ecf1e298bb --- /dev/null +++ b/pkgs/test/incrementalBuild/hello-removeFile.patch @@ -0,0 +1,62 @@ +diff --git a/Makefile.in b/Makefile.in +index 77b000c..0805eda 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -227,7 +227,7 @@ am_lib_libhello_a_OBJECTS = lib/c-ctype.$(OBJEXT) \ + lib/quotearg.$(OBJEXT) lib/strnlen1.$(OBJEXT) \ + lib/unistd.$(OBJEXT) lib/wctype-h.$(OBJEXT) \ + lib/xmalloc.$(OBJEXT) lib/xalloc-die.$(OBJEXT) \ +- lib/xstrndup.$(OBJEXT) src/additionalFile.$(OBJEXT) ++ lib/xstrndup.$(OBJEXT) + lib_libhello_a_OBJECTS = $(am_lib_libhello_a_OBJECTS) + am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(infodir)" \ + "$(DESTDIR)$(man1dir)" +@@ -1380,7 +1380,7 @@ lib_libhello_a_SOURCES = lib/c-ctype.h lib/c-ctype.c lib/c-strcase.h \ + lib/gettext.h lib/localcharset.h lib/localcharset.c \ + lib/progname.h lib/progname.c lib/quotearg.c lib/strnlen1.h \ + lib/strnlen1.c lib/unistd.c lib/wctype-h.c lib/xmalloc.c \ +- lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c src/additionalFile.c ++ lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c + lib_libhello_a_LIBADD = $(gl_LIBOBJS) + lib_libhello_a_DEPENDENCIES = $(gl_LIBOBJS) + EXTRA_lib_libhello_a_SOURCES = lib/stripslash.c lib/error.c \ +diff --git a/src/additionalFile.c b/src/additionalFile.c +deleted file mode 100644 +index 34d683d..0000000 +--- a/src/additionalFile.c ++++ /dev/null +@@ -1,6 +0,0 @@ +-#include "config.h" +-#include "system.h" +- +-int somefunc() { +- return 0; +-} +diff --git a/src/hello.c b/src/hello.c +index df67de2..768285a 100644 +--- a/src/hello.c ++++ b/src/hello.c +@@ -36,6 +36,10 @@ static const struct option longopts[] = { + static void print_help (void); + static void print_version (void); + ++int somefunc() { ++ return 1; ++} ++ + int + main (int argc, char *argv[]) + { +diff --git a/tests/hello-1 b/tests/hello-1 +index e15fa95..3b7a815 100755 +--- a/tests/hello-1 ++++ b/tests/hello-1 +@@ -21,7 +21,7 @@ export LANGUAGE LC_ALL LC_MESSAGES LANG + + tmpfiles="hello-test1.ok" + cat < hello-test1.ok +-Hello, world! ++Hello, incremental world! + EOF + + tmpfiles="$tmpfiles hello-test1.out" diff --git a/pkgs/test/incrementalBuild/hello.patch b/pkgs/test/incrementalBuild/hello.patch new file mode 100644 index 000000000000..3d0d50c2f20e --- /dev/null +++ b/pkgs/test/incrementalBuild/hello.patch @@ -0,0 +1,26 @@ +diff --git a/src/hello.c b/src/hello.c +index 182303c..453962f 100644 +--- a/src/hello.c ++++ b/src/hello.c +@@ -57,7 +57,7 @@ main (int argc, char *argv[]) + #endif + + /* Having initialized gettext, get the default message. */ +- greeting_msg = _("Hello, world!"); ++ greeting_msg = _("Hello, incremental world!"); + + /* Even exiting has subtleties. On exit, if any writes failed, change + the exit status. The /dev/full device on GNU/Linux can be used for +diff --git a/tests/hello-1 b/tests/hello-1 +index 3b7a815..e15fa95 100755 +--- a/tests/hello-1 ++++ b/tests/hello-1 +@@ -21,7 +21,7 @@ export LANGUAGE LC_ALL LC_MESSAGES LANG + + tmpfiles="hello-test1.ok" + cat < hello-test1.ok +-Hello, world! ++Hello, incremental world! + EOF + + tmpfiles="$tmpfiles hello-test1.out" From cd6c65fe2d96e8342f85f8467dee9b76618a144d Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Wed, 15 Jun 2022 11:10:47 +0200 Subject: [PATCH 05/16] checkpointedBuild: consider removing files and make buildartifacts the only output of the prepare step --- pkgs/build-support/build-incremental.nix | 43 ++++++++++++++---------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/build-incremental.nix index 6cdf07f0b801..9c779befb20e 100644 --- a/pkgs/build-support/build-incremental.nix +++ b/pkgs/build-support/build-incremental.nix @@ -2,14 +2,14 @@ rec { /* Prepare a derivation for local builds. * - * This function adds an additional output for a derivation, - * containing the build output. + * This function prepares incremental builds by provinding, + * containing the build output and the sources for cross checking. * The build output can be used later to allow incremental builds - * by passing the `buildOut` output to the `mkIncrementalBuild` function. + * by passing the derivation output to the `mkIncrementalBuild` function. * * To build a project incrementaly follow these steps: * - run prepareIncrementalBuild on the desired derivation - * e.G `incrementalBuildArtifacts = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox).incrementalBuildArtifacts;` + * e.G `incrementalBuildArtifacts = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox);` * - change something you want in the sources of the package( e.G using source override) * changedVBox = pkgs.virtuabox.overrideAttrs (old: { * src = path/to/vbox/sources; @@ -18,12 +18,16 @@ rec { * - enjoy shorter build times */ prepareIncrementalBuild = drv: drv.overrideAttrs (old: { - outputs = (old.outputs or [ "out" ]) ++ [ "incrementalBuildArtifacts" ]; - installPhase = pkgs.lib.optionalString (!(builtins.hasAttr "outputs" old)) '' - mkdir -p $out - '' + (old.installPhase or "") + '' - mkdir -p $incrementalBuildArtifacts - cp -r ./* $incrementalBuildArtifacts/ + outputs = [ "out" ]; + name = drv.name + "-incrementalBuildArtifacts"; + preBuild = (old.preBuild or "") + '' + mkdir -p $out/sources + cp -r ./* $out/sources/ + ''; + + installPhase = '' + mkdir -p $out/outputs + cp -r ./* $out/outputs/ ''; }); @@ -32,16 +36,19 @@ rec { * * Usage: * let - * incrementalBuildArtifacts = (prepareIncrementalBuild drv).incrementalBuildArtifacts + * incrementalBuildArtifacts = prepareIncrementalBuild drv * in mkIncrementalBuild drv incrementalBuildArtifacts */ mkIncrementalBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: { - prePatch = '' - for file in $(diff -r ./ ${previousBuildArtifacts} --brief | grep "Files" |sed 's/^Only in \([^:]*\): /\1\//' | sed 's/^Files \(.*\) and .* differ/\1/') - do - touch $file - done - ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/* . - '' + (old.prePatch or ""); + preBuild = (old.preBuild or "") + '' + set +e + diff -ur ${previousBuildArtifacts}/sources ./ > sourceDifference.patch + set -e + shopt -s extglob + rm -r !("sourceDifference.patch") + ls -al . + ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/outputs/* . + patch -p 1 -i sourceDifference.patch + ''; }); } From c8afee88bf346c80f79d036e34ca2d232b037792 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Mon, 20 Jun 2022 11:17:09 +0200 Subject: [PATCH 06/16] checkpointedBuild: fix tests for checkpointedBuild functions --- pkgs/test/incrementalBuild/default.nix | 10 ++--- .../hello-additionalFile.patch | 45 ++++++++++--------- .../incrementalBuild/hello-removeFile.patch | 43 ++++++++++-------- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/pkgs/test/incrementalBuild/default.nix b/pkgs/test/incrementalBuild/default.nix index cff1efee9a12..c11c9835ccbf 100644 --- a/pkgs/test/incrementalBuild/default.nix +++ b/pkgs/test/incrementalBuild/default.nix @@ -1,6 +1,6 @@ { hello, buildIncremental, runCommandNoCC, texinfo, stdenv, rsync }: let - baseHello = buildIncremental.prepareIncrementalBuild hello; + baseHelloArtifacts = buildIncremental.prepareIncrementalBuild hello; patchedHello = hello.overrideAttrs (old: { buildInputs = [ texinfo ]; src = runCommandNoCC "patch-hello-src" { } '' @@ -10,17 +10,17 @@ let patch -p1 < ${./hello.patch} ''; }); - incrementalBuiltHello = buildIncremental.mkIncrementalBuild patchedHello baseHello.incrementalBuildArtifacts; + incrementalBuiltHello = buildIncremental.mkIncrementalBuild patchedHello baseHelloArtifacts; incrementalBuiltHelloWithCheck = incrementalBuiltHello.overrideAttrs (old: { doCheck = true; checkPhase = '' echo "checking if unchanged source file is not recompiled" - [ "$(stat --format="%Y" lib/exitfail.o)" = "$(stat --format="%Y" ${baseHello.incrementalBuildArtifacts}/lib/exitfail.o)" ] + [ "$(stat --format="%Y" lib/exitfail.o)" = "$(stat --format="%Y" ${baseHelloArtifacts}/outputs/lib/exitfail.o)" ] ''; }); - baseHelloRemoveFile = buildIncremental.prepareIncrementalBuild (hello.overrideAttrs (old: { + baseHelloRemoveFileArtifacts = buildIncremental.prepareIncrementalBuild (hello.overrideAttrs (old: { patches = [ ./hello-additionalFile.patch ]; })); @@ -41,7 +41,7 @@ let ''; }); - incrementalBuiltHelloWithRemovedFile = buildIncremental.mkIncrementalBuild patchedHelloRemoveFile baseHelloRemoveFile.incrementalBuildArtifacts; + incrementalBuiltHelloWithRemovedFile = buildIncremental.mkIncrementalBuild patchedHelloRemoveFile baseHelloRemoveFileArtifacts; in stdenv.mkDerivation { name = "patched-hello-returns-correct-output"; diff --git a/pkgs/test/incrementalBuild/hello-additionalFile.patch b/pkgs/test/incrementalBuild/hello-additionalFile.patch index 31fbe034909e..345bc10ee49e 100644 --- a/pkgs/test/incrementalBuild/hello-additionalFile.patch +++ b/pkgs/test/incrementalBuild/hello-additionalFile.patch @@ -1,25 +1,30 @@ +:100644 100644 0000000 0000000 M Makefile.in +:000000 100644 0000000 0000000 A src/additionalFile.c +:100644 100644 0000000 0000000 M src/hello.c +:100644 100644 0000000 0000000 M src/system.h + diff --git a/Makefile.in b/Makefile.in -index 0805eda..77b000c 100644 +index 1597d39..f63f830 100644 --- a/Makefile.in +++ b/Makefile.in -@@ -227,7 +227,7 @@ am_lib_libhello_a_OBJECTS = lib/c-ctype.$(OBJEXT) \ - lib/quotearg.$(OBJEXT) lib/strnlen1.$(OBJEXT) \ - lib/unistd.$(OBJEXT) lib/wctype-h.$(OBJEXT) \ - lib/xmalloc.$(OBJEXT) lib/xalloc-die.$(OBJEXT) \ +@@ -312,7 +312,7 @@ am_lib_libhello_a_OBJECTS = lib/basename-lgpl.$(OBJEXT) \ + lib/version-etc.$(OBJEXT) lib/version-etc-fsf.$(OBJEXT) \ + lib/wctype-h.$(OBJEXT) lib/xmalloc.$(OBJEXT) \ + lib/xalloc-die.$(OBJEXT) lib/xstriconv.$(OBJEXT) \ - lib/xstrndup.$(OBJEXT) + lib/xstrndup.$(OBJEXT) src/additionalFile.$(OBJEXT) lib_libhello_a_OBJECTS = $(am_lib_libhello_a_OBJECTS) - am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(infodir)" \ - "$(DESTDIR)$(man1dir)" -@@ -1380,7 +1380,7 @@ lib_libhello_a_SOURCES = lib/c-ctype.h lib/c-ctype.c lib/c-strcase.h \ - lib/gettext.h lib/localcharset.h lib/localcharset.c \ - lib/progname.h lib/progname.c lib/quotearg.c lib/strnlen1.h \ - lib/strnlen1.c lib/unistd.c lib/wctype-h.c lib/xmalloc.c \ -- lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c -+ lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c src/additionalFile.c + am_hello_OBJECTS = src/hello.$(OBJEXT) + hello_OBJECTS = $(am_hello_OBJECTS) +@@ -1842,7 +1842,7 @@ lib_libhello_a_SOURCES = lib/basename-lgpl.c lib/c-ctype.h \ + $(am__append_4) $(am__append_5) lib/version-etc.h \ + lib/version-etc.c lib/version-etc-fsf.c lib/wctype-h.c \ + lib/xmalloc.c lib/xalloc-die.c lib/xstriconv.h lib/xstriconv.c \ +- lib/xstrndup.h lib/xstrndup.c ++ lib/xstrndup.h lib/xstrndup.c src/additionalFile.c lib_libhello_a_LIBADD = $(gl_LIBOBJS) lib_libhello_a_DEPENDENCIES = $(gl_LIBOBJS) - EXTRA_lib_libhello_a_SOURCES = lib/stripslash.c lib/error.c \ + EXTRA_lib_libhello_a_SOURCES = lib/close.c lib/stripslash.c lib/dup2.c \ diff --git a/src/additionalFile.c b/src/additionalFile.c new file mode 100644 index 0000000..34d683d @@ -33,10 +38,10 @@ index 0000000..34d683d + return 0; +} diff --git a/src/hello.c b/src/hello.c -index 453962f..df67de2 100644 +index 2e7d38e..a8e36dc 100644 --- a/src/hello.c +++ b/src/hello.c -@@ -57,7 +57,11 @@ main (int argc, char *argv[]) +@@ -146,7 +146,11 @@ main (int argc, char *argv[]) #endif /* Having initialized gettext, get the default message. */ @@ -50,12 +55,12 @@ index 453962f..df67de2 100644 /* Even exiting has subtleties. On exit, if any writes failed, change the exit status. The /dev/full device on GNU/Linux can be used for diff --git a/src/system.h b/src/system.h -index d1acac2..935b955 100644 +index d39cdb9..dc425d2 100644 --- a/src/system.h +++ b/src/system.h -@@ -42,4 +42,6 @@ - /* Check for errors on write. */ - # include "closeout.h" +@@ -59,4 +59,6 @@ + } \ + while (0) +int somefunc(); + diff --git a/pkgs/test/incrementalBuild/hello-removeFile.patch b/pkgs/test/incrementalBuild/hello-removeFile.patch index 99ecf1e298bb..2939790dabce 100644 --- a/pkgs/test/incrementalBuild/hello-removeFile.patch +++ b/pkgs/test/incrementalBuild/hello-removeFile.patch @@ -1,25 +1,30 @@ +:100644 100644 0000000 0000000 M Makefile.in +:100644 000000 0000000 0000000 D src/additionalFile.c +:100644 100644 0000000 0000000 M src/hello.c +:100755 100755 0000000 0000000 M tests/hello-1 + diff --git a/Makefile.in b/Makefile.in -index 77b000c..0805eda 100644 +index f63f830..1597d39 100644 --- a/Makefile.in +++ b/Makefile.in -@@ -227,7 +227,7 @@ am_lib_libhello_a_OBJECTS = lib/c-ctype.$(OBJEXT) \ - lib/quotearg.$(OBJEXT) lib/strnlen1.$(OBJEXT) \ - lib/unistd.$(OBJEXT) lib/wctype-h.$(OBJEXT) \ - lib/xmalloc.$(OBJEXT) lib/xalloc-die.$(OBJEXT) \ +@@ -312,7 +312,7 @@ am_lib_libhello_a_OBJECTS = lib/basename-lgpl.$(OBJEXT) \ + lib/version-etc.$(OBJEXT) lib/version-etc-fsf.$(OBJEXT) \ + lib/wctype-h.$(OBJEXT) lib/xmalloc.$(OBJEXT) \ + lib/xalloc-die.$(OBJEXT) lib/xstriconv.$(OBJEXT) \ - lib/xstrndup.$(OBJEXT) src/additionalFile.$(OBJEXT) + lib/xstrndup.$(OBJEXT) lib_libhello_a_OBJECTS = $(am_lib_libhello_a_OBJECTS) - am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(infodir)" \ - "$(DESTDIR)$(man1dir)" -@@ -1380,7 +1380,7 @@ lib_libhello_a_SOURCES = lib/c-ctype.h lib/c-ctype.c lib/c-strcase.h \ - lib/gettext.h lib/localcharset.h lib/localcharset.c \ - lib/progname.h lib/progname.c lib/quotearg.c lib/strnlen1.h \ - lib/strnlen1.c lib/unistd.c lib/wctype-h.c lib/xmalloc.c \ -- lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c src/additionalFile.c -+ lib/xalloc-die.c lib/xstrndup.h lib/xstrndup.c + am_hello_OBJECTS = src/hello.$(OBJEXT) + hello_OBJECTS = $(am_hello_OBJECTS) +@@ -1842,7 +1842,7 @@ lib_libhello_a_SOURCES = lib/basename-lgpl.c lib/c-ctype.h \ + $(am__append_4) $(am__append_5) lib/version-etc.h \ + lib/version-etc.c lib/version-etc-fsf.c lib/wctype-h.c \ + lib/xmalloc.c lib/xalloc-die.c lib/xstriconv.h lib/xstriconv.c \ +- lib/xstrndup.h lib/xstrndup.c src/additionalFile.c ++ lib/xstrndup.h lib/xstrndup.c lib_libhello_a_LIBADD = $(gl_LIBOBJS) lib_libhello_a_DEPENDENCIES = $(gl_LIBOBJS) - EXTRA_lib_libhello_a_SOURCES = lib/stripslash.c lib/error.c \ + EXTRA_lib_libhello_a_SOURCES = lib/close.c lib/stripslash.c lib/dup2.c \ diff --git a/src/additionalFile.c b/src/additionalFile.c deleted file mode 100644 index 34d683d..0000000 @@ -33,12 +38,12 @@ index 34d683d..0000000 - return 0; -} diff --git a/src/hello.c b/src/hello.c -index df67de2..768285a 100644 +index a8e36dc..53722d9 100644 --- a/src/hello.c +++ b/src/hello.c -@@ -36,6 +36,10 @@ static const struct option longopts[] = { - static void print_help (void); - static void print_version (void); +@@ -126,6 +126,10 @@ parse_options (int argc, char *argv[], const char **greeting_msg) + } + } +int somefunc() { + return 1; @@ -48,7 +53,7 @@ index df67de2..768285a 100644 main (int argc, char *argv[]) { diff --git a/tests/hello-1 b/tests/hello-1 -index e15fa95..3b7a815 100755 +index 96ffef8..f0b9f8d 100755 --- a/tests/hello-1 +++ b/tests/hello-1 @@ -21,7 +21,7 @@ export LANGUAGE LC_ALL LC_MESSAGES LANG From fc2e3fac619288868c4e0b019a8c561ea6885777 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Mon, 27 Jun 2022 14:03:10 +0200 Subject: [PATCH 07/16] doc: add section about checkpointed build --- doc/build-helpers/special.md | 1 + .../special/incremental-build.section.md | 28 +++++++++++++++++++ pkgs/test/incrementalBuild/default.nix | 10 +++---- pkgs/top-level/all-packages.nix | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 doc/build-helpers/special/incremental-build.section.md diff --git a/doc/build-helpers/special.md b/doc/build-helpers/special.md index f88648207fdc..baf40a261e81 100644 --- a/doc/build-helpers/special.md +++ b/doc/build-helpers/special.md @@ -7,4 +7,5 @@ special/fhs-environments.section.md special/makesetuphook.section.md special/mkshell.section.md special/vm-tools.section.md +special/incremental-build.section.md ``` diff --git a/doc/build-helpers/special/incremental-build.section.md b/doc/build-helpers/special/incremental-build.section.md new file mode 100644 index 000000000000..f8e81ddeffc5 --- /dev/null +++ b/doc/build-helpers/special/incremental-build.section.md @@ -0,0 +1,28 @@ +# pkgs.buildIncremental.* {#sec-incremental-build} + +`pkgs.buildIncremental` provides a way to build derivations incrementally. It consists of two functions to make incremental builds using nix possible. + +For hermeticity, Nix derivations do not allow any state to carry over between builds, making a transparent incremental build within a derivation impossible. + +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. + +To build a derivation incrementally, the following steps needs to be fullfilled: + * - run prepareIncrementalBuild on the desired derivation + * e.G `incrementalBuildArtifacts = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox);` + * - change something you want in the sources of the package( e.G using source override) + * changedVBox = pkgs.virtuabox.overrideAttrs (old: { + * src = path/to/vbox/sources; + * } + * - use `mkIncrementalBuild changedVBox buildOutput` + * enjoy shorter build times + +As Nix has no builtin support for the detection of the previous built derivation, a base version needs to be declared. +To create the outputs later used as base version for incremental builds, the function `pkgs.buildIncremental.prepareIncrementalBuild` is used. +The function takes the original derivation as an argument and transforms the output to a base version for an incremental build. +While doing so, the original output is not created and the installation phase is overwritten to produce the incremental build artifacts. + +When the built artifacts of the base version of the derivation are created, the code can be modified and changes are built using the `pkgs.buildIncremental.mkIncrementalBuild` function. +The `pkgs.buildIncremental.mkIncrementalBuild` function detects the changes in the code and places the output of the base version derivation within the build folder. +Then, the build tool is able to detect the changes and makes the decision of which parts of the derivation needs to be recompiled and produces the output, as expected in the derivation, without incremental build support. + + diff --git a/pkgs/test/incrementalBuild/default.nix b/pkgs/test/incrementalBuild/default.nix index c11c9835ccbf..b962befbfbac 100644 --- a/pkgs/test/incrementalBuild/default.nix +++ b/pkgs/test/incrementalBuild/default.nix @@ -1,6 +1,6 @@ -{ hello, buildIncremental, runCommandNoCC, texinfo, stdenv, rsync }: +{ hello, incrementalBuildTools, runCommandNoCC, texinfo, stdenv, rsync }: let - baseHelloArtifacts = buildIncremental.prepareIncrementalBuild hello; + baseHelloArtifacts = incrementalBuildTools.prepareIncrementalBuild hello; patchedHello = hello.overrideAttrs (old: { buildInputs = [ texinfo ]; src = runCommandNoCC "patch-hello-src" { } '' @@ -10,7 +10,7 @@ let patch -p1 < ${./hello.patch} ''; }); - incrementalBuiltHello = buildIncremental.mkIncrementalBuild patchedHello baseHelloArtifacts; + incrementalBuiltHello = incrementalBuildTools.mkIncrementalBuild patchedHello baseHelloArtifacts; incrementalBuiltHelloWithCheck = incrementalBuiltHello.overrideAttrs (old: { doCheck = true; @@ -20,7 +20,7 @@ let ''; }); - baseHelloRemoveFileArtifacts = buildIncremental.prepareIncrementalBuild (hello.overrideAttrs (old: { + baseHelloRemoveFileArtifacts = incrementalBuildTools.prepareIncrementalBuild (hello.overrideAttrs (old: { patches = [ ./hello-additionalFile.patch ]; })); @@ -41,7 +41,7 @@ let ''; }); - incrementalBuiltHelloWithRemovedFile = buildIncremental.mkIncrementalBuild patchedHelloRemoveFile baseHelloRemoveFileArtifacts; + incrementalBuiltHelloWithRemovedFile = incrementalBuildTools.mkIncrementalBuild patchedHelloRemoveFile baseHelloRemoveFileArtifacts; in stdenv.mkDerivation { name = "patched-hello-returns-correct-output"; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 9ff07d888b7f..4ad119789512 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -429,7 +429,7 @@ with pkgs; camunda-modeler = callPackage ../applications/misc/camunda-modeler { }; - inncrementalBuildTools = callPackage ../build-support/build-incremental.nix {}; + incrementalBuildTools = callPackage ../build-support/build-incremental.nix {}; caroline = callPackage ../development/libraries/caroline { }; From c85d18ff91710698f696cf964132967f9fc4bb5b Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Tue, 20 Dec 2022 10:29:27 +0100 Subject: [PATCH 08/16] checkpointedBuilds: allow dotglob and remove debugging leftover --- pkgs/build-support/build-incremental.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/build-incremental.nix index 9c779befb20e..d3ddb3a68384 100644 --- a/pkgs/build-support/build-incremental.nix +++ b/pkgs/build-support/build-incremental.nix @@ -44,9 +44,8 @@ rec { set +e diff -ur ${previousBuildArtifacts}/sources ./ > sourceDifference.patch set -e - shopt -s extglob + shopt -s extglob dotglob rm -r !("sourceDifference.patch") - ls -al . ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/outputs/* . patch -p 1 -i sourceDifference.patch ''; From ddfddf4b719134ac0acd04595372ca1bde4d0879 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Tue, 20 Dec 2022 10:43:15 +0100 Subject: [PATCH 09/16] checkpointedBuilds: add comments in the code --- pkgs/build-support/build-incremental.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/build-incremental.nix index d3ddb3a68384..e7bbf3871069 100644 --- a/pkgs/build-support/build-incremental.nix +++ b/pkgs/build-support/build-incremental.nix @@ -20,11 +20,21 @@ rec { prepareIncrementalBuild = drv: drv.overrideAttrs (old: { outputs = [ "out" ]; name = drv.name + "-incrementalBuildArtifacts"; + # To determine differences between the state of the build directory + # from an earlier build and a later one we store the state of the build + # directory before build, but after patch phases. + # This way, the same derivation can be used multiple times and only changes are detected. + # Additionally Removed files are handled correctly in later builds. preBuild = (old.preBuild or "") + '' mkdir -p $out/sources cp -r ./* $out/sources/ ''; + # After the build the build directory is copied again + # to get the output files. + # We copy the complete build folder, to take care for + # Build tools, building in the source directory, instead of + # having a build root directory, e.G the Linux kernel. installPhase = '' mkdir -p $out/outputs cp -r ./* $out/outputs/ @@ -40,6 +50,10 @@ rec { * in mkIncrementalBuild drv incrementalBuildArtifacts */ mkIncrementalBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: { + # The actual incremental build phase. + # We compare the changed sources from a previous build with the current and create a patch + # Afterwards we clean the build directory to copy the previous output files (Including the sources) + # The source difference patch is applied to get the latest changes again to allow short build times. preBuild = (old.preBuild or "") + '' set +e diff -ur ${previousBuildArtifacts}/sources ./ > sourceDifference.patch From 6db96122047fe7bdc1d58e201c9d161c0c479aba Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Tue, 20 Dec 2022 13:53:03 +0100 Subject: [PATCH 10/16] rename: incremental -> checkpointed builds --- doc/build-helpers/special.md | 2 +- ...section.md => checkpoint-build.section.md} | 24 ++++++++-------- ...d-incremental.nix => checkpoint-build.nix} | 28 +++++++++---------- .../default.nix | 16 +++++------ .../hello-additionalFile.patch | 0 .../hello-removeFile.patch | 0 .../hello.patch | 0 pkgs/test/default.nix | 2 +- pkgs/top-level/all-packages.nix | 2 +- 9 files changed, 37 insertions(+), 37 deletions(-) rename doc/build-helpers/special/{incremental-build.section.md => checkpoint-build.section.md} (52%) rename pkgs/build-support/{build-incremental.nix => checkpoint-build.nix} (69%) rename pkgs/test/{incrementalBuild => checkpointBuild}/default.nix (63%) rename pkgs/test/{incrementalBuild => checkpointBuild}/hello-additionalFile.patch (100%) rename pkgs/test/{incrementalBuild => checkpointBuild}/hello-removeFile.patch (100%) rename pkgs/test/{incrementalBuild => checkpointBuild}/hello.patch (100%) diff --git a/doc/build-helpers/special.md b/doc/build-helpers/special.md index baf40a261e81..265c2da92bf1 100644 --- a/doc/build-helpers/special.md +++ b/doc/build-helpers/special.md @@ -7,5 +7,5 @@ special/fhs-environments.section.md special/makesetuphook.section.md special/mkshell.section.md special/vm-tools.section.md -special/incremental-build.section.md +special/checkpoint-build.section.md ``` diff --git a/doc/build-helpers/special/incremental-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md similarity index 52% rename from doc/build-helpers/special/incremental-build.section.md rename to doc/build-helpers/special/checkpoint-build.section.md index f8e81ddeffc5..ae26d9db1a2e 100644 --- a/doc/build-helpers/special/incremental-build.section.md +++ b/doc/build-helpers/special/checkpoint-build.section.md @@ -1,28 +1,28 @@ -# pkgs.buildIncremental.* {#sec-incremental-build} +# pkgs.checkpointBuildTools.* {#sec-checkpoint-build} -`pkgs.buildIncremental` provides a way to build derivations incrementally. It consists of two functions to make incremental builds using nix possible. +`pkgs.checkpointBuildTools` provides a way to build derivations incrementally. It consists of two functions to make checkpoint builds using nix possible. For hermeticity, Nix derivations do not allow any state to carry over between builds, making a transparent incremental build within a derivation impossible. 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. -To build a derivation incrementally, the following steps needs to be fullfilled: - * - run prepareIncrementalBuild on the desired derivation - * e.G `incrementalBuildArtifacts = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox);` +To build a derivation based on build checkpoints, the following steps needs to be fullfilled: + * - run prepareCheckpointBuild on the desired derivation + * e.G `checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);` * - change something you want in the sources of the package( e.G using source override) * changedVBox = pkgs.virtuabox.overrideAttrs (old: { * src = path/to/vbox/sources; * } - * - use `mkIncrementalBuild changedVBox buildOutput` + * - use `mkCheckpointedBuild changedVBox buildOutput` * enjoy shorter build times As Nix has no builtin support for the detection of the previous built derivation, a base version needs to be declared. -To create the outputs later used as base version for incremental builds, the function `pkgs.buildIncremental.prepareIncrementalBuild` is used. -The function takes the original derivation as an argument and transforms the output to a base version for an incremental build. -While doing so, the original output is not created and the installation phase is overwritten to produce the incremental build artifacts. +To create the outputs later used as base version for checkpoint builds, the function `pkgs.checkpointBuildTools.prepareCheckpointBuild` is used. +The function takes the original derivation as an argument and transforms the output to a base version for an checkpoint build build. +While doing so, the original output is not created and the installation phase is overwritten to produce the checkpoint artifacts. -When the built artifacts of the base version of the derivation are created, the code can be modified and changes are built using the `pkgs.buildIncremental.mkIncrementalBuild` function. -The `pkgs.buildIncremental.mkIncrementalBuild` function detects the changes in the code and places the output of the base version derivation within the build folder. -Then, the build tool is able to detect the changes and makes the decision of which parts of the derivation needs to be recompiled and produces the output, as expected in the derivation, without incremental build support. +When the built artifacts of the base version of the derivation are created, the code can be modified and changes are built using the `pkgs.checkpointBuildTools.mkCheckpointedBuild` function. +The `pkgs.checkpointBuildTools.mkCheckpointedBuild` function detects the changes in the code and places the output of the base version derivation within the build folder. +Then, the build tool is able to detect the changes and makes the decision of which parts of the derivation needs to be recompiled and produces the output, as expected in the derivation, without checkpoint build support. diff --git a/pkgs/build-support/build-incremental.nix b/pkgs/build-support/checkpoint-build.nix similarity index 69% rename from pkgs/build-support/build-incremental.nix rename to pkgs/build-support/checkpoint-build.nix index e7bbf3871069..be25ca4d04b1 100644 --- a/pkgs/build-support/build-incremental.nix +++ b/pkgs/build-support/checkpoint-build.nix @@ -2,24 +2,24 @@ rec { /* Prepare a derivation for local builds. * - * This function prepares incremental builds by provinding, + * This function prepares checkpoint builds by provinding, * containing the build output and the sources for cross checking. - * The build output can be used later to allow incremental builds - * by passing the derivation output to the `mkIncrementalBuild` function. + * The build output can be used later to allow checkpoint builds + * by passing the derivation output to the `mkCheckpointBuild` function. * - * To build a project incrementaly follow these steps: + * To build a project with checkpoints follow these steps: * - run prepareIncrementalBuild on the desired derivation - * e.G `incrementalBuildArtifacts = (pkgs.buildIncremental.prepareIncrementalBuild pkgs.virtualbox);` + * e.G `incrementalBuildArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);` * - change something you want in the sources of the package( e.G using source override) * changedVBox = pkgs.virtuabox.overrideAttrs (old: { * src = path/to/vbox/sources; * } - * - use `mkIncrementalBuild changedVBox buildOutput` + * - use `mkCheckpointedBuild changedVBox buildOutput` * - enjoy shorter build times */ - prepareIncrementalBuild = drv: drv.overrideAttrs (old: { + prepareCheckpointBuild = drv: drv.overrideAttrs (old: { outputs = [ "out" ]; - name = drv.name + "-incrementalBuildArtifacts"; + name = drv.name + "-checkpointArtifacts"; # To determine differences between the state of the build directory # from an earlier build and a later one we store the state of the build # directory before build, but after patch phases. @@ -41,16 +41,16 @@ rec { ''; }); - /* Build a derivation incrementally based on the output generated by - * the `prepareIncrementalBuild function. + /* Build a derivation based on the checkpoint output generated by + * the `prepareCheckpointBuild function. * * Usage: * let - * incrementalBuildArtifacts = prepareIncrementalBuild drv - * in mkIncrementalBuild drv incrementalBuildArtifacts + * checkpointArtifacts = prepareCheckpointBuild drv + * in mkCheckpointedBuild drv checkpointArtifacts */ - mkIncrementalBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: { - # The actual incremental build phase. + mkCheckpointedBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: { + # The actual checkpoint build phase. # We compare the changed sources from a previous build with the current and create a patch # Afterwards we clean the build directory to copy the previous output files (Including the sources) # The source difference patch is applied to get the latest changes again to allow short build times. diff --git a/pkgs/test/incrementalBuild/default.nix b/pkgs/test/checkpointBuild/default.nix similarity index 63% rename from pkgs/test/incrementalBuild/default.nix rename to pkgs/test/checkpointBuild/default.nix index b962befbfbac..4a59760230a6 100644 --- a/pkgs/test/incrementalBuild/default.nix +++ b/pkgs/test/checkpointBuild/default.nix @@ -1,6 +1,6 @@ -{ hello, incrementalBuildTools, runCommandNoCC, texinfo, stdenv, rsync }: +{ hello, checkpointBuildTools, runCommandNoCC, texinfo, stdenv, rsync }: let - baseHelloArtifacts = incrementalBuildTools.prepareIncrementalBuild hello; + baseHelloArtifacts = checkpointBuildTools.prepareCheckpointBuild hello; patchedHello = hello.overrideAttrs (old: { buildInputs = [ texinfo ]; src = runCommandNoCC "patch-hello-src" { } '' @@ -10,9 +10,9 @@ let patch -p1 < ${./hello.patch} ''; }); - incrementalBuiltHello = incrementalBuildTools.mkIncrementalBuild patchedHello baseHelloArtifacts; + checkpointBuiltHello = checkpointBuildTools.mkCheckpointedBuild patchedHello baseHelloArtifacts; - incrementalBuiltHelloWithCheck = incrementalBuiltHello.overrideAttrs (old: { + checkpointBuiltHelloWithCheck = checkpointBuiltHello.overrideAttrs (old: { doCheck = true; checkPhase = '' echo "checking if unchanged source file is not recompiled" @@ -20,7 +20,7 @@ let ''; }); - baseHelloRemoveFileArtifacts = incrementalBuildTools.prepareIncrementalBuild (hello.overrideAttrs (old: { + baseHelloRemoveFileArtifacts = checkpointBuildTools.prepareCheckpointBuild (hello.overrideAttrs (old: { patches = [ ./hello-additionalFile.patch ]; })); @@ -41,7 +41,7 @@ let ''; }); - incrementalBuiltHelloWithRemovedFile = incrementalBuildTools.mkIncrementalBuild patchedHelloRemoveFile baseHelloRemoveFileArtifacts; + checkpointBuiltHelloWithRemovedFile = checkpointBuildTools.mkCheckpointedBuild patchedHelloRemoveFile baseHelloRemoveFileArtifacts; in stdenv.mkDerivation { name = "patched-hello-returns-correct-output"; @@ -49,9 +49,9 @@ stdenv.mkDerivation { touch $out echo "testing output of hello binary" - [ "$(${incrementalBuiltHelloWithCheck}/bin/hello)" = "Hello, incremental world!" ] + [ "$(${checkpointBuiltHelloWithCheck}/bin/hello)" = "Hello, incremental world!" ] echo "testing output of hello with removed file" - [ "$(${incrementalBuiltHelloWithRemovedFile}/bin/hello)" = "Hello, incremental world!" ] + [ "$(${checkpointBuiltHelloWithRemovedFile}/bin/hello)" = "Hello, incremental world!" ] ''; } diff --git a/pkgs/test/incrementalBuild/hello-additionalFile.patch b/pkgs/test/checkpointBuild/hello-additionalFile.patch similarity index 100% rename from pkgs/test/incrementalBuild/hello-additionalFile.patch rename to pkgs/test/checkpointBuild/hello-additionalFile.patch diff --git a/pkgs/test/incrementalBuild/hello-removeFile.patch b/pkgs/test/checkpointBuild/hello-removeFile.patch similarity index 100% rename from pkgs/test/incrementalBuild/hello-removeFile.patch rename to pkgs/test/checkpointBuild/hello-removeFile.patch diff --git a/pkgs/test/incrementalBuild/hello.patch b/pkgs/test/checkpointBuild/hello.patch similarity index 100% rename from pkgs/test/incrementalBuild/hello.patch rename to pkgs/test/checkpointBuild/hello.patch diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index 8ca6c4faf56e..c23814747b41 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -113,7 +113,7 @@ with pkgs; install-shell-files = callPackage ./install-shell-files {}; - incremental-build = callPackage ./incrementalBuild {}; + checkpoint-build = callPackage ./checkpointBuild {}; kernel-config = callPackage ./kernel.nix {}; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 4ad119789512..d2b9581e4b15 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -429,7 +429,7 @@ with pkgs; camunda-modeler = callPackage ../applications/misc/camunda-modeler { }; - incrementalBuildTools = callPackage ../build-support/build-incremental.nix {}; + checkpointBuildTools = callPackage ../build-support/checkpoint-build.nix {}; caroline = callPackage ../development/libraries/caroline { }; From 0ab2262bdd601a2997d7f6067eadc767391f7298 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Thu, 7 Dec 2023 17:15:17 +0100 Subject: [PATCH 11/16] checkpointBuild: fix whitespaces in documentation --- .../special/checkpoint-build.section.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/build-helpers/special/checkpoint-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md index ae26d9db1a2e..b8dc2e99708f 100644 --- a/doc/build-helpers/special/checkpoint-build.section.md +++ b/doc/build-helpers/special/checkpoint-build.section.md @@ -16,13 +16,11 @@ To build a derivation based on build checkpoints, the following steps needs to b * - use `mkCheckpointedBuild changedVBox buildOutput` * enjoy shorter build times -As Nix has no builtin support for the detection of the previous built derivation, a base version needs to be declared. -To create the outputs later used as base version for checkpoint builds, the function `pkgs.checkpointBuildTools.prepareCheckpointBuild` is used. -The function takes the original derivation as an argument and transforms the output to a base version for an checkpoint build build. -While doing so, the original output is not created and the installation phase is overwritten to produce the checkpoint artifacts. +As Nix has no builtin support for the detection of the previous built derivation, a base version needs to be declared. +To create the outputs later used as base version for checkpoint builds, the function `pkgs.checkpointBuildTools.prepareCheckpointBuild` is used. +The function takes the original derivation as an argument and transforms the output to a base version for an checkpoint build build. +While doing so, the original output is not created and the installation phase is overwritten to produce the checkpoint artifacts. -When the built artifacts of the base version of the derivation are created, the code can be modified and changes are built using the `pkgs.checkpointBuildTools.mkCheckpointedBuild` function. -The `pkgs.checkpointBuildTools.mkCheckpointedBuild` function detects the changes in the code and places the output of the base version derivation within the build folder. +When the built artifacts of the base version of the derivation are created, the code can be modified and changes are built using the `pkgs.checkpointBuildTools.mkCheckpointedBuild` function. +The `pkgs.checkpointBuildTools.mkCheckpointedBuild` function detects the changes in the code and places the output of the base version derivation within the build folder. Then, the build tool is able to detect the changes and makes the decision of which parts of the derivation needs to be recompiled and produces the output, as expected in the derivation, without checkpoint build support. - - From f61980dbf91d43e969157bc34186f912024f62cf Mon Sep 17 00:00:00 2001 From: messemar Date: Tue, 12 Dec 2023 10:43:54 +0100 Subject: [PATCH 12/16] doc: fix typo in checkpoint build section Co-authored-by: Markus Partheymueller --- doc/build-helpers/special/checkpoint-build.section.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build-helpers/special/checkpoint-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md index b8dc2e99708f..afdf78ebf3c9 100644 --- a/doc/build-helpers/special/checkpoint-build.section.md +++ b/doc/build-helpers/special/checkpoint-build.section.md @@ -10,7 +10,7 @@ To build a derivation based on build checkpoints, the following steps needs to b * - run prepareCheckpointBuild on the desired derivation * e.G `checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);` * - change something you want in the sources of the package( e.G using source override) - * changedVBox = pkgs.virtuabox.overrideAttrs (old: { + * changedVBox = pkgs.virtualbox.overrideAttrs (old: { * src = path/to/vbox/sources; * } * - use `mkCheckpointedBuild changedVBox buildOutput` From 0d6d654f3683e58495c04c10d13319df52a59034 Mon Sep 17 00:00:00 2001 From: messemar Date: Tue, 12 Dec 2023 13:19:21 +0100 Subject: [PATCH 13/16] doc: checkpointBuild: fix wording Co-authored-by: Robert Hensing --- doc/build-helpers/special/checkpoint-build.section.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/build-helpers/special/checkpoint-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md index afdf78ebf3c9..e5889a05be16 100644 --- a/doc/build-helpers/special/checkpoint-build.section.md +++ b/doc/build-helpers/special/checkpoint-build.section.md @@ -6,17 +6,17 @@ For hermeticity, Nix derivations do not allow any state to carry over between bu 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. -To build a derivation based on build checkpoints, the following steps needs to be fullfilled: - * - run prepareCheckpointBuild on the desired derivation - * e.G `checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);` - * - change something you want in the sources of the package( e.G using source override) +To change a normal derivation to a checkpoint based build, these steps must be taken: + * apply `prepareCheckpointBuild` on the desired derivation + e.g. `checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);` + - change something you want in the sources of the package. (e.g. using a source override) * changedVBox = pkgs.virtualbox.overrideAttrs (old: { * src = path/to/vbox/sources; * } * - use `mkCheckpointedBuild changedVBox buildOutput` * enjoy shorter build times -As Nix has no builtin support for the detection of the previous built derivation, a base version needs to be declared. +As Nix intentionally has no built-in support for the detection of the previously built derivation, a base version must be declared. To create the outputs later used as base version for checkpoint builds, the function `pkgs.checkpointBuildTools.prepareCheckpointBuild` is used. The function takes the original derivation as an argument and transforms the output to a base version for an checkpoint build build. While doing so, the original output is not created and the installation phase is overwritten to produce the checkpoint artifacts. From a3e0a52de43d929493b6639e83cb701f197c2643 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Tue, 12 Dec 2023 13:21:01 +0100 Subject: [PATCH 14/16] checkpointBuilds: add hooks for checkpoint builds --- pkgs/build-support/checkpoint-build.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/build-support/checkpoint-build.nix b/pkgs/build-support/checkpoint-build.nix index be25ca4d04b1..e08dde353e89 100644 --- a/pkgs/build-support/checkpoint-build.nix +++ b/pkgs/build-support/checkpoint-build.nix @@ -36,8 +36,10 @@ rec { # Build tools, building in the source directory, instead of # having a build root directory, e.G the Linux kernel. installPhase = '' + runHook preCheckpointInstall mkdir -p $out/outputs cp -r ./* $out/outputs/ + runHook postCheckpointInstall ''; }); From 15c2c6827bac0a023556177ea7ba5646efe43742 Mon Sep 17 00:00:00 2001 From: Martin Messer Date: Tue, 12 Dec 2023 14:12:15 +0100 Subject: [PATCH 15/16] checkpointBuild: doc remove textual description in favor of an integrated example --- .../special/checkpoint-build.section.md | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/doc/build-helpers/special/checkpoint-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md index e5889a05be16..7988656b0bf5 100644 --- a/doc/build-helpers/special/checkpoint-build.section.md +++ b/doc/build-helpers/special/checkpoint-build.section.md @@ -1,4 +1,4 @@ -# pkgs.checkpointBuildTools.* {#sec-checkpoint-build} +# pkgs.checkpointBuildTools {#sec-checkpoint-build} `pkgs.checkpointBuildTools` provides a way to build derivations incrementally. It consists of two functions to make checkpoint builds using nix possible. @@ -7,20 +7,30 @@ For hermeticity, Nix derivations do not allow any state to carry over between bu 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. To change a normal derivation to a checkpoint based build, these steps must be taken: - * apply `prepareCheckpointBuild` on the desired derivation - e.g. `checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);` + - apply `prepareCheckpointBuild` on the desired derivation + e.g.: +```nix +checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox); +``` - change something you want in the sources of the package. (e.g. using a source override) - * changedVBox = pkgs.virtualbox.overrideAttrs (old: { - * src = path/to/vbox/sources; - * } - * - use `mkCheckpointedBuild changedVBox buildOutput` - * enjoy shorter build times +```nix +changedVBox = pkgs.virtualbox.overrideAttrs (old: { + src = path/to/vbox/sources; +} +``` + - use `mkCheckpointedBuild changedVBox buildOutput` + - enjoy shorter build times -As Nix intentionally has no built-in support for the detection of the previously built derivation, a base version must be declared. -To create the outputs later used as base version for checkpoint builds, the function `pkgs.checkpointBuildTools.prepareCheckpointBuild` is used. -The function takes the original derivation as an argument and transforms the output to a base version for an checkpoint build build. -While doing so, the original output is not created and the installation phase is overwritten to produce the checkpoint artifacts. - -When the built artifacts of the base version of the derivation are created, the code can be modified and changes are built using the `pkgs.checkpointBuildTools.mkCheckpointedBuild` function. -The `pkgs.checkpointBuildTools.mkCheckpointedBuild` function detects the changes in the code and places the output of the base version derivation within the build folder. -Then, the build tool is able to detect the changes and makes the decision of which parts of the derivation needs to be recompiled and produces the output, as expected in the derivation, without checkpoint build support. +## Example {#sec-checkpoint-build-example} +```nix +{ pkgs ? import {} }: with (pkgs) checkpointBuildTools; +let + helloCheckpoint = checkpointBuildTools.prepareCheckpointBuild pkgs.hello; + changedHello = pkgs.hello.overrideAttrs (_: { + doCheck = false; + patchPhase = '' + sed -i 's/Hello, world!/Hello, Nix!/g' src/hello.c + ''; + }); +in checkpointBuildTools.mkCheckpointBuild changedHello helloCheckpoint +``` From 5ebb78d9522ce232286ae0518a1020b8e099e7e1 Mon Sep 17 00:00:00 2001 From: messemar Date: Wed, 13 Dec 2023 11:15:29 +0100 Subject: [PATCH 16/16] doc: checkpointBuild: fix wording Co-authored-by: Philipp Schuster --- doc/build-helpers/special/checkpoint-build.section.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build-helpers/special/checkpoint-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md index 7988656b0bf5..5f01e699b947 100644 --- a/doc/build-helpers/special/checkpoint-build.section.md +++ b/doc/build-helpers/special/checkpoint-build.section.md @@ -1,6 +1,6 @@ # pkgs.checkpointBuildTools {#sec-checkpoint-build} -`pkgs.checkpointBuildTools` provides a way to build derivations incrementally. It consists of two functions to make checkpoint builds using nix possible. +`pkgs.checkpointBuildTools` provides a way to build derivations incrementally. It consists of two functions to make checkpoint builds using Nix possible. For hermeticity, Nix derivations do not allow any state to carry over between builds, making a transparent incremental build within a derivation impossible.