├── README.md ├── undrv.nix └── undrv.sh /README.md: -------------------------------------------------------------------------------- 1 | # undrv 2 | 3 | This is a tool that can generate the necessary bits and bobs for 4 | evaluating a drv in Nix -- from already-instantiated drvs in your 5 | store! 6 | 7 | Status: it works, but only if any uses of `toFile` in the original 8 | expressions don't write strings with references to the file. 9 | 10 | ## Sample usage 11 | 12 | ``` 13 | $ drv=$(nix-instantiate "" -A firefox | tee /dev/stderr) 14 | warning: you did not specify '--add-root'; the result might be removed by the garbage collector 15 | /nix/store/rkzxfbm7zabvij245qbcsr203ngkccaq-firefox-110.0.1.drv 16 | 17 | $ bash undrv.sh "$drv" 18 | Wrote output to undrv-output. Checking if it evaluates to the originally given drv... 19 | 20 | $ nix-instantiate undrv-output/default.nix 21 | warning: you did not specify '--add-root'; the result might be removed by the garbage collector 22 | /nix/store/rkzxfbm7zabvij245qbcsr203ngkccaq-firefox-110.0.1.drv 23 | 24 | $ du -hd0 --apparent-size undrv-output $(nix-instantiate --find-file nixpkgs)/ 25 | 9.7M undrv-output 26 | 119M /var/lib/nixpkgs/ 27 | 28 | $ du -hd0 undrv-output $(nix-instantiate --find-file nixpkgs)/ 29 | 11M undrv-output 30 | 210M /var/lib/nixpkgs/ 31 | 32 | $ # Wow! It's ~10x smaller! 33 | ``` 34 | -------------------------------------------------------------------------------- /undrv.nix: -------------------------------------------------------------------------------- 1 | { top, lib, depsDir, drvsJSON, pathsJSON }: 2 | let 3 | pathInfo = builtins.fromJSON (builtins.readFile pathsJSON); 4 | rawDrvs = builtins.fromJSON (builtins.readFile drvsJSON); 5 | restoredDrvs = lib.mapAttrs (drvPath: drvData: drv drvPath) rawDrvs; 6 | drv = drvPath: let 7 | drvData = rawDrvs.${drvPath}; 8 | 9 | # Attrset mapping contextless store path strings to the same 10 | # string but with the context of that path 11 | inputSrcs = lib.genAttrs drvData.inputSrcs (storePath: let 12 | contentAddress = pathInfo.${storePath}.ca; 13 | contentAddressScheme = builtins.head (lib.splitString ":" contentAddress); 14 | match = builtins.match "/nix/store/([a-z0-9]+)-(.*)" storePath; 15 | hash = builtins.elemAt match 0; 16 | name = builtins.elemAt match 1; 17 | file = "${depsDir + "/${hash}/${name}"}"; 18 | in { 19 | fixed = file; 20 | text = builtins.toFile name (builtins.readFile file); 21 | }.${contentAddressScheme} 22 | ); 23 | 24 | inputSrcValues = map (name: inputSrcs.${name}) drvData.inputSrcs; 25 | 26 | # Attrset mapping input drv paths to lists of output paths (with context) 27 | inputDrvOutputs = lib.mapAttrs 28 | (inputDrvPath: outputNames: 29 | let 30 | inputDrv = restoredDrvs.${inputDrvPath}; 31 | in 32 | map (outputName: inputDrv.${outputName}.outPath) outputNames 33 | ) 34 | drvData.inputDrvs; 35 | 36 | inputDrvOutputList = lib.concatLists (lib.attrValues inputDrvOutputs); 37 | 38 | restoreReferences = str: 39 | (builtins.replaceStrings inputDrvOutputList inputDrvOutputList 40 | (builtins.replaceStrings inputSrcValues inputSrcValues str)); 41 | 42 | env = lib.mapAttrs (key: value: restoreReferences value) drvData.env; 43 | 44 | result = derivation (env // { 45 | inherit (drvData) system; 46 | name = builtins.elemAt (builtins.match "/nix/store/[a-z0-9]+-(.*)\.drv" drvPath) 0; 47 | builder = restoreReferences drvData.builder; 48 | args = map restoreReferences drvData.args; 49 | } // lib.optionalAttrs (drvData.env ? outputs) { 50 | # using drvData.outputs here discards order. Eugh. 51 | outputs = lib.splitString " " drvData.env.outputs; 52 | }); 53 | in 54 | result; 55 | in 56 | restoredDrvs.${top} 57 | -------------------------------------------------------------------------------- /undrv.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | SOURCE_DIR="$(cd "$(dirname "$0")"; pwd)" 4 | 5 | if [[ -e "${output:=undrv-output}" ]]; then 6 | chmod -R u+w "$output" 7 | rm -rf "$output" 8 | fi 9 | mkdir "$output" 10 | drv="$1" 11 | nix show-derivation -r "$drv" > $output/drvs.json 12 | nix path-info --json --derivation -r "$drv" | jq 'map({key: .path, value: .}) | from_entries' > $output/paths.json 13 | cd $output 14 | mkdir deps/ 15 | IFS= 16 | default.nix <; 35 | drvsJSON = ./drvs.json; 36 | pathsJSON = ./paths.json; 37 | } 38 | EOF 39 | 40 | nix eval \ 41 | --raw \ 42 | --show-trace \ 43 | --debugger \ 44 | --file . \ 45 | drvPath >/dev/null 46 | 47 | drv=$(nix eval \ 48 | --raw \ 49 | --show-trace \ 50 | --debugger \ 51 | --file . \ 52 | drvPath) 53 | 54 | echo >&2 "Wrote output to $output. Checking if it evaluates to the originally given drv..." 55 | [[ "$1" = "$drv" ]] || nix-diff "$1" "$drv" 56 | --------------------------------------------------------------------------------