about summary refs log tree commit diff
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2017-02-07 23:40:13 +0100
committeraszlig <aszlig@redmoonstudios.org>2017-02-08 00:12:12 +0100
commite413688502b482694b9c4057055e15a83fc8265a (patch)
treeecabd6a39445f57772918c6683eb7842e65a127a
parent952c700e5071b8f450feba0ce572b2f800a35ee0 (diff)
modules/core: Add a new module for lazy packages
This allows to add packages to vuizvui.lazyPackages which then aren't
directly installed onto the system but instead built by the Hydra and
only fetched from it as soon as a binary of one of these packages is
executed.

Doing this only within a NixOS module however isn't enough, because by
default gc-keep-outputs is false, so a garbage collect on the Hydra
instance would remove the packages we wrap in vuizvui.lazyPackages.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
-rw-r--r--modules/core/lazy-packages.nix67
-rw-r--r--modules/module-list.nix1
-rw-r--r--release.nix15
3 files changed, 81 insertions, 2 deletions
diff --git a/modules/core/lazy-packages.nix b/modules/core/lazy-packages.nix
new file mode 100644
index 00000000..939ece52
--- /dev/null
+++ b/modules/core/lazy-packages.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (lib) escapeShellArg;
+  inherit (config.vuizvui) lazyPackages;
+
+  # Encode the store path in base 64 so that the wrapper
+  # doesn't have a direct dependency on the package.
+  encoder = "${escapeShellArg "${pkgs.coreutils}/bin/base64"} -w0";
+  decoder = "${escapeShellArg "${pkgs.coreutils}/bin/base64"} -d";
+
+  # The command used to fetch the store path from the binary cache.
+  fetchSubstitute = "${escapeShellArg "${pkgs.nix}/bin/nix-store"} -r";
+
+  mkWrapper = package: pkgs.runCommand "${package.name}-lazy" {
+    inherit package;
+  } ''
+    encoded="$(echo "$package" | ${encoder})"
+
+    if [ ! -e "$package/bin" ]; then
+      echo "Store path $package doesn't have a \`bin' directory" \
+           "so we can't create lazy wrappers for it. Please" \
+           "remove \`${escapeShellArg package.name}' from" \
+           "\`vuizvui.lazyPackages'." >&2
+      exit 1
+    fi
+
+    for bin in "$package"/bin/*; do
+      [ -x "$bin" -a ! -d "$bin" ] || continue
+      binpath="''${bin#$package/}"
+
+      mkdir -p "$out/bin"
+      ( echo ${escapeShellArg "#!${pkgs.stdenv.shell}"}
+        echo "encoded='$encoded'"
+        echo "binpath='$binpath'"
+        echo -n ${escapeShellArg ''
+          storepath="$(echo "$encoded" | ${decoder})"
+          program="$storepath/$binpath"
+          if [ ! -e "$storepath" ]; then
+            ${fetchSubstitute} "$storepath" || exit $?
+          fi
+          exec "$program" "$@"
+        ''}
+      ) > "$out/bin/$(basename "$bin")"
+      chmod +x "$out/bin/$(basename "$bin")"
+    done
+  '';
+
+  wrappers = map mkWrapper config.vuizvui.lazyPackages;
+
+in {
+  options.vuizvui.lazyPackages = lib.mkOption {
+    type = lib.types.listOf lib.types.package;
+    default = [];
+    example = lib.literalExample "[ pkgs.gimp pkgs.libreoffice ]";
+    description = ''
+      Packages which are built for this system but instead of being a full
+      runtime dependency, only wrappers of all executables that reside in the
+      <literal>bin</literal> directory are actually runtime dependencies.
+
+      As soon as one of these wrappers is executed, the real package is fetched
+      and the corresponding binary is executed.
+    '';
+  };
+
+  config.environment.systemPackages = map mkWrapper lazyPackages;
+}
diff --git a/modules/module-list.nix b/modules/module-list.nix
index b12d1fff..5994dd61 100644
--- a/modules/module-list.nix
+++ b/modules/module-list.nix
@@ -2,6 +2,7 @@
   ./core/common.nix
   ./core/licensing.nix
   ./core/tests.nix
+  ./core/lazy-packages.nix
   ./hardware/gamecontroller.nix
   ./hardware/rtl8192cu
   ./hardware/t100ha
diff --git a/release.nix b/release.nix
index 5591dcac..b71ad0c3 100644
--- a/release.nix
+++ b/release.nix
@@ -69,8 +69,19 @@ let
 in with pkgsUpstream.lib; with builtins; {
 
   machines = let
-    getBuild = const (getAttr "build");
-  in mapAttrsRecursiveCond (m: !(m ? eval)) getBuild allMachines;
+    # We need to expose all the real builds within vuizvui.lazyPackages to make
+    # sure they don't get garbage collected on the Hydra instance.
+    wrapLazy = machine: pkgsUpstream.runCommand machine.build.name {
+      fakeRuntimeDeps = machine.eval.config.vuizvui.lazyPackages;
+      product = machine.build;
+    } ''
+      mkdir -p "$out/nix-support"
+      echo "$product" > "$out/nix-support/fake-runtime-dependencies"
+      for i in $fakeRuntimeDeps; do
+        echo "$i" >> "$out/nix-support/fake-runtime-dependencies"
+      done
+    '';
+  in mapAttrsRecursiveCond (m: !(m ? eval)) (const wrapLazy) allMachines;
 
   isoImages = let
     buildIso = attrs: let