Add 'prog/lang-nix/nix-freeze-derivation/' from commit 'c72b1964d4e379a43c73c2c03483ac4a0ceb5c84'

git-subtree-dir: prog/lang-nix/nix-freeze-derivation
git-subtree-mainline: 4f3dfcc1ae
git-subtree-split: c72b1964d4
This commit is contained in:
Dmitry Bogatov 2023-07-23 16:33:18 -04:00
commit 26daab923d
3 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,71 @@
nix-freeze-derivation
=====================
This project provides Nix function that makes derivation, with every
file in input derivation replaced with symlink to single file in Nix
storage. For example, let's freeze derivation for `nnn` file manager:
let
nixpkgs = import <nixpkgs> { };
nix-freeze-derivation = nixpkgs.fetchgit {
url = "https://git.sr.ht/~kaction/nix-freeze-derivation";
rev = "...";
sha256 = "...";
};
freeze = nixpkgs.callPackage nix-freeze-derivation {};
in {
frozen-nnn = freeze nnn;
inherit nnn;
}
Here is original derivation:
├── bin
│   └── nnn
└── share
├── bash-completion
│   └── completions
│   └── nnn.bash
├── fish
│   └── vendor_completions.d
│   └── nnn.fish
├── man
│   └── man1
│   └── nnn.1.gz
└── zsh
└── site-functions
└── _nnn
And here is frozen derivation:
├── bin
│   └── nnn -> /nix/store/zshsj2hy2c8fiqh3vw08yq5lq8k4abx9-frozen-file
└── share
├── bash-completion
│   └── completions
│   └── nnn.bash -> /nix/store/8qf7q4j6aizbd6kgfp8dgwchf789v7dl-frozen-file
├── fish
│   └── vendor_completions.d
│   └── nnn.fish -> /nix/store/58rm78igazcsgcik4hwj4d2mkzn5h6sn-frozen-file
├── man
│   └── man1
│   └── nnn.1.gz -> /nix/store/7grf462gxkp1jifyw8gz41v30z8v3442-frozen-file
└── zsh
└── site-functions
└── _nnn -> /nix/store/11k4jkycz9zj7s708m9jw41yyskr7aas-frozen-file
For almost all intentions and purposes frozen derivation is equivalent
to original one, but copying it between machines only copies changed
files. On other hand, freezing derivation takes some time, especially
noticable with derivation that do not involve real compiling. For
example, freezing Python package can take as much time as building
package itself.
`nix-freeze-derivation` was inspired by `nix-freeze-tree`[^1], which is
much faster and can freeze arbitrary path, not just derivation.
Unfortunately, `nix-freeze-tree` does not work with derivations that has
propagated build inputs[^2], that is why `nix-freeze-derivation` was
created.
[^1] https://git.sr.ht/~jack/nix-freeze-tree
[^2] https://todo.sr.ht/~jack/nix-freeze-tree/7

View File

@ -0,0 +1,70 @@
{ 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

View File

@ -0,0 +1,6 @@
with (import <nixpkgs> {});
let freeze = callPackage ./default.nix {};
in {
frozen-nnn = freeze nnn;
inherit nnn;
}