source/prog/lang-nix/nix-freeze-derivation/default.nix

78 lines
2.2 KiB
Nix

{
lib,
stdenv,
runCommand,
rsync,
}: drv: let
join = dir: name: dir + ("/" + name);
files = scan drv;
scan = path: let
f = k: v:
if v == "directory"
then scan (join path k)
else [(join path k)];
in
lib.pipe path [
builtins.readDir
(builtins.mapAttrs f)
builtins.attrValues
lib.concatLists
];
pathDerivation = path: let
location =
builtins.replaceStrings [(builtins.toString drv)] [""] path;
name = lib.pipe location [
(builtins.substring 1 (-1))
(builtins.replaceStrings ["/"] ["."])
# Nix is too smart and too paranoid for what I am doing here.
# It infers that "name" string is derived from path, hence
# depends on it. I know better.
builtins.unsafeDiscardStringContext
];
# Break reference to original derivation.
#
# Reference to file in form ${path} copies file to store, if is is
# not already there. And if it is already there, as part of other
# derivation, reference to contained derivation is retained, which
# is not what we need here. So here we make intermediate
# derivation that contains copy of file at $out.
file = runCommand "frozen-file" {} ''
cp -v ${path} $out
'';
in
stdenv.mkDerivation {
name = "${name}";
phases = ["installPhase"];
inherit (drv) propagatedBuildInputs;
# Link file, copy symlink. Distinction is important to make sure
# relative symlinks are not broken. Actually, there should not be
# relative symlinks in first place, but still.
installPhase = ''
mkdir -p $out/${location}
rmdir $out/${location}
if test -h ${file} ; then
cp -v ${file} $out/${location}
else
ln -s ${file} $out/${location}
fi
'';
};
rsyncCommand = path: let
src = pathDerivation path;
in "rsync -az ${src}/* $out/";
union = stdenv.mkDerivation {
inherit (drv) name buildInputs propagatedBuildInputs checkInputs;
nativeBuildInputs = [rsync];
phases = ["installPhase"];
installPhase =
"mkdir -p $out;"
+ builtins.concatStringsSep ";" (builtins.map rsyncCommand files);
};
in
union