Low-level derivations: Difference between revisions

DoggoBit (talk | contribs)
Make the example technically correct
DoggoBit (talk | contribs)
Second example
Line 26: Line 26:
# Write the string <code>echo "Hello, World!"</code> to the output file;
# Write the string <code>echo "Hello, World!"</code> to the output file;
# Make the output file executable.
# Make the output file executable.
However, if we try to build this example:
{{code|lang=console|line=no|highlight=3,8|
$ nix-build example.nix
this derivation will be built:
  /nix/store/l6s955asmyc22nx7y5pg3ngnzg30r4vb-hello-world.drv
building '/nix/store/l6s955asmyc22nx7y5pg3ngnzg30r4vb-hello-world.drv'...
error: executing '/bin/bash': No such file or directory
error: builder for '/nix/store/l6s955asmyc22nx7y5pg3ngnzg30r4vb-hello-world.drv' failed with exit code 1;
      last 1 log lines:
      > error: executing '/bin/bash': No such file or directory
      For full logs, run:
        nix log /nix/store/l6s955asmyc22nx7y5pg3ngnzg30r4vb-hello-world.drv
}}
This failure could potentially be surprising. We don't have any syntactic errors, and the logic of the script sure looks good. Let's dive in into what's happening. First, we can see that the derivation is being built under the <code>/nix/store/l6s955asmyc22nx7y5pg3ngnzg30r4vb-hello-world.drv</code> path in the [[Nix store]]. The hash for the derivation is how Nix is able to guarantee isolation between different invocations of a derivation with different ''inputs''. The hash is essentially generated as a function of the input attrset.
However, the builder failed, because it couldn't find <code>/bin/bash</code>. This happens because the [[Nix ecosystem]] isolates our build environment from the system itself, we cannot use a binary that isn't defined in our derivation, in this case [[bash]] itself! In order to use it, we need to add it as an input to our derivation, by referencing the package directly:
{{File|example.nix|nix|highlight=1-3,7,11|<nowiki>
let
  pkgs = import '</nowiki><<nowiki>nixpkgs</nowiki>><nowiki>'
in
derivation {
  name = "hello-world";
  system = builtins.currentSystem;
  builder = "${pkgs.bash}/bin/bash";
  args = [
    "-c"
    ''
      echo '#!${pkgs.bash}/bin/bash' </nowiki>><nowiki> $out
      echo 'echo "Hello, World!"' </nowiki>>><nowiki> $out
      chmod +x $out
    ''
  ];
}</nowiki>}}
Building the package again, we'll come across another surprising error:
{{code|lines=no|lang=console|highlight=8|
$ nix-build example.nix
this derivation will be built:
  /nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv
building '/nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv'...
bash: line 3: chmod: command not found
error: builder for '/nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv' failed with exit code 127;
      last 1 log lines:
      > bash: line 3: chmod: command not found
      For full logs, run:
        nix log /nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv
}}
Turns out <code>chmod</code> is not part of our build environment either; that's how minimal the low-level derivation environment is! However, before we fix the script in a similar manner, let's observe another important detail: the store path. Compare the build location of this example, and the previous one; the SHAs are different! That is because our derivation has different inputs to the one before, therefore it's built under a different location in the Nix store. And to highlight this, without fixing the script, let's build it again:
{{code|lines=no|lang=console|highlight=3|
$ nix-build example.nix
this derivation will be built:
  /nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv
building '/nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv'...
bash: line 3: chmod: command not found
error: builder for '/nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv' failed with exit code 127;
      last 1 log lines:
      > bash: line 3: chmod: command not found
      For full logs, run:
        nix log /nix/store/kbxagjwnr916bg4ajn5fz13bmc3lmr8v-hello-world.drv
}}
The SHA is the same, because our inputs haven't changed. This is the essence to why derivations are ''pure functions'' of their inputs.