diff options
author | Shea Levy <shea@shealevy.com> | 2013-01-11 14:42:09 -0500 |
---|---|---|
committer | Shea Levy <shea@shealevy.com> | 2013-01-11 14:42:09 -0500 |
commit | d1662d715514e6ef9d3dc29f132f1b3d8e608a18 (patch) | |
tree | 766b0b4d303fda96f7c9ba0d6381fb0048f5c4e1 /pkgs/build-support/replace-dependency.nix | |
parent | 2dd59fc4cf74d62d47de613512213c63cf6400b6 (diff) | |
download | nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.tar nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.tar.gz nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.tar.bz2 nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.tar.lz nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.tar.xz nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.tar.zst nixpkgs-d1662d715514e6ef9d3dc29f132f1b3d8e608a18.zip |
Add the replace-dependency build support function.
The use case is to do a deep replacement of a dependency without rebuilding the entire tree. For example, suppose a security hole is found in glibc and a patch released. Ideally, you'd just rebuild everything, but that takes time, space, and CPU that you might not have, so in the mean time you could build a safe version of, say, firefox with: firefox-safe = replace-dependency { drv = firefox; old-dependency = glibc; new-dependency = patched-glibc; }; Building firefox-safe will rebuild glibc, but only do a simple copy/string replacement on all other dependencies of firefox. On my system (MBP 13" mid-2012), after a new glibc had been build building firefox took around 11 seconds. See the comments in the file for more details.
Diffstat (limited to 'pkgs/build-support/replace-dependency.nix')
-rw-r--r-- | pkgs/build-support/replace-dependency.nix | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/pkgs/build-support/replace-dependency.nix b/pkgs/build-support/replace-dependency.nix new file mode 100644 index 00000000000..364d5548151 --- /dev/null +++ b/pkgs/build-support/replace-dependency.nix @@ -0,0 +1,80 @@ +{ 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: safe-firefox = replace-dependency { +# drv = firefox; +# old-dependency = glibc; +# new-dependency = 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, old-dependency, new-dependency }: + +with lib; + +let + 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; + + old-storepath = builtins.storePath (discard old-dependency.outPath); + + references-of = drv: getAttr (discard (toString drv)) references; + + depends-on-old = drv: elem old-storepath (references-of drv) || + any depends-on-old (references-of drv); + + drv-name = drv: + discard (substring 33 (stringLength (builtins.baseNameOf drv)) (builtins.baseNameOf drv)); + + replace-strings = drv: rewritten-drvs: runCommand (drv-name drv) { nixStore = "${nix}/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'" + ) rewritten-drvs) + } | $nixStore --restore $out + ''; + + fn = { drv, rewritten-drvs }: rewritten-drvs // ( + if depends-on-old drv + then listToAttrs [ { + name = discard (toString drv); + + value = replace-strings drv (rewritten-drvs // (fold (drv: acc: + (fn { inherit drv rewritten-drvs; }) // acc + ) {} (references-of drv))); + } ] + else {} + ); +in assert (stringLength old-dependency.name == stringLength new-dependency.name); getAttr (discard drv.outPath) (fn { + inherit drv; + rewritten-drvs = listToAttrs [ {name = discard old-dependency.outPath; value = new-dependency;} ]; +}) |