Nix Derivation Basics
First import a pinned version of nixpkgs
so that we all get the same result:
nix-repl> nixpkgs-src = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/c1e5f8723ceb684c8d501d4d4ae738fef704747e.tar.gz";
sha256 = "02k3l9wnwpmq68xmmfy4wb2panqa1rs04p1mzh2kiwn0449hl86j";
}
nix-repl> nixpkgs = import nixpkgs-src {}
We use the pinned version of nixpkgs
so that everyone following the
tutorial will get the exact same derivation.
Standard Derivation
nix-repl> hello-drv = nixpkgs.stdenv.mkDerivation {
name = "hello.txt";
unpackPhase = "true";
installPhase = ''
echo -n "Hello World!" > $out
'';
}
nix-repl> hello-drv
«derivation /nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv»
$ cat /nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv
Derive([("out","/nix/store/f6qq9bwv0lxw5glzjmin1y1r1s3kangv-hello.txt","","")],...)
$ nix show-derivation /nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv
{
"/nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv": {
"outputs": {
"out": {
"path": "/nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt"
}
},
"inputSrcs": [
"/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"
],
...
}
}
Building Derivation
We can now build our derivation:
$ nix-build /nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv
/nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt
$ cat /nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt
Hello World!
This may take some time to load on your computer, as Nix fetches the essential build tools that are commonly needed to build Nix packages.
We can also build the derivation within Nix repl using the :b
command:
nix-repl> :b hello-drv
[1 built, 0.0 MiB DL]
this derivation produced the following outputs:
out -> /nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt
Tracing Derivation
Our hello-drv
produce the same output as hello.txt
in previous chapter,
but produce different output in the Nix store. (previously we had
/nix/store/925f1jb1ajrypjbyq7rylwryqwizvhp0-hello.txt
)
We can trace the dependencies of the derivation back to its source:
$ nix-store --query --deriver /nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt
/nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv
$ nix-store --query --deriver /nix/store/925f1jb1ajrypjbyq7rylwryqwizvhp0-hello.txt
unknown-deriver
Our hello.txt
built from stdenv.mkDerivation
is built from a derivation
artifact hello.txt.drv
, but our hello.txt
created from builtins.path
has no deriver.
In other words, the Nix artifacts are different because they are produced from
different derivations.
We can further trace the dependencies of hello.txt.drv
:
$ nix-store -qR /nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv
/nix/store/01n3wxxw29wj2pkjqimmmjzv7pihzmd7-which-2.21.tar.gz.drv
/nix/store/03f77phmfdmsbfpcc6mspjfff3yc9fdj-setup-hook.sh
...
That's a lot of dependencies! Where are they being used? We will learn about it in the next chapter.
Derivation in a Nix File
We save the same earlier derivation we defined inside a Nix file named
hello.nix
. Now we can build our derivation directly:
$ nix-build 04-derivations/01-derivation-basics/hello.nix
/nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt
We can also get the derivation without building it using nix-instantiate
:
$ nix-instantiate 04-derivations/01-derivation-basics/hello.nix
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
/nix/store/ad6c51ia15p9arjmvvqkn9fys9sf1kdw-hello.txt.drv
Ignore the warning from nix-instantiate
, as we don't care whether the derivation
is deleted during Nix garbage collection.
Notice that both the derivation and the build output have the same hash
as the earlier result we had in nix repl
.
Caching Nix Build Artifacts
We create hello-sleep.nix
as a variant of
hello.nix
which sleeps for 10 seconds in its buildPhase
.
(We will go through how each phase works in the next chapter)
The 10 seconds sleep simulates the time taken to compile a program.
We can see what happens when we try to build the same Nix derivation
multiple times.
First, instantiating a derivation is not affected by the build time:
$ time nix-instantiate 04-derivations/01-derivation-basics/hello-sleep.nix
/nix/store/58ngrpwgv6hl633a1iyjbmjqlbdqjw92-hello.txt.drv
real 0m0,217s
user 0m0,179s
sys 0m0,032s
The first time we build hello-sleep.nix
, it is going to take about 10 seconds.
We can also see the logs we printed during the build phase is shown:
$ time nix-build 04-derivations/01-derivation-basics/hello-sleep.nix
these derivations will be built:
/nix/store/58ngrpwgv6hl633a1iyjbmjqlbdqjw92-hello.txt.drv
building '/nix/store/58ngrpwgv6hl633a1iyjbmjqlbdqjw92-hello.txt.drv'...
unpacking sources
patching sources
configuring
no configure script, doing nothing
building
Building hello world...
Finished building hello world!
installing
post-installation fixup
shrinking RPATHs of ELF executables and libraries in /nix/store/lm801yriwjj4298ry74hdv5j0rpkpacq-hello.txt
strip is /nix/store/bnjps68g8ax6abzvys2xpx12imrx8949-binutils-2.31.1/bin/strip
patching script interpreter paths in /nix/store/lm801yriwjj4298ry74hdv5j0rpkpacq-hello.txt
checking for references to /build/ in /nix/store/lm801yriwjj4298ry74hdv5j0rpkpacq-hello.txt...
/nix/store/lm801yriwjj4298ry74hdv5j0rpkpacq-hello.txt
real 0m12,202s
user 0m0,371s
sys 0m0,084s
But the next time we build hello-sleep.nix
, it will take no time to build,
and there is no build output:
$ time nix-build 03-nix-basics/05-derivation/hello-sleep.nix
/nix/store/lm801yriwjj4298ry74hdv5j0rpkpacq-hello.txt
real 0m0,310s
user 0m0,256s
sys 0m0,047s
Nix determines whether a derivation needs to be rebuilt based on the input
derivation. For our case, in both calls to hello-sleep.nix
,
nix-build
instantiates the derivation behind the scene: i.e.
/nix/store/58ngrpwgv6hl633a1iyjbmjqlbdqjw92-hello.txt.drv
.
So it determines that the result has previously already
been built, and reuses the same Nix artifact.
Derivation as File
With the duck-typing nature of Nix, derivations act just like files in Nix.
We can actually treat the hello-drv
we defined earlier as a file and
read from it:
nix-repl> builtins.readFile hello-drv
querying info about missing paths"Hello World!"
How does that work? Internally Nix lazily builds a
derivation when it is evaluated, and turns it into
a file path. We can verify that by using builtins.toPath
:
nix-repl> builtins.toPath hello-drv
"/nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt"
With this property, we can also import derivations from a Nix file, and then use it as if the derivation has been built:
nix-repl> hello = import ./code/04-derivations/01-derivation-basics/hello.nix
nix-repl> builtins.readFile hello
querying info about missing paths"Hello World!"
We can even use a derivation as a string. Nix automatically builds the derivation when it is evaluated as a string:
nix-repl> "path of hello: ${hello}"
"path of hello: /nix/store/z449wrqvwncs8clk7bsliabv1g1ci3n3-hello.txt"