at 15.09-beta 81 lines 3.1 kB view raw
1{ runCommand, nix, lib }: 2 3# Replace a single dependency in the requisites tree of drv, propagating 4# the change all the way up the tree, without a full rebuild. This can be 5# useful, for example, to patch a security hole in libc and still use your 6# system safely without rebuilding the world. This should be a short term 7# solution, as soon as a rebuild can be done the properly rebuild derivation 8# should be used. The old dependency and new dependency MUST have the same-length 9# name, and ideally should have close-to-identical directory layout. 10# 11# Example: safeFirefox = replaceDependency { 12# drv = firefox; 13# oldDependency = glibc; 14# newDependency = overrideDerivation glibc (attrs: { 15# patches = attrs.patches ++ [ ./fix-glibc-hole.patch ]; 16# }); 17# }; 18# This will rebuild glibc with your security patch, then copy over firefox 19# (and all of its dependencies) without rebuilding further. 20{ drv, oldDependency, newDependency }: 21 22with lib; 23 24let 25 references = import (runCommand "references.nix" { exportReferencesGraph = [ "graph" drv ]; } '' 26 (echo { 27 while read path 28 do 29 echo " \"$path\" = [" 30 read count 31 read count 32 while [ "0" != "$count" ] 33 do 34 read ref_path 35 if [ "$ref_path" != "$path" ] 36 then 37 echo " (builtins.storePath $ref_path)" 38 fi 39 count=$(($count - 1)) 40 done 41 echo " ];" 42 done < graph 43 echo }) > $out 44 '').outPath; 45 46 discard = builtins.unsafeDiscardStringContext; 47 48 oldStorepath = builtins.storePath (discard (toString oldDependency)); 49 50 referencesOf = drv: getAttr (discard (toString drv)) references; 51 52 dependsOnOldMemo = listToAttrs (map 53 (drv: { name = discard (toString drv); 54 value = elem oldStorepath (referencesOf drv) || 55 any dependsOnOld (referencesOf drv); 56 }) (builtins.attrNames references)); 57 58 dependsOnOld = drv: getAttr (discard (toString drv)) dependsOnOldMemo; 59 60 drvName = drv: 61 discard (substring 33 (stringLength (builtins.baseNameOf drv)) (builtins.baseNameOf drv)); 62 63 rewriteHashes = drv: hashes: runCommand (drvName drv) { nixStore = "${nix}/bin/nix-store"; } '' 64 $nixStore --dump ${drv} | sed 's|${baseNameOf drv}|'$(basename $out)'|g' | sed -e ${ 65 concatStringsSep " -e " (mapAttrsToList (name: value: 66 "'s|${baseNameOf name}|${baseNameOf value}|g'" 67 ) hashes) 68 } | $nixStore --restore $out 69 ''; 70 71 rewrittenDeps = listToAttrs [ {name = discard (toString oldDependency); value = newDependency;} ]; 72 73 rewriteMemo = listToAttrs (map 74 (drv: { name = discard (toString drv); 75 value = rewriteHashes (builtins.storePath drv) 76 (filterAttrs (n: v: builtins.elem (builtins.storePath (discard (toString n))) (referencesOf drv)) rewriteMemo); 77 }) 78 (filter dependsOnOld (builtins.attrNames references))) // rewrittenDeps; 79 80in assert (stringLength (drvName (toString oldDependency)) == stringLength (drvName (toString newDependency))); 81getAttr (discard (toString drv)) rewriteMemo