on e94de40 help sign in
haskell.nix/lib/materialize.nix
156 lines 6.6 kB history raw
1{ pkgs, nix, runCommand, writeShellScript, checkMaterialization }@defaults:
2{ sha256 ? null # Has to make this a fixed output derivation
3, sha256Arg ? "sha256"
4 # Name of the sha256 argument for more meaningful
5 # error messages when checking the materialization.
6, materialized # null or path where to find materialized version of
7 # the output. If this is set but does not exist
8 # the derivation will fail but with a message
9 # advising how to populate it.
10, reasonNotSafe ? null
11 # Some times there a reasont the derivation will
12 # not produce output that can be safely materialized.
13 # Set this to a string explaining why and materialization
14 # will not be used (if sha256 was set an error will be
15 # displayed including the reasonNotSafe string).
16, checkMaterialization ? defaults.checkMaterialization
17 # When checkMaterialization is set the derivation
18 # will be calculated the slow way (without using `sha256`
19 # and `materialized`) the result will be used to check
20 # `sha256` and `materialized` (if set).
21}: derivation:
23let
24 inherit (derivation) name;
26 traceIgnoringSha256 = reason: x:
27 if sha256 != null
28 then builtins.trace ("Warning: ignoring sha256 for " + name + " " + reason) x
29 else x;
31 traceIgnoringMaterialized = reason: x:
32 if materialized != null
33 then builtins.trace ("Warning: ignoring materialized for " + name + " " + reason) x
34 else x;
36 unchecked =
37 let
38 sha256message = "To make this a fixed-output derivation but not materialized, set `${sha256Arg}` to the output of ${calculateMaterializedSha}";
39 materializeMessage = "To materialize the output entirely, pass a writable path as the `materialized` argument and pass that path to ${generateMaterialized}";
40 in if reasonNotSafe != null
41 then
42 # Warn the user if they tried to pin stuff down when it is not safe
43 traceIgnoringSha256 reasonNotSafe
44 (traceIgnoringMaterialized reasonNotSafe calculateNoHash)
45 else if materialized != null
46 then calculateUseMaterialized
47 else if sha256 != null
48 then
49 # Let the user know how to materialize if they want to.
50 builtins.trace materializeMessage calculateUseHash
51 else # materialized == null && sha256 == null
52 # Let the user know how to calculate a sha256 or materialize if they want to.
53 builtins.trace sha256message (builtins.trace materializeMessage calculateNoHash);
55 # Build fully and check the hash and materialized versions
56 checked = runCommand name {} (''
57 ERR=$(mktemp -d)/errors.txt
58 ''
59 + (pkgs.lib.optionalString (sha256 != null) ''
60 NEW_HASH=$(${calculateMaterializedSha})
61 if [[ ${sha256} != $NEW_HASH ]]; then
62 echo Changes to ${name} not reflected in ${sha256Arg}
63 diff -ru ${calculateUseHash} ${calculateNoHash} || true
64 echo "Calculated hash for ${name} was not ${sha256}. New hash is :" >> $ERR
65 echo " ${sha256Arg} = \"$NEW_HASH\";" >> $ERR
66 else
67 echo ${sha256Arg} used for ${name} is correct
68 fi
69 '')
70 + (
71 if materialized != null && !__pathExists materialized
72 then ''
73 echo "Materialized nix used for ${name} is missing. To fix run: ${updateMaterialized}" >> $ERR
74 cat $ERR
75 false
76 ''
77 else
78 (pkgs.lib.optionalString (materialized != null && __pathExists materialized) ''
79 if diff -qr ${materialized} ${calculateNoHash} &>/dev/null; then
80 echo materialized nix used for ${name} is correct
81 else
82 echo Changes to plan not reflected in materialized nix for ${name}
83 diff -ru ${materialized} ${calculateNoHash} || true
84 echo "Materialized nix used for ${name} incorrect. To fix run: ${updateMaterialized}" >> $ERR
85 fi
86 '')
87 + ''
88 if [[ -e $ERR ]]; then
89 cat $ERR
90 false
91 else
92 cp -Lr ${unchecked} $out
93 # Make sure output files can be removed from the sandbox
94 chmod -R +w $out
95 fi
96 '')
97 );
99 hashArgs = {
100 outputHashMode = "recursive";
101 outputHashAlgo = "sha256";
102 outputHash = sha256;
103 };
104 calculateNoHash = derivation;
105 calculateUseHash =
106 # Use `cp -Lr` here to get rid of symlinks so we know the result
107 # can be safely materialized (no symlinks to the store).
108 runCommand name hashArgs ''
109 cp -Lr ${derivation} $out
110 # Make sure output files can be removed from the sandbox
111 chmod -R +w $out
112 '';
113 calculateUseMaterialized =
114 assert materialized != null;
115 assert __pathExists materialized;
116 runCommand name (pkgs.lib.optionalAttrs (sha256 == null) hashArgs) ''
117 cp -Lr ${materialized} $out
118 # Make sure output files can be removed from the sandbox
119 chmod -R +w $out
120 '';
121 calculateMaterializedSha =
122 writeShellScript "calculateSha" ''${nix}/bin/nix-hash --base32 --type sha256 ${calculateNoHash}'';
124 # Generate the materialized files in a particular path.
125 generateMaterialized =
126 writeShellScript "generateMaterialized" ''
127 TARGET=$1
129 # Crudely try and guard people from writing to the Nix store accidentally
130 if [[ ''${TARGET##/nix/store/} != $TARGET ]]; then
131 echo "Attempted to write to $TARGET in the Nix store! Put your materialized files somewhere else!"
132 exit 1
133 fi
135 # Generate the files
136 rm -rf $TARGET
137 cp -r ${calculateNoHash} "$TARGET"
138 chmod -R +w "$TARGET"
139 '';
140 # Update the materialized files at 'materialized', which must already be set.
141 updateMaterialized =
142 assert materialized != null;
143 writeShellScript "updateMaterialized" ''${generateMaterialized} ${toString materialized}'';
145 # Materialized location was specified, but the files are not there.
146 missingMaterialized = materialized != null && !__pathExists materialized;
148 # Use the checked version if requested or if the `materialized` version
149 # is missing (perhaps deleted or not created yet).
150 result = if checkMaterialization || missingMaterialized
151 then checked
152 else unchecked;
154in result
155 # Also include the scripts for fixing materialization files in passthru.
156 // { passthru = (result.passthru or {}) // { inherit generateMaterialized updateMaterialized calculateMaterializedSha; }; }