replaceVarsWith
pkgs.replaceVarsWith
replaceVarsWith
is a wrapper around the bash function substitute
in the stdenv. It allows for terse replacement of names in the specified path, while checking
for common mistakes such as naming a replacement that does nothing or forgetting a variable which
needs to be replaced.
As with the --subst-var-by
flag, names are encoded as @name@
in the provided file at the provided path.
Any unmatched variable names in the file at the provided path will cause a build failure.
By default, any remaining text that matches @[A-Za-z_][0-9A-Za-z_'-]@
in the output after replacement
has occurred will cause a build failure. Variables can be excluded from this check by passing "null" for them.
Inputs
src
(Store Path String)- The file in which to replace variables.
replacements
(AttrsOf String)- Each entry in this set corresponds to a
--subst-var-by
entry insubstitute
or null to keep it unchanged. dir
(String)- Sub directory in $out to store the result in. Commonly set to "bin".
isExecutable
(Boolean)- Whether to mark the output file as executable.
Most arguments supported by mkDerivation are also supported, with some exceptions for which an error will be thrown.
Example
{ replaceVarsWith }:
replaceVarsWith {
src = ./my-setup-hook.sh;
replacements = { world = "hello"; };
dir = "bin";
isExecutable = true;
}
See ../../test/replace-vars/default.nix
for tests of this function. Also see replaceVars
for a short
version with src and replacements only.
Noogle detected
Learn about functors
Implementation
The following is the current implementation of this function.
{
src,
replacements,
dir ? null,
isExecutable ? false,
...
}@attrs:
let
# We use `--replace-fail` instead of `--subst-var-by` so that if the thing isn't there, we fail.
subst-var-by = name: value: [
"--replace-fail"
(lib.escapeShellArg "@${name}@")
(lib.escapeShellArg (lib.defaultTo "@${name}@" value))
];
substitutions = lib.concatLists (lib.mapAttrsToList subst-var-by replacements);
left-overs = map ({ name, ... }: name) (
builtins.filter ({ value, ... }: value == null) (lib.attrsToList replacements)
);
optionalAttrs =
if (builtins.intersectAttrs attrs forcedAttrs == { }) then
builtins.removeAttrs attrs [ "replacements" ]
else
throw "Passing any of ${builtins.concatStringsSep ", " (builtins.attrNames forcedAttrs)} to replaceVarsWith is not supported.";
forcedAttrs = {
doCheck = true;
dontUnpack = true;
preferLocalBuild = true;
allowSubstitutes = false;
buildPhase = ''
runHook preBuild
target=$out
if test -n "$dir"; then
target=$out/$dir/$name
mkdir -p $out/$dir
fi
substitute "$src" "$target" ${lib.concatStringsSep " " substitutions}
if test -n "$isExecutable"; then
chmod +x $target
fi
runHook postBuild
'';
# Look for Nix identifiers surrounded by `@` that aren't substituted.
checkPhase =
let
lookahead =
if builtins.length left-overs == 0 then "" else "(?!${builtins.concatStringsSep "|" left-overs}@)";
regex = lib.escapeShellArg "@${lookahead}[a-zA-Z_][0-9A-Za-z_'-]*@";
in
''
runHook preCheck
if grep -Pqe ${regex} "$target"; then
echo The following look like unsubstituted Nix identifiers that remain in "$target":
grep -Poe ${regex} "$target"
echo Use the more precise '`substitute`' function if this check is in error.
exit 1
fi
runHook postCheck
'';
};
in
stdenvNoCC.mkDerivation (
{
name = baseNameOf (toString src);
}
// optionalAttrs
// forcedAttrs
)