2020-06-08 13:19:20 +03:00
|
|
|
{ pkgs, nix, runCommand, writeShellScript, checkMaterialization }@defaults:
|
2020-04-27 14:20:32 +03:00
|
|
|
{ sha256 ? null # Has to make this a fixed output derivation
|
|
|
|
, sha256Arg ? "sha256"
|
|
|
|
# Name of the sha256 argument for more meaningful
|
2020-04-20 04:27:52 +03:00
|
|
|
# error messages when checking the materialization.
|
|
|
|
, materialized # null or path where to find materialized version of
|
|
|
|
# the output. If this is set but does not exist
|
|
|
|
# the derivation will fail but with a message
|
|
|
|
# advising how to populate it.
|
2020-11-26 14:33:51 +03:00
|
|
|
, this ? "this"
|
|
|
|
# A name to use when referring to the thing currently being
|
|
|
|
# materialized. Clients can pass in an attribute name or similar.
|
2020-04-27 14:20:32 +03:00
|
|
|
, reasonNotSafe ? null
|
|
|
|
# Some times there a reasont the derivation will
|
2020-04-20 04:27:52 +03:00
|
|
|
# not produce output that can be safely materialized.
|
|
|
|
# Set this to a string explaining why and materialization
|
|
|
|
# will not be used (if sha256 was set an error will be
|
|
|
|
# displayed including the reasonNotSafe string).
|
2019-12-10 06:42:25 +03:00
|
|
|
, checkMaterialization ? defaults.checkMaterialization
|
2020-04-20 04:27:52 +03:00
|
|
|
# When checkMaterialization is set the derivation
|
|
|
|
# will be calculated the slow way (without using `sha256`
|
|
|
|
# and `materialized`) the result will be used to check
|
|
|
|
# `sha256` and `materialized` (if set).
|
2019-12-10 06:42:25 +03:00
|
|
|
}: derivation:
|
|
|
|
|
|
|
|
let
|
2022-07-17 09:18:19 +03:00
|
|
|
name = derivation.name + pkgs.lib.optionalString (derivation ? version) "-${derivation.version}";
|
2019-12-10 06:42:25 +03:00
|
|
|
|
|
|
|
traceIgnoringSha256 = reason: x:
|
|
|
|
if sha256 != null
|
|
|
|
then builtins.trace ("Warning: ignoring sha256 for " + name + " " + reason) x
|
|
|
|
else x;
|
|
|
|
|
|
|
|
traceIgnoringMaterialized = reason: x:
|
|
|
|
if materialized != null
|
|
|
|
then builtins.trace ("Warning: ignoring materialized for " + name + " " + reason) x
|
|
|
|
else x;
|
|
|
|
|
|
|
|
unchecked =
|
2020-06-08 13:19:20 +03:00
|
|
|
let
|
2022-07-17 09:18:19 +03:00
|
|
|
sha256message = "${name}: To make ${this} a fixed-output derivation but not materialized, set `${sha256Arg}` to the output of the 'calculateMaterializedSha' script in 'passthru'.";
|
|
|
|
materializeMessage = "${name}: To materialize ${this} entirely, pass a writable path as the `materialized` argument and run the 'updateMaterialized' script in 'passthru'.";
|
2020-06-08 13:19:20 +03:00
|
|
|
in if reasonNotSafe != null
|
2019-12-10 06:42:25 +03:00
|
|
|
then
|
|
|
|
# Warn the user if they tried to pin stuff down when it is not safe
|
|
|
|
traceIgnoringSha256 reasonNotSafe
|
|
|
|
(traceIgnoringMaterialized reasonNotSafe calculateNoHash)
|
2020-06-08 13:19:20 +03:00
|
|
|
else if materialized != null
|
|
|
|
then calculateUseMaterialized
|
|
|
|
else if sha256 != null
|
2019-12-10 06:42:25 +03:00
|
|
|
then
|
2020-06-08 13:19:20 +03:00
|
|
|
# Let the user know how to materialize if they want to.
|
|
|
|
builtins.trace materializeMessage calculateUseHash
|
|
|
|
else # materialized == null && sha256 == null
|
|
|
|
# Let the user know how to calculate a sha256 or materialize if they want to.
|
|
|
|
builtins.trace sha256message (builtins.trace materializeMessage calculateNoHash);
|
2019-12-10 06:42:25 +03:00
|
|
|
|
|
|
|
# Build fully and check the hash and materialized versions
|
2021-02-12 00:45:23 +03:00
|
|
|
checked =
|
|
|
|
# Let the user know what we are checking. This is useful for debugging issues
|
|
|
|
# where materialization fails to prevent infinite recurstion when:
|
|
|
|
# checkMaterialization = true;
|
|
|
|
(if materialized != null
|
|
|
|
then __trace "Checking materialization in ${toString materialized}"
|
|
|
|
else x: x)
|
|
|
|
runCommand name {} (''
|
2020-04-20 04:27:52 +03:00
|
|
|
ERR=$(mktemp -d)/errors.txt
|
|
|
|
''
|
|
|
|
+ (pkgs.lib.optionalString (sha256 != null) ''
|
2020-06-08 13:19:20 +03:00
|
|
|
NEW_HASH=$(${calculateMaterializedSha})
|
|
|
|
if [[ ${sha256} != $NEW_HASH ]]; then
|
2019-12-10 06:42:25 +03:00
|
|
|
echo Changes to ${name} not reflected in ${sha256Arg}
|
|
|
|
diff -ru ${calculateUseHash} ${calculateNoHash} || true
|
2020-04-20 04:27:52 +03:00
|
|
|
echo "Calculated hash for ${name} was not ${sha256}. New hash is :" >> $ERR
|
|
|
|
echo " ${sha256Arg} = \"$NEW_HASH\";" >> $ERR
|
2019-12-10 06:42:25 +03:00
|
|
|
else
|
|
|
|
echo ${sha256Arg} used for ${name} is correct
|
|
|
|
fi
|
|
|
|
'')
|
2020-04-20 04:27:52 +03:00
|
|
|
+ (
|
|
|
|
if materialized != null && !__pathExists materialized
|
|
|
|
then ''
|
2020-06-08 13:19:20 +03:00
|
|
|
echo "Materialized nix used for ${name} is missing. To fix run: ${updateMaterialized}" >> $ERR
|
2020-04-20 04:27:52 +03:00
|
|
|
cat $ERR
|
|
|
|
false
|
|
|
|
''
|
2019-12-10 06:42:25 +03:00
|
|
|
else
|
2020-04-20 04:27:52 +03:00
|
|
|
(pkgs.lib.optionalString (materialized != null && __pathExists materialized) ''
|
|
|
|
if diff -qr ${materialized} ${calculateNoHash} &>/dev/null; then
|
|
|
|
echo materialized nix used for ${name} is correct
|
|
|
|
else
|
|
|
|
echo Changes to plan not reflected in materialized nix for ${name}
|
|
|
|
diff -ru ${materialized} ${calculateNoHash} || true
|
2020-06-08 13:19:20 +03:00
|
|
|
echo "Materialized nix used for ${name} incorrect. To fix run: ${updateMaterialized}" >> $ERR
|
2020-04-20 04:27:52 +03:00
|
|
|
fi
|
|
|
|
'')
|
|
|
|
+ ''
|
2020-06-08 13:19:20 +03:00
|
|
|
if [[ -e $ERR ]]; then
|
2020-04-20 04:27:52 +03:00
|
|
|
cat $ERR
|
|
|
|
false
|
|
|
|
else
|
2020-06-04 14:57:50 +03:00
|
|
|
cp -Lr ${unchecked} $out
|
2020-04-20 04:27:52 +03:00
|
|
|
# Make sure output files can be removed from the sandbox
|
|
|
|
chmod -R +w $out
|
|
|
|
fi
|
|
|
|
'')
|
2019-12-10 06:42:25 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
hashArgs = {
|
|
|
|
outputHashMode = "recursive";
|
|
|
|
outputHashAlgo = "sha256";
|
|
|
|
outputHash = sha256;
|
|
|
|
};
|
|
|
|
calculateNoHash = derivation;
|
2020-04-20 04:27:52 +03:00
|
|
|
calculateUseHash =
|
2020-06-04 14:57:50 +03:00
|
|
|
# Use `cp -Lr` here to get rid of symlinks so we know the result
|
2020-04-20 04:27:52 +03:00
|
|
|
# can be safely materialized (no symlinks to the store).
|
|
|
|
runCommand name hashArgs ''
|
2020-06-04 14:57:50 +03:00
|
|
|
cp -Lr ${derivation} $out
|
2020-04-20 04:27:52 +03:00
|
|
|
# Make sure output files can be removed from the sandbox
|
|
|
|
chmod -R +w $out
|
|
|
|
'';
|
2020-04-27 14:20:32 +03:00
|
|
|
calculateUseMaterialized =
|
|
|
|
assert materialized != null;
|
|
|
|
assert __pathExists materialized;
|
2022-06-02 11:11:27 +03:00
|
|
|
{ outPath = materialized; inherit name; };
|
2020-06-08 13:19:20 +03:00
|
|
|
calculateMaterializedSha =
|
2020-06-10 03:10:06 +03:00
|
|
|
writeShellScript "calculateSha" ''${nix}/bin/nix-hash --base32 --type sha256 ${calculateNoHash}'';
|
2020-06-08 13:19:20 +03:00
|
|
|
|
|
|
|
# Generate the materialized files in a particular path.
|
|
|
|
generateMaterialized =
|
|
|
|
writeShellScript "generateMaterialized" ''
|
|
|
|
TARGET=$1
|
|
|
|
|
|
|
|
# Crudely try and guard people from writing to the Nix store accidentally
|
|
|
|
if [[ ''${TARGET##/nix/store/} != $TARGET ]]; then
|
|
|
|
echo "Attempted to write to $TARGET in the Nix store! Put your materialized files somewhere else!"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Generate the files
|
2022-02-21 13:09:08 +03:00
|
|
|
mkdir -p $TARGET
|
2020-06-08 13:19:20 +03:00
|
|
|
rm -rf $TARGET
|
|
|
|
cp -r ${calculateNoHash} "$TARGET"
|
|
|
|
chmod -R +w "$TARGET"
|
|
|
|
'';
|
|
|
|
# Update the materialized files at 'materialized', which must already be set.
|
|
|
|
updateMaterialized =
|
|
|
|
assert materialized != null;
|
|
|
|
writeShellScript "updateMaterialized" ''${generateMaterialized} ${toString materialized}'';
|
2019-12-10 06:42:25 +03:00
|
|
|
|
2020-04-20 04:27:52 +03:00
|
|
|
# Materialized location was specified, but the files are not there.
|
|
|
|
missingMaterialized = materialized != null && !__pathExists materialized;
|
|
|
|
|
|
|
|
# Use the checked version if requested or if the `materialized` version
|
|
|
|
# is missing (perhaps deleted or not created yet).
|
2020-06-08 13:19:20 +03:00
|
|
|
result = if checkMaterialization || missingMaterialized
|
2019-12-10 06:42:25 +03:00
|
|
|
then checked
|
2020-06-08 13:19:20 +03:00
|
|
|
else unchecked;
|
|
|
|
|
|
|
|
in result
|
|
|
|
# Also include the scripts for fixing materialization files in passthru.
|
|
|
|
// { passthru = (result.passthru or {}) // { inherit generateMaterialized updateMaterialized calculateMaterializedSha; }; }
|