about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support/replace-dependency.nix
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2019-01-07 02:18:36 +0000
committerAlyssa Ross <hi@alyssa.is>2019-01-07 02:18:47 +0000
commit36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2 (patch)
treeb3faaf573407b32aa645237a4d16b82778a39a92 /nixpkgs/pkgs/build-support/replace-dependency.nix
parent4e31070265257dc67d120c27e0f75c2344fdfa9a (diff)
parentabf060725d7614bd3b9f96764262dfbc2f9c2199 (diff)
downloadnixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.gz
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.bz2
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.lz
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.xz
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.tar.zst
nixlib-36f56d99fa0a0765c9f1de4a5f17a9b05830c3f2.zip
Add 'nixpkgs/' from commit 'abf060725d7614bd3b9f96764262dfbc2f9c2199'
git-subtree-dir: nixpkgs
git-subtree-mainline: 4e31070265257dc67d120c27e0f75c2344fdfa9a
git-subtree-split: abf060725d7614bd3b9f96764262dfbc2f9c2199
Diffstat (limited to 'nixpkgs/pkgs/build-support/replace-dependency.nix')
-rw-r--r--nixpkgs/pkgs/build-support/replace-dependency.nix83
1 files changed, 83 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/build-support/replace-dependency.nix b/nixpkgs/pkgs/build-support/replace-dependency.nix
new file mode 100644
index 000000000000..15ab50bf3974
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/replace-dependency.nix
@@ -0,0 +1,83 @@
+{ runCommand, nix, lib }:
+
+# Replace a single dependency in the requisites tree of drv, propagating
+# the change all the way up the tree, without a full rebuild. This can be
+# useful, for example, to patch a security hole in libc and still use your
+# system safely without rebuilding the world. This should be a short term
+# solution, as soon as a rebuild can be done the properly rebuild derivation
+# should be used. The old dependency and new dependency MUST have the same-length
+# name, and ideally should have close-to-identical directory layout.
+#
+# Example: safeFirefox = replaceDependency {
+#   drv = firefox;
+#   oldDependency = glibc;
+#   newDependency = overrideDerivation glibc (attrs: {
+#     patches  = attrs.patches ++ [ ./fix-glibc-hole.patch ];
+#   });
+# };
+# This will rebuild glibc with your security patch, then copy over firefox
+# (and all of its dependencies) without rebuilding further.
+{ drv, oldDependency, newDependency, verbose ? true }:
+
+with lib;
+
+let
+  warn = if verbose then builtins.trace else (x: y: y);
+  references = import (runCommand "references.nix" { exportReferencesGraph = [ "graph" drv ]; } ''
+    (echo {
+    while read path
+    do
+        echo "  \"$path\" = ["
+        read count
+        read count
+        while [ "0" != "$count" ]
+        do
+            read ref_path
+            if [ "$ref_path" != "$path" ]
+            then
+                echo "    (builtins.storePath $ref_path)"
+            fi
+            count=$(($count - 1))
+        done
+        echo "  ];"
+    done < graph
+    echo }) > $out
+  '').outPath;
+
+  discard = builtins.unsafeDiscardStringContext;
+
+  oldStorepath = builtins.storePath (discard (toString oldDependency));
+
+  referencesOf = drv: references.${discard (toString drv)};
+
+  dependsOnOldMemo = listToAttrs (map
+    (drv: { name = discard (toString drv);
+            value = elem oldStorepath (referencesOf drv) ||
+                    any dependsOnOld (referencesOf drv);
+          }) (builtins.attrNames references));
+
+  dependsOnOld = drv: dependsOnOldMemo.${discard (toString drv)};
+
+  drvName = drv:
+    discard (substring 33 (stringLength (builtins.baseNameOf drv)) (builtins.baseNameOf drv));
+
+  rewriteHashes = drv: hashes: runCommand (drvName drv) { nixStore = "${nix.out}/bin/nix-store"; } ''
+    $nixStore --dump ${drv} | sed 's|${baseNameOf drv}|'$(basename $out)'|g' | sed -e ${
+      concatStringsSep " -e " (mapAttrsToList (name: value:
+        "'s|${baseNameOf name}|${baseNameOf value}|g'"
+      ) hashes)
+    } | $nixStore --restore $out
+  '';
+
+  rewrittenDeps = listToAttrs [ {name = discard (toString oldDependency); value = newDependency;} ];
+
+  rewriteMemo = listToAttrs (map
+    (drv: { name = discard (toString drv);
+            value = rewriteHashes (builtins.storePath drv)
+              (filterAttrs (n: v: builtins.elem (builtins.storePath (discard (toString n))) (referencesOf drv)) rewriteMemo);
+          })
+    (filter dependsOnOld (builtins.attrNames references))) // rewrittenDeps;
+
+  drvHash = discard (toString drv);
+in assert (stringLength (drvName (toString oldDependency)) == stringLength (drvName (toString newDependency)));
+rewriteMemo.${drvHash} or (warn "replace-dependency.nix: Derivation ${drvHash} does not depend on ${discard (toString oldDependency)}" drv)