at 18.09-beta 3.3 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, verbose ? true }: 21 22with lib; 23 24let 25 warn = if verbose then builtins.trace else (x: y: y); 26 references = import (runCommand "references.nix" { exportReferencesGraph = [ "graph" drv ]; } '' 27 (echo { 28 while read path 29 do 30 echo " \"$path\" = [" 31 read count 32 read count 33 while [ "0" != "$count" ] 34 do 35 read ref_path 36 if [ "$ref_path" != "$path" ] 37 then 38 echo " (builtins.storePath $ref_path)" 39 fi 40 count=$(($count - 1)) 41 done 42 echo " ];" 43 done < graph 44 echo }) > $out 45 '').outPath; 46 47 discard = builtins.unsafeDiscardStringContext; 48 49 oldStorepath = builtins.storePath (discard (toString oldDependency)); 50 51 referencesOf = drv: references.${discard (toString drv)}; 52 53 dependsOnOldMemo = listToAttrs (map 54 (drv: { name = discard (toString drv); 55 value = elem oldStorepath (referencesOf drv) || 56 any dependsOnOld (referencesOf drv); 57 }) (builtins.attrNames references)); 58 59 dependsOnOld = drv: dependsOnOldMemo.${discard (toString drv)}; 60 61 drvName = drv: 62 discard (substring 33 (stringLength (builtins.baseNameOf drv)) (builtins.baseNameOf drv)); 63 64 rewriteHashes = drv: hashes: runCommand (drvName drv) { nixStore = "${nix.out}/bin/nix-store"; } '' 65 $nixStore --dump ${drv} | sed 's|${baseNameOf drv}|'$(basename $out)'|g' | sed -e ${ 66 concatStringsSep " -e " (mapAttrsToList (name: value: 67 "'s|${baseNameOf name}|${baseNameOf value}|g'" 68 ) hashes) 69 } | $nixStore --restore $out 70 ''; 71 72 rewrittenDeps = listToAttrs [ {name = discard (toString oldDependency); value = newDependency;} ]; 73 74 rewriteMemo = listToAttrs (map 75 (drv: { name = discard (toString drv); 76 value = rewriteHashes (builtins.storePath drv) 77 (filterAttrs (n: v: builtins.elem (builtins.storePath (discard (toString n))) (referencesOf drv)) rewriteMemo); 78 }) 79 (filter dependsOnOld (builtins.attrNames references))) // rewrittenDeps; 80 81 drvHash = discard (toString drv); 82in assert (stringLength (drvName (toString oldDependency)) == stringLength (drvName (toString newDependency))); 83rewriteMemo.${drvHash} or (warn "replace-dependency.nix: Derivation ${drvHash} does not depend on ${discard (toString oldDependency)}" drv)