about summary refs log tree commit diff
diff options
context:
space:
mode:
-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