Just looked at the current implementation of writeTextFile
,
source of `writeTextFile`:
writeTextFile =
{ name # the name of the derivation
, text
, executable ? false # run chmod +x ?
, destination ? "" # relative path appended to $out eg "/bin/foo"
, checkPhase ? "" # syntax checks, e.g. for scripts
, meta ? { }
}:
runCommand name
{ inherit text executable checkPhase meta;
passAsFile = [ "text" ];
# Pointless to do this on a remote machine.
preferLocalBuild = true;
allowSubstitutes = false;
}
''
target=$out${lib.escapeShellArg destination}
mkdir -p "$(dirname "$target")"
if [ -e "$textPath" ]; then
mv "$textPath" "$target"
else
echo -n "$text" > "$target"
fi
eval "$checkPhase"
(test -n "$executable" && chmod +x "$target") || true
'';
and couldnāt figure out these:
QUESTION 1. Where does textPath
come from?
Probably missed a with
statement or something otherwise obvious, but it just drives me crazy.
ANSWER
As @abathur points out below, I totally missed the passAsFile
attribute that can be passed to a derivation:
A list of names of attributes that should be passed via files rather than environment variables.
Follow-up to QUESTION 1: Why is there a need then to check if textPath
exists?
The body of the writeTextFile
function has the following check:
if [ -e "$textPath" ]; then
mv "$textPath" "$target"
else
echo -n "$text" > "$target"
fi
Doesnāt passAsFile
ensure that this file will always be created? At least, I couldnāt see any write*
functions building on writeTextFile
tamper with this one (and donāt even know how they could).
ANSWER
From @YorikSar:
As for the check whether
textPath
exists, I would guess that it was written when the support forpassAsFile
was just added to Nix, and the author wanted to support older versions of Nix as well as the new one. And hereās the PR that introduced that: writeTextFile: Use passAsFile if available by wmertens Ā· Pull Request #6411 Ā· NixOS/nixpkgs Ā· GitHub - indeed, it was added even beforepassAsFile
support was released.
QUESTION 2. How does the meta
input attribute get its default attributes?
Just learned about meta-attributes while writing this question and looking up the suggested threads in the side panelā¦
writeTextFile
's input attribute set has a meta
attribute that defaults to {}
, but it is not used anywhere in its body. writeTextFile
calls runCommand
, but none of the runCommand*
funcitons touch meta
explicitly. The only place meta
is used (in relation with writeTextFile
) is in writeShellApplication
:
meta.mainProgram = name;
Attributes cannot be set this way,
nix-repl> as = { a = 1; b = 3; }
nix-repl> as.c = 27
error: syntax error, unexpected '=', expecting end of file
at «string»:1:6:
1| as.c = 27
| ^
so where is it set?
ANSWER
(The wrong path I went down on - leaving this here for educational purposes)
@YorikSar pointed out below in an answer that this is wrong, but leaving this here just in case.
Did a brute-force grep on
'mainProgram = [^"]'
(hoping that the default value wonāt be an empty stringā¦), and the most generic result not tied to any specific packages was inpkgs/stdenv/generic/check-meta.nix
. Following the breadcrumbs in this file,
mainProgram = str;
āmetaTypes
ācheckMetaAttr
ācheckMeta
ā (kind of lost the thread here, but I thinkcheckMeta
is then called in the function body)it feels like this is the place that sets the missing attributes. Started looking if
pkgs/stdenv/generic/check-meta.nix
is called anywhere meaningful, and the answer is yes:pkgs/stdenv/generic/make-derivation.nix:6
ā pkgs/stdenv/generic/default.nix:55
āmkDerivation
āwriteShellApplication
āwriteTextFile
ārunCommand
ārunCommandWith
ā (callsstdenv.mkDerivation
)The weakest part of this is that
check-meta
is quite complex and I simply intuited that the defaults are set thereā¦
@YorikSarās first answer below explains where meta
default attributes are set, but it turns out that my question is unrelated to that; @YorikSarās second answer pointed out that I did not consider the scope, and thus made false assumptions:
meta.mainProgram = name;
is used like this in writeShellApplication
:
writeShellApplication =
{ # ...omitted...
}:
writeTextFile {
# ...omitted...
meta.mainProgram = name;
}
where the attribute set is defined to be an input for writeTextFile
. As the input attribute set is just being constructed, one can apply the Nix convention to define nested attribute sets more succinctly, so the above example can be re-written as (pasting @YorikSar 's example verbatim):
writeShellApplication =
{ # ...omitted...
}:
writeTextFile {
# ...omitted...
meta = {
mainProgram = name;
};
}
In nix repl
:
nix-repl> ({ a.b = 27; }).a.b
27