28ff7991a1
> Currently when distributed builds enabled each derivation is uploaded to the remote server to have their hashes rewritten. fixes issue 13056
84 lines
3.2 KiB
Nix
84 lines
3.2 KiB
Nix
{ runCommandLocal, 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 (runCommandLocal "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: runCommandLocal (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)
|