Main questions:
writeShellApplication
results in what?I am following this tutorial from nix.dev.
I am stuck at the end of section 2.11, just before beginning section 2.12.
Basically, I have everything working as expected. But I don't understand how everything is working (lol).
I have a default.nix
:
{
pkgs,
lib,
config,
...
}: {
options = {
scripts = {
output = lib.mkOption {
type = lib.types.package;
};
geocode = lib.mkOption {
type = lib.types.package;
};
};
requestParams = lib.mkOption {
type = lib.types.listOf lib.types.str;
};
map = {
zoom = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default = 10;
};
center = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "brazil";
};
};
};
config = {
scripts.output = pkgs.writeShellApplication {
name = "map";
runtimeInputs = with pkgs; [
curl
feh
];
text = ''
${./map.sh} ${lib.concatStringsSep " " config.requestParams} | feh -
'';
};
scripts.geocode = pkgs.writeShellApplication {
name = "geocode";
runtimeInputs = with pkgs; [
curl
jq
];
text = ''
exec ${./geocode.sh} "$@"
'';
};
requestParams = [
"size=640x640"
"scale=2"
(
lib.mkIf (config.map.zoom != null)
"zoom=${toString config.map.zoom}"
)
(
lib.mkIf (config.map.center != null)
"center=\"$(${config.scripts.geocode}/bin/geocode ${lib.escapeShellArg config.map.center})\""
)
];
map.zoom = 3;
map.center = "brazil";
};
}
and I also have an eval.nix
:
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11";
pkgs = import nixpkgs {};
in
pkgs.lib.evalModules {
modules = [
(
{
config,
...
}: {
config._module.args = {
inherit pkgs;
};
}
)
./default.nix
];
}
[user@hostname:~/path/to/project/folder]$ ls -1
gives me
default.nix
eval.nix
geocode.sh
map.sh
shell.nix
Running [user@hostname:~/path/to/project/folder]$ nix-build eval.nix -A config.scripts.output
prints a Nix Store path to stdout
, and then running
[user@hostname:~/path/to/project/folder]$ ls -1
again gives me
default.nix
eval.nix
geocode.sh
map.sh
result
shell.nix
And result/bin/map
works flawlessly.
nix-build
that
writeShellApplication
evaluates to a derivation. Is this inference
correct?My understanding of nix-build
, for reference, is a follows:
nix-build
is a wrapper onnix-instantiate
andnix-store --realize
.
nix-instantiate
takes in a path to a package (i.e., a path to a.nix
file with a function that evaluates to a derivation), evaluates that package (i.e., evaluates the function defined inside the.nix
file that is located in the path given to it), stores that package in some path in the Nix Store (i.e., stores the value obtained from the evaluation of function defined inside the.nix
file that is located in the path given to it in some path in the Nix store), and then prints that path tostdout
.
nix-store --realize
takes in a path to some derivation stored in the Nix Store, goes through with it (i.e. derives files according to the instructions of the derivation itself), stores the build result in the some path in the Nix Store (i.e., stores the derived files in some path in the Nix Store), and symlinks between that Nix Store path and the current directory (i.e., creates theresult
symlink, linking between the path in which the derived files were stored in the Nix Store and the current directory).All
nix-build
does is take a path to a.nix
file containing a Nix Expression that evaluates to a derivation, and runsnix-instantiate
followed bynix-store --realize
on it: it "instantiates" the derivation that is obtained by evaluating the Nix Expression defined in the.nix
file that is located in the path given it, storing that derivation in some path in the Nix Store, and then it disposes of that path to "realize" that derivation, symlinking to the current directory the build result.
Now, you can use nix-build
with a -A
flag. Here's my understanding of that:
man nix-build
says that the options passed to the-A
flag are passed tonix-instantiate
. With this flag, you can specify a value from an attribute within an attribute set that is obtained by evaluating the Nix Expression defined in the./nix
file located in the path passed to the command to be used instead of the Nix Expression defined in the./nix
file located in the path passed to the command. All you have to do is specify the name of that attribute.
Now, assuming that my inference at 1.
is correct, and that my understanding of
the -A
flag in nix-build
is also sound, here's my second question:
[user@hostname:~/path/to/project/folder]$ nix-build eval.nix -A config.scripts.output
, how come Nix knew to also "build"
config.scripts.geocode
? I never pointed nix-build
to the expression
located at config.scripts.geocode
. Does Nix implictly create a dependency
between the derivation that is obtained by evaluating config.scripts.output
and the one that is obtained by evaluating config.script.geocode
? Perhaps
some functionality of a package's
closure? How would
that work?Also, the reason I assert that "Nix knew how to 'build'
config.scripts.geocode
" is because, well, ./result/bin/map
works. Perhaps I
am also wrong about this.
After some quick discussion in Vimjoyer's Discord server:
Nix evaluates config.scripts.output
because I pointed nix-build
to it
with the -A
flag.
To evaluate config.scripts.output
, Nix must know the value of
config.requestParams
.
Evaluating config.requestParams
requires knowing the value of
${config.scripts.geocode}
${config.script.geocode}
is a string
interpolation. Evaluating
the value of config.script.geocode
eventually
boils down to the return value of mkDerivation
. Per
this, mkDerivation
outputs a special attribute set that can be used in string interpolation,
and in that case evaluates to the Nix store path of its build result.
So this is why config.script.geocode
gets built.