# Nix Pills - notes
###### tags: `INFO5` `Nix`
**Author: @Coko**
Nix Pills by **Luca Bruno** (aka Lethalman): https://nixos.org/guides/nix-pills
## Chapter 1: [Why You Should Give it a Try](https://nixos.org/guides/nix-pills/why-you-should-give-it-a-try.html)
## Chapter 2: [Install on Your Running System](https://nixos.org/guides/nix-pills/install-on-your-running-system.html)
### Install Nix:
```bash
curl -L https://nixos.org/nix/install | sh
```
### Useful directories:
- ~/.nix-profile
- ~/.nix-defexpr/channels
- ~/.profile
- /nix/store/
- /nix/var/nix/gcroots
- /nix/var/nix/gcroots/auto/
Enter the Nix env: `source ~/.nix-profile/etc/profile.d/nix.sh`
Nix operators list: https://nixos.org/manual/nix/stable/expressions/language-operators.html
### Common `nix repl` built-ins:
- `builtins.div <a> <b>`: Division of **a** by **b**.
- `builtins.trace <msg> <value>`: Function that takes two arguments. The first is the **message to display**, the second is the **value to return**. It's usually used for debugging purpose.
- `builtins.currentSystem`: Shows host system. (ex: *"x86_64-linux"*)
- `builtins.isAttrs <a>`: Returns true if the argument is a set.
- `builtins.attrNames <set>`: Returns a list of keys of the given set.
- `builtins.toString <obj>`: Converts object to a string.
- `builtins.functionArgs <fun>`: Lists the argument names for a given function and show whether they are optional.
- `builtins.intersectAttrs <set1> <set2>`: Returns a set whose names are the intersection, and the attribute values are taken from the second set.
### Other nix commands:
- `nix-instantiate <d>`: Instantiate store derivations from Nix expressions.
- `nix-store --dump <path>`: Produce a NAR (Nix ARchive) file containing the contents of the file system tree rooted at path. The archive is written to standard output.
- `nix-store --restore <path>`: Unpack a NAR archive to path, which must not already exist. The archive is read from standard input.
- `nix-shell`: Start an interactive shell based on a Nix expression.
- `nix-collect-garbage`: Delete unreachable store paths.
- `nix-collect-garbage -d`: Delete older generations on all profiles. Used to clean up the system.
- `nix-hash --type sha256 <file>`: Compute the sha256 of a file.
- `nix-store --dump <file>|sha256sum`: Same as above.
## Chapter 3: [Enter the Environment](https://nixos.org/guides/nix-pills/enter-environment.html#enter-environment)
### Install stuff
install package hello: `nix-env -i hello`
remove package hello: `nix-env -e hello`
list generations: `nix-env --list-generations`
list generations (+ actual location): `nix-env -q --out-path`
list installed derivations: `nix-env -q`
rollback to previous generation: `nix-env --rollback`
select specific generation (3): `nix-env -G 3`
*/.nix-profile/bin is at the HEAD of $PATH. So, installing a package which is already installed on the host system will result in the nix package to be used instead of the default system installation. Try installing `man` and run `which man`. The current installation of `man` should point to `/home/user/.nix-profile/bin/man`.*
### Querying the store
show the direct runtime dependencies of hello: ```nix-store -q --references `which hello` ```
show reverse dependencies of hello: ```nix-store -q --referrers `which hello` ```
### Closures
The closures of a derivation is a list of all its dependencies, recursively, including absolutely everything necessary to use that derivation.
```nix-store -qR `which man` ```
```nix-store -q --tree `which man` ```
```nix-store -q --tree ~/.nix-profile```
### Recovery
remove everything: `nix-env -e '*'`
rollback manually: `/nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env --rollback`
To restore Nix, you can also use Nix to install a new version of Nix:
`/nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env `
### Channels
List Nix channels (to download package): `nix-channel --list`
Same as viewing content of file at `~/.nix-channels`.
update channel: `nix-channel --update`
upgrade all packages: `nix-env -u`
## Chapter 4: [The Basics of the Language](https://nixos.org/guides/nix-pills/basics-of-language.html)
### Value types
Launch nix-repl (CLI to test out the Nix language): `nix repl` (`:?` for help, `:q` to quit)
Structures:
- Identifiers (can be with dash, ex: `a-b` is a valid var name)
- Strings = "test" (use `${var}` to print value of var in str, you cannot use integers directly though: ~~`${5+5}`~~)
To print integers directly in string, we need to use the **builtins toString function**:
`"this is the number ${builtins.toString 5}"`
- Lists = [true 725 "test" (7*7)+1]
- Sets:
- = { firstname = "john"; lastname = "doe"; age = 25; }
- `a.firstname => "john"`
- `a."firstname" => "john"`
- Use recursive attr sets when elements must reference other elements:
```nix
s = { a = 12; b = 8 + a; } # invalid
s = rec { a = 12; b = 8 + a; } # valid
```
- If expressions (else branch always required):
```nix
nix-repl> a = 3
nix-repl> b = 4
nix-repl> if a > b then "yes" else "no"
"no"
```
- Let
- With (some kind of import function)
Nix is lazy, expressions not used are not evaluated:
```nix
nix-repl> let a = builtins.div 4 0; b = 6; in b
6 # no error even though we devided by 0
```
## Chapter 5: [Functions and Imports](https://nixos.org/guides/nix-pills/functions-and-imports.html)
### Functions
Nix docs on functions: [here](https://nixos.org/manual/nix/stable/expressions/language-constructs.html)
```nix
nix-repl> hello = s: "Hello ${s.firstname} ${s.lastname}!"
nix-repl> hello { firstname = "John"; lastname = "Doe"; }
"Hello John Doe!"
```
default values : `mul = { a, b ? 2 }: a*b`
variadic (optional attributes): `mul = { a, b, ... }: a*b`
access variadic: `mul = s@{ a, b, ... }: a*b*s.c` (you need to name the set => prefix with **@-pattern**)
### Imports
Simple: import atomic expressions
```nix
nix-repl> a = import ./a.nix
nix-repl> b = import ./b.nix
nix-repl> mul = import ./mul.nix
nix-repl> mul a b
12
```
Import more complex expressions (functions)
```nix
# file: 'test.nix'
{ a, b ? 3, trueMsg ? "yes", falseMsg ? "no" }:
if a > b
then builtins.trace trueMsg true
else builtins.trace falseMsg false
```
```nix
nix-repl> import ./test.nix { a = 5; trueMsg = "ok"; }
trace: ok
true
```
## Chapter 6: [Our First Derivation](https://nixos.org/guides/nix-pills/our-first-derivation.html)
### Derivation function
The derivation function receives a set as first argument. This set requires at least the following three attributes:
- **name:** the name of the derivation. In the nix store the format is hash-name, that's the name.
- **system:** is the name of the system in which the derivation can be built. For example, x86_64-linux.
- **builder:** it is the binary program that builds the derivation.
```nix
# example with fake system
d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
```
### What are .drv files?
Before continuing, some analogies with the C language:
- .nix files are like .c files
- .drv files are intermediate files like .o files. The .drv describes how to build a derivation, it's the bare minimum information.
- out paths are then the product of the build
Pretty print .drv file:
```nix
nix show-derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
```
```nix
{
"/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv": {
"outputs": {
"out": {
"path": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
}
},
"inputSrcs": [],
"inputDrvs": {},
"system": "mysystem",
"builder": "mybuilder",
"args": [],
"env": {
"builder": "mybuilder",
"name": "myname",
"out": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname",
"system": "mysystem"
}
}
}
```
**Important:** the hash of the out path is based solely on the input derivations in the current version of Nix, not on the contents of the build product. It's possible however to have content-addressable derivations for e.g. tarballs as we'll see later on.
summary of .drv format:
1. Output paths (there can be multiple ones). By default nix creates one out path called "out".
2. List of input derivations. It's empty because we are not referring to any other derivation. Otherwise, there would be a list of other .drv files.
3. System and the builder executable.
4. List of environment variables passed to the builder.
### Build derivation
- In `nix repl`:
```nix
:b <drv> # build derivation drv
```
- Outside `nix repl`:
```sh
nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
```
Man page information on `nix-store -r`
```sh
nix-store --help | grep -E "\--realise"
```
### Referring to other derivations
`ls /nix/store/*coreutils*/bin`
## Chapter 7: [Working Derivation](https://nixos.org/guides/nix-pills/working-derivation.html)
### Using a script as a builder
`declare -xp` lists exported variables (`declare` is a builtin bash function)
```bash
# builder.sh
declare -xp
echo foo > $out
```
```nix
nix-repl> d = derivation { name = "foo"; builder = "${bash}/bin/bash"; args = [ ./builder.sh ]; system = builtins.currentSystem; }
nix-repl> :b d
This derivation produced the following outputs:
out -> /nix/store/pd1v1m42bna50p8ap9caaf74v09mkyqk-foo
[1 built (2 failed), 0.0 MiB DL]
```
### Packaging a simple C program
```c=
// simple.c
void main() {
puts("Simple!");
}
```
```bash=
export PATH="$coreutils/bin:$gcc/bin"
mkdir $out
gcc -o $out/simple $src
```
```nix
nix-repl> :l <nixpkgs>
nix-repl> simple = derivation { name = "simple"; builder = "${bash}/bin/bash"; args = [ ./simple_builder.sh ]; gcc = gcc; coreutils = coreutils; src = ./simple.c; system = builtins.currentSystem; }
nix-repl> :b simple
This derivation produced the following outputs:
out -> /nix/store/9mzvf0aw37y2pq8fqrs2p2iyqlgx6bw0-simple
[2 built (3 failed), 0.0 MiB DL]
```
```bash
$ /nix/store/9mzvf0aw37y2pq8fqrs2p2iyqlgx6bw0-simple/simple
Simple!
```
### Outside of nix repl
```nix=
# simple.nix
# import <nixpkgs> and execute it on the empty set {}.
# "import <nixpkgs> {}" returns a set of derivations.
# these derivations are brought into scope thanks to the "with" keyword.
# in the end, the line is equivalent to typing ":l <nixpkgs>" in nix repl.
with (import <nixpkgs> {});
derivation {
name = "simple";
builder = "${bash}/bin/bash";
args = [ ./simple_builder.sh ];
# inherit copies variables from into the scope.
# "inherit x" is equivalent to "x = x".
inherit gcc coreutils;
src = ./simple.c;
system = builtins.currentSystem;
}
```
Build derivation with `nix-build simple.nix`. What it does behind the hood:
- `nix-instantiate`: parse and evaluate simple.nix and return the .drv file corresponding to the parsed derivation set
- `nix-store -r`: realise the .drv file, which actually builds it.
Finally, it creates the symlink.
## Chapter 8: [Generic Builders](https://nixos.org/guides/nix-pills/generic-builders.html)
### A generic builder
```bash=
# builder.sh
# set to exit the build on any error
set -e
# reset PATH
unset PATH
# fill the PATH variable using $buildInputs
for p in $buildInputs; do
export PATH=$p/bin${PATH:+:}$PATH
done
# unpack the source
tar -xf $src
# find directory where source has been unpacked and cd into it
for d in *; do
if [ -d "$d" ]; then
cd "$d"
break
fi
done
./configure --prefix=$out
# compile and install
make
make install
```
```nix=
with (import <nixpkgs> {});
derivation {
name = "hello";
builder = "${bash}/bin/bash";
args = [ ./builder.sh ];
buildInputs = [ gnutar gzip gnumake gcc binutils-unwrapped coreutils gawk gnused gnugrep ];
src = ./hello-2.10.tar.gz;
system = builtins.currentSystem;
}
```
### A more convenient derivation function
```nix=
# autotools.nix
# define a function which accepts a parameter "pkgs" and returns a function which accepts a parameter "attrs"
pkgs: attrs:
# bring pkgs into the scope
with pkgs;
# define helper variable "defaultAttrs" which serves as a set of common attributes used in derivations
let defaultAttrs = {
builder = "${bash}/bin/bash";
args = [ ./builder.sh ];
baseInputs = [ gnutar gzip gnumake gcc binutils-unwrapped coreutils gawk gnused gnugrep ];
buildInputs = [];
system = builtins.currentSystem;
};
in
# create the derivation
derivation (defaultAttrs // attrs)
```
**NB:** The [// operator](https://nixos.org/manual/nix/stable/expressions/language-operators.html) is an operator between two sets. The result is the union of the two sets. In case of conflicts between attribute names, the value on the right set is preferred.
Modified **builder.sh** that takes `$baseInputs` into account:
```bash=
# builder.sh
# set to exit the build on any error
set -e
# reset PATH
unset PATH
# fill the PATH variable using $baseInputs
for p in $baseInputs; do
export PATH=$p/bin${PATH:+:}$PATH
done
# fill the PATH variable using $buildInputs
for p in $buildInputs; do
export PATH=$p/bin${PATH:+:}$PATH
done
# unpack the source
tar -xf $src
# find directory where source has been unpacked and cd into it
for d in *; do
if [ -d "$d" ]; then
cd "$d"
break
fi
done
./configure --prefix=$out
# compile and install
make
make install
```
Rewrite `hello.nix`:
```nix=
# hello.nix
let
pkgs = import <nixpkgs> {};
# import custom function from "autotools.nix"
mkDerivation = import ./autotools.nix pkgs;
# execute function mkDerivation and provide "hello set" as attrs
in mkDerivation {
name = "hello";
src = ./hello-2.10.tar.gz;
}
```
**NB:** Note that `buildInputs` remains empty because we do not need other dependencies to be in PATH. If we needed other dependencies, we could specify them by adding `buildInputs` in `mkDerivation {...}`. A simple example below:
```nix=
# hello.nix
let
pkgs = import <nixpkgs> {};
# import custom function from "autotools.nix"
mkDerivation = import ./autotools.nix pkgs;
# execute function mkDerivation and provide "hello set" as attrs
in mkDerivation {
name = "hello";
src = ./hello-2.10.tar.gz;
# we indicate that we want to use additional dependencies "foo" and "bar"
buildInputs = [ foo bar ]
}
```
## Chapter 9: [Automatic Runtime Dependencies](https://nixos.org/guides/nix-pills/automatic-runtime-dependencies.html)
### Build dependencies
```bash
$ nix-instantiate hello.nix
/nix/store/z77vn965a59irqnrrjvbspiyl2rph0jp-hello.drv
$ nix-store -q --references /nix/store/z77vn965a59irqnrrjvbspiyl2rph0jp-hello.drv
/nix/store/0q6pfasdma4as22kyaknk4kwx4h58480-hello-2.10.tar.gz
/nix/store/1zcs1y4n27lqs0gw4v038i303pb89rw6-coreutils-8.21.drv
/nix/store/2h4b30hlfw4fhqx10wwi71mpim4wr877-gnused-4.2.2.drv
/nix/store/39bgdjissw9gyi4y5j9wanf4dbjpbl07-gnutar-1.27.1.drv
/nix/store/7qa70nay0if4x291rsjr7h9lfl6pl7b1-builder.sh
/nix/store/g6a0shr58qvx2vi6815acgp9lnfh9yy8-gnugrep-2.14.drv
/nix/store/jdggv3q1sb15140qdx0apvyrps41m4lr-bash-4.2-p45.drv
/nix/store/pglhiyp1zdbmax4cglkpz98nspfgbnwr-gnumake-3.82.drv
/nix/store/q9l257jn9lndbi3r9ksnvf4dr8cwxzk7-gawk-4.1.0.drv
/nix/store/rgyrqxz1ilv90r01zxl0sq5nq0cq7v3v-binutils-2.23.1.drv
/nix/store/qzxhby795niy6wlagfpbja27dgsz43xk-gcc-wrapper-4.8.3.drv
/nix/store/sk590g7fv53m3zp0ycnxsc41snc2kdhp-gzip-1.6.drv
```
### Runtime dependencies
Steps:
- Dump the derivation as NAR, a serialization of the derivation output. Works fine whether it's a single file or a directory.
- For each build dependency .drv and its relative out path, search the contents of the NAR for this out path.
- If found, then it's a runtime dependency.
```bash
$ nix-instantiate hello.nix
/nix/store/z77vn965a59irqnrrjvbspiyl2rph0jp-hello.drv
$ nix-store -r /nix/store/z77vn965a59irqnrrjvbspiyl2rph0jp-hello.drv
/nix/store/a42k52zwv6idmf50r9lps1nzwq9khvpf-hello
$ nix-store -q --references /nix/store/a42k52zwv6idmf50r9lps1nzwq9khvpf-hello
/nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19
/nix/store/8jm0wksask7cpf85miyakihyfch1y21q-gcc-4.8.3
/nix/store/a42k52zwv6idmf50r9lps1nzwq9khvpf-hello
```
The build process adds that gcc lib path thinking it may be useful at runtime, but really it's not. How do we get rid of it? Nix authors have written another magical tool called [patchelf](https://nixos.org/patchelf.html), which is able to reduce the [rpath](https://en.wikipedia.org/wiki/Rpath) to the paths that are really used by the binary.
### Another phase in the builder
Add the following at the end of `builder.sh` (after `make install`)
```bash
# fixup: for each file in $out we run patchelf --shrink-rpath and strip
# this well help remove any unnecessary runtime dependency
find $out -type f -exec patchelf --shrink-rpath '{}' \; -exec strip '{}' \; 2>/dev/null
```
However, since we are using two new commands: **find** and **patchelf**, we must add them to the `baseInputs` of `autotools.nix`:
```nix
# define a function which accepts a parameter "pkgs" and returns a function which accepts a parameter "attrs"
pkgs: attrs:
# bring pkgs into the scope
with pkgs;
# define helper variable "defaultAttrs" which serves as a set of common attributes used in derivations
let defaultAttrs = {
builder = "${bash}/bin/bash";
args = [ ./builder.sh ];
# add findutils and patchelf for the fixup phase in builder.sh
baseInputs = [ gnutar gzip gnumake gcc binutils-unwrapped coreutils gawk gnused gnugrep findutils patchelf ];
buildInputs = [];
system = builtins.currentSystem;
};
in
# create the derivation
derivation (defaultAttrs // attrs)
```
Thanks to the newly added **fixup** phase in our builder, we only have two runtime dependencies:
```bash
$ nix-build hello.nix
[...]
$ nix-store -q --references result
/nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19
/nix/store/md4a3zv0ipqzsybhjb8ndjhhga1dj88x-hello
```
The hello binary will use that exact version of glibc library and interpreter, not the system one:
```bash
$ ldd result/bin/hello
linux-vdso.so.1 (0x00007fff11294000)
libc.so.6 => /nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/libc.so.6 (0x00007f7ab7362000)
/nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/ld-linux-x86-64.so.2 (0x00007f7ab770f000)
```
It's all self-contained! That's what's cool with Nix :)
## Chapter 10: [Developing with nix-shell](https://nixos.org/guides/nix-pills/developing-with-nix-shell.html)
### What's nix-shell
```bash
$ nix-shell hello.nix
[nix-shell]$ make
make: *** No targets specified and no makefile found. Stop.
[nix-shell]$ which make
/usr/bin/make # looks like nix-shell inherited $PATH
[nix-shell]$ echo $baseInputs
/nix/store/jff4a6zqi0yrladx3kwy4v6844s3swpc-gnutar-1.27.1 [...]
```
The following did not work on my machine because my user is missing permission to write to the Nix store:
```bash
[nix-shell]$ source builder.sh
...
/nix/store/vizjhz04x6xl57x2vrpqa52j8q6rkjfh-coreutils-9.0/bin/install: cannot remove '/nix/store/2d3a7glhfr1fk6xzkaynp2p9vq0r6pzn-hello/bin/hello': Permission denied
...
```
### A builder for nix-shell
```nix=
# autotools.nix
# define a function which accepts a parameter "pkgs" and returns a function which accepts a parameter "attrs"
pkgs: attrs:
# bring pkgs into the scope
with pkgs;
# define helper variable "defaultAttrs" which serves as a set of common attributes used in derivations
let defaultAttrs = {
builder = "${bash}/bin/bash";
args = [ ./builder.sh ];
setup = ./setup.sh;
baseInputs = [ gnutar gzip gnumake gcc binutils-unwrapped coreutils gawk gnused gnugrep patchelf findutils ];
buildInputs = [];
system = builtins.currentSystem;
};
in
# create the derivation
derivation (defaultAttrs // attrs)
```
```bash=
# builder.sh
# set to exit the build on any error
set -e
# source setup and call the genericBuild function
source $setup
genericBuild
```
```bash=
# fixup.sh
# reset PATH
unset PATH
# fill the PATH variable using $buildInputs
for p in $baseInputs $buildInputs; do
export PATH=$p/bin${PATH:+:}$PATH
done
function unpackPhase() {
# unpack the source
tar -xzf $src
# find directory where source has been unpacked and cd into it
for d in *; do
if [ -d "$d" ]; then
cd "$d"
break
fi
done
}
function configurePhase() {
./configure --prefix=$out
}
function buildPhase() {
make
}
function installPhase() {
make install
}
# fixup: for each file in $out we run patchelf --shrink-rpath and strip
# this well help remove any unnecessary runtime dependency
function fixupPhase() {
find $out -type f -exec patchelf --shrink-rpath '{}' \; -exec strip '{}' \; 2>/dev/null
}
function genericBuild() {
unpackPhase
configurePhase
buildPhase
installPhase
fixupPhase
}
```
```nix=
# hello.nix
let
pkgs = import <nixpkgs> {};
mkDerivation = import ./autotools.nix pkgs;
in mkDerivation {
name = "hello";
src = ./hello-2.10.tar.gz;
}
```
## Chapter 11: [Garbage Collector](https://nixos.org/guides/nix-pills/garbage-collector.html)
### How does it work
Programming languages with a garbage collector have an important concept in order to keep track of live objects: GC roots. A GC root is an object that is always alive (unless explicitly removed as GC root). All objects recursively referred to by a GC root are live.
Therefore, the garbage collection process starts from GC roots, and recursively mark referenced objects as live. All other objects can be collected and deleted.
In Nix there's this same concept. Instead of being objects, of course, [GC roots are store paths](https://nixos.org/manual/nix/stable/package-management/garbage-collector-roots.html). The implementation is very simple and transparent to the user. GC roots are stored under **/nix/var/nix/gcroots**. If there's a symlink to a store path, then that store path is a GC root.
Nix allows this directory to have subdirectories: it will simply recurse directories in search of symlinks to store paths.
So we have a list of GC roots. At this point, deleting dead store paths is as easy as you can imagine. We have the list of all live store paths, hence the rest of the store paths are dead.
In particular, Nix first moves dead store paths to **/nix/store/trash** which is an atomic operation. Afterwards, the trash is emptied.
### Playing with the GC
```bash
$ nix-collect-garbage
...
note: currently hard linking saves -0.00 MiB
2704 store paths deleted, 670.44 MiB freed
```
🥳 **670.44 MiB freed!!** 🥳
Installing bsd-games:
```bash
$ nix-env -iA nixpkgs.bsdgames
$ readlink -f `which fortune`
/nix/store/b3lxx3d3ggxcggvjw5n0m1ya1gcrmbyn-bsd-games-2.17/bin/fortune
$ nix-store -q --roots `which fortune`
/nix/var/nix/profiles/default-9-link
$ nix-env --list-generations
[...]
9 2014-08-20 12:44:14 (current)
```
### Remove everything
```bash
$ nix-channel --update
$ nix-env -u --always
$ rm /nix/var/nix/gcroots/auto/*
$ nix-collect-garbage -d
...
deleting unused links...
note: currently hard linking saves -0.00 MiB
2430 store paths deleted, 612.93 MiB freed
```
🥳 **612.93 MiB freed!!** 🥳
## Chapter 12: [Inputs Design Pattern](https://nixos.org/guides/nix-pills/inputs-design-pattern.html)
### The single repository pattern
Nix Packages repository: [nixpkgs](https://github.com/NixOS/nixpkgs)
The repository is not heavy because the packages are only built when needed.
Modifying `setup.sh` to include **pkg-config**:
```sh
for p in $baseInputs $buildInputs; do
if [ -d $p/bin ]; then
export PATH="$p/bin${PATH:+:}$PATH"
fi
if [ -d $p/lib/pkgconfig ]; then
export PKG_CONFIG_PATH="$p/lib/pkgconfig${PKG_CONFIG_PATH:+:}$PKG_CONFIG_PATH"
fi
done
```
Pass **gd** to the **buildInputs**:
```nix
# graphviz.nix
let
pkgs = import <nixpkgs> {};
mkDerivation = import ./autotools.nix pkgs;
in mkDerivation {
name = "graphviz";
src = ./graphviz-2.49.3.tar.gz;
buildInputs = with pkgs; [
pkg-config
(pkgs.lib.getLib gd)
(pkgs.lib.getDev gd)
];
}
```
The **[-A]** argument is used to access an attribute of the set from the given .nix expression.
The **[-f]** option is used to specify the expression to use, in this case the current directory, therefore `./default.nix`.
The **[-i]** stands for installation.
The **[-A]** is the same as above for nix-build.
### The inputs pattern
The **"inputs"** pattern allows our expressions to be easily customizable through a set of arguments. These arguments could be flags, derivations, or whatever else. Our package expressions are functions, don't think there's any magic in there.
It also makes the expressions independent of the repository. Given that all the needed information is passed through arguments, it is possible to use that expression in any other context.
## Chapter 13: [Callpackage Design Pattern](https://nixos.org/guides/nix-pills/callpackage-design-pattern.html)
### The callPackage function
The function takes two arguments:
- An argument set with values
- A function
It then intersects the argument set with the function and return the result. It is defined as follow: `nix-repl> callPackage = set: f: f (builtins.intersectAttrs (builtins.functionArgs f) set)`
Simple example:
```nix
nix-repl> values = { a = 3; b = 5; c = 10; }
nix-repl> callPackage = set: f: f (builtins.intersectAttrs (builtins.functionArgs f) set)
nix-repl> callPackage values add
8
```
Modify **callPackage** to allow overrides:
```nix
nix-repl> callPackage = set: f: overrides: f ((builtins.intersectAttrs (builtins.functionArgs f) set) // overrides)
nix-repl> callPackage values add { }
8
nix-repl> callPackage values add { b = 12; }
15
```
### Final version of `default.nix`
```nix
let
nixpkgs = import <nixpkgs> {};
allPkgs = nixpkgs // pkgs;
# define the callPackage function
callPackage = path: overrides:
let f = import path; # import the file beforehand
in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
pkgs = with nixpkgs; {
mkDerivation = import ./autotools.nix nixpkgs;
hello = callPackage ./hello.nix { };
graphviz = callPackage ./graphviz.nix { };
graphvizCore = callPackage ./graphviz.nix { gdSupport = false; };
};
in pkgs
```
## Chapter 14: [Override Design Pattern](https://nixos.org/guides/nix-pills/override-design-pattern.html)
**The idea:** Defining overrides without modifying the original package.
### The override implementation
```nix
# lib.nix
{
makeOverridable = f: origArgs:
let
origRes = f origArgs;
in
origRes // { override = newArgs: f (origArgs // newArgs); };
}
```
```nix
$ nix repl
nix-repl> :l lib.nix
Added 1 variables.
nix-repl> f = { a, b }: { result = a+b; }
nix-repl> f { a = 3; b = 5; }
{ result = 8; }
nix-repl> res = makeOverridable f { a = 3; b = 5; }
nix-repl> res
{ override = «lambda»; result = 8; }
nix-repl> res.override { a = 10; }
{ result = 15; }
```
The problem is that once we call the override of a set, we lose the ability to override (the last set does have a `result` but is missing an `override`). A solution is to call `makeOverridable` recursively:
```nix
rec {
makeOverridable = f: origArgs:
let
origRes = f origArgs;
in
origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
}
```
Now we can successfully use `override` recursively:
```nix
nix-repl> res = makeOverridable f { a = 3; b = 5; }
nix-repl> res2 = res.override { a = 10; }
```
## Chapter 15: [Nix Search Paths](https://nixos.org/guides/nix-pills/nix-search-paths.html)
```bash
$ nix-instantiate --eval -E '<ping>'
error: file `ping` was not found in the Nix search path (add it using $NIX_PATH or -I)
$ NIX_PATH=$PATH nix-instantiate --eval -E '<ping>'
/bin/ping
$ nix-instantiate -I /bin --eval -E '<ping>'
/bin/ping
```
Note that the [-I] option accepts a single directory. Paths added with [-I] take precedence over NIX_PATH.
```nix
$ nix-instantiate --eval -E '<nixpkgs>'
/home/coko/.nix-defexpr/channels/nixpkgs
$ echo $NIX_PATH
```
It seems **NIX_PATH** is not defined. I checked in **~/.nix-profile/etc/profile.d/nix.sh** and it turns out that there is no mention of **NIX_PATH** in here. Turns out it is normal on non-NixOS. Check out [issue #149791](https://github.com/NixOS/nixpkgs/issues/149791) on GitHub or [this post](https://discourse.nixos.org/t/where-is-nix-path-supposed-to-be-set/16434/6).
First copy `default.nix` and `graphviz.nix` in **/home/coko/mypkgs**. Then do the following:
```nix
$ export NIX_PATH=mypkgs=/home/coko/mypkgs:/home/coko/.nix-defexpr/channels/nixpkgs
$ nix-instantiate --eval '<mypkgs>'
{ graphviz = <code>; graphvizCore = <code>; hello = <code>; mkDerivation = <code>; }
```
Contrary to **nix-instantiate** and **nix-build**, **nix-env** does not rely on `NIX_PATH` to find the `nixpkgs` repository. Instead, it uses `~/.nix-defexpr`. To specify an alternative to `~/.nix-defexpr`, we can use the [-f] option: `nix-env -f '<mypkgs>' -i graphviz`.
`nix-env -f '<mypkgs>' -qaP`
`nix-env -f '<mypkgs>' -i -A graphviz`: [-A] option when you want to specify an attribute name instead of a derivation.
## Chapter 16: [Nixpkgs Parameters](https://nixos.org/guides/nix-pills/nixpkgs-parameters.html)
### General structure of nixpkgs
- `default.nix`: File loaded when referring to `<nixpkgs>`. Checks whether the `nix` version is at least **2.2** (see below) and if it is, it will import `./pkgs/top-level/impure.nix`.
```nix
# ~/.nix-defexpr/channels/nixpkgs/default.nix
let requiredVersion = import ./lib/minver.nix; in
if ! builtins ? nixVersion || builtins.compareVersions requiredVersion builtins.nixVersion == 1 then
abort ''
This version of Nixpkgs requires Nix >= ${requiredVersion}, please upgrade:
- If you are running NixOS, `nixos-rebuild' can be used to upgrade your system.
- Alternatively, with Nix > 2.0 `nix upgrade-nix' can be used to imperatively
upgrade Nix. You may use `nix-env --version' to check which version you have.
- If you installed Nix using the install script (https://nixos.org/nix/install),
it is safe to upgrade by running it again:
curl -L https://nixos.org/nix/install | sh
For more information, please see the NixOS release notes at
https://nixos.org/nixos/manual or locally at
${toString ./nixos/doc/manual/release-notes}.
If you need further help, see https://nixos.org/nixos/support.html
''
else
import ./pkgs/top-level/impure.nix
```
If we take at look at `~/.nix-defexpr/channels/nixpkgs/lib/minver.nix`:
```bash
$ cat ~/.nix-defexpr/channels/nixpkgs/lib/minver.nix
# Expose the minimum required version for evaluating Nixpkgs
"2.2"
```
- `all-packages.nix` (located at `~/.nix-defexpr/channels/nixpkgs/pkgs/top-level/all-packages.nix`): File that composes all the packages. It contains a function that accepts multiple parameters such as:
- **system**: Defaults to the current system
- **config**: Defaults to null
- others...
The **system** parameter, as per comment in the expression, it's the system for which the packages will be built. It allows for example to install i686 packages on amd64 machines.
The **config** parameter is a simple attribute set. Packages can read some of its values and change the behavior of some derivations.
### The system parameter
```nix
# myrelease.nix
{ system ? builtins.currentSystem }:
let pkgs = import <nixpkgs> { inherit system; };
...
```
This parameter allows to easily select a set of packages for a particular system. For example:
```bash
nix-build -A psmisc --argstr system i686-linux
```
This will build the psmisc derivation for i686-linux instead of x86_64-linux.
### The config parameter
The `all-packages.nix` expression accepts the `config` parameter. If it's `null`, then it reads the `NIXPKGS_CONFIG` environment variable. If not specified, `nixpkgs` will peek `$HOME/.nixpkgs/config.nix`.
```nix
nix-repl> pkgs = import <nixpkgs> {}
nix-repl> pkgs.config
{ doCheckByDefault = false; warnings = [ ... ]; }
nix-repl> pkgs = import <nixpkgs> { config = { foo = "bar"; }; }
nix-repl> pkgs.config
{ doCheckByDefault = false; foo = "bar"; warnings = [ ... ]; }
```
## Chapter 17: [Nixpkgs Overriding Packages](https://nixos.org/guides/nix-pills/nixpkgs-overriding-packages.html)
Defining a recursive function (`let ... in ...`):
```nix
nix-repl> let factorial = n: if n == 0 then 1 else n * factorial (n - 1); in factorial 5
120
```
### The problem
We want to build an override of the package **graphviz**. But how do we tell a package **P** that depends on **graphviz** to use the override version?
### In an imperative world...
```nix
pkgs = import <nixpkgs> {};
pkgs.graphviz = pkgs.graphviz.override { xorg = null; };
build(pkgs.P)
```
Cool but it won't be that easy in our case. Nix is a functional language and you can assign to variables only once.
### [Fixed point](https://en.wikipedia.org/wiki/Fixed_point_%28mathematics%29)
```nix
# Take a function and evaluate it with its own returned value.
fix = f: let result = f result; in result;
```
Thanks to lazy evaluation, we are not trapped in an infinite loop.
```nix
nix-repl> fix = f: let result = f result; in result
nix-repl> pkgs = self: { a = 3; b = 4; c = self.a+self.b; }
nix-repl> fix pkgs
{ a = 3; b = 4; c = 7; }
```
Without the rec keyword, we were able to refer to`a` and `b` of the same set.
- First `pkgs` gets called with an unevaluated thunk `(pkgs(pkgs(...)`
- To set the value of `c` then `self.a` and `self.b` are evaluated.
- The `pkgs` function gets called again to get the value of `a` and `b`.
The trick is that `c` is not needed to be evaluated in the inner call, thus it doesn't go in an infinite loop.
This helped me to understand dynamic binding better: http://r6.ca/blog/20140422T142911Z.html
We can almost think of `rec { bindings }` as syntactic sugar for `fix (self: with self; { bindings })`.
### Overriding a set with fixed point
```nix
nix-repl> overrides = { a = 1; b = 2; }
nix-repl> let newpkgs = pkgs (newpkgs // overrides); in newpkgs
{ a = 3; b = 4; c = 3; }
nix-repl> let newpkgs = pkgs (newpkgs // overrides); in newpkgs // overrides
{ a = 1; b = 2; c = 3; }
```
In the first case we computed pkgs with the overrides, in the second case we also included the overriden attributes in the result.
### Overriding nixpkgs packages
```nix
config.nix
{
packageOverrides = pkgs: {
graphviz = pkgs.graphviz.override { xorg = null; };
};
}
```
```nix
nix-repl> pkgs = import <nixpkgs> { config = import ./config.nix; }
nix-repl> :b pkgs.asciidocFull
```
## Chapter 18: [Nix Store Paths](https://nixos.org/guides/nix-pills/nix-store-paths.html)
### Source paths
```bash
echo mycontent > myfile
$ nix repl
nix-repl> derivation { system = "x86_64-linux"; builder = ./myfile; name = "foo"; }
«derivation /nix/store/y4h73bmrc9ii5bxg6i7ck6hsf5gqv8ck-foo.drv»
```
#### Step 1. Compute the hash of the file
```bash
$ nix-hash --type sha256 myfile
2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3
# or
$ nix-store --dump myfile|sha256sum
2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3
```
#### Step 2. Build the string description
Then nix uses a special string which includes the hash, the path type and the file name. We store this in another file:
```bash
$ echo -n "source:sha256:2bfef67de873c54551d884fdab3055d84d573e654efa79db3c0d7b98883f9ee3:/nix/store:myfile" > myfile.str
```
#### Step 3. Compute the final hash
Finally the comments tell us to compute the base-32 representation of the first 160 bits (truncation) of a sha256 of the above string:
```bash
$ nix-hash --type sha256 --truncate --base32 --flat myfile.str
xv2iccirbrvklck36f1g7vldn5v58vck
```
### Output paths
At the time nix computes the out path, the .drv contains an empty string for each out path. So what we do is getting our .drv and replacing the out path with an empty string:
```bash
$ cp -f /nix/store/y4h73bmrc9ii5bxg6i7ck6hsf5gqv8ck-foo.drv myout.drv
$ cat myout.drv
Derive([("out","/nix/store/hs0yi5n5nw6micqhy8l1igkbhqdkzqa1-foo","","")],[],["/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"],"x86_64-linux","/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile",[],[("builder","/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"),("name","foo"),("out","/nix/store/hs0yi5n5nw6micqhy8l1igkbhqdkzqa1-foo"),("system","x86_64-linux")])
$ sed -i 's,/nix/store/hs0yi5n5nw6micqhy8l1igkbhqdkzqa1-foo,,g' myout.drv
$ cat myout.drv
Derive([("out","","","")],[],["/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"],"x86_64-linux","/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile",[],[("builder","/nix/store/xv2iccirbrvklck36f1g7vldn5v58vck-myfile"),("name","foo"),("out",""),("system","x86_64-linux")])
```
The `myout.drv` is the .drv state in which nix is when computing the out path for our derivation:
```bash
$ sha256sum myout.drv
1bdc41b9649a0d59f270a92d69ce6b5af0bc82b46cb9d9441ebc6620665f40b5 myout.drv
$ echo -n "output:out:sha256:1bdc41b9649a0d59f270a92d69ce6b5af0bc82b46cb9d9441ebc6620665f40b5:/nix/store:foo" > myout.str
$ nix-hash --type sha256 --truncate --base32 --flat myout.str
hs0yi5n5nw6micqhy8l1igkbhqdkzqa1
```
### Fixed-output paths
```bash
$ echo mycontent > myfile
$ sha256sum myfile
f3f3c4763037e059b4d834eaf68595bbc02ba19f6d2a500dce06d124e2cd99bb myfile
nix-repl> derivation { name = "bar"; system = "x86_64-linux"; builder = "none"; outputHashMode = "flat"; outputHashAlgo = "sha256"; outputHash = "f3f3c4763037e059b4d834eaf68595bbc02ba19f6d2a500dce06d124e2cd99bb"; }
«derivation /nix/store/ymsf5zcqr9wlkkqdjwhqllgwa97rff5i-bar.drv»
```
```bash
$ nix show-derivation /nix/store/ymsf5zcqr9wlkkqdjwhqllgwa97rff5i-bar.drv
{
"/nix/store/ymsf5zcqr9wlkkqdjwhqllgwa97rff5i-bar.drv": {
"outputs": {
"out": {
"path": "/nix/store/a00d5f71k0vp5a6klkls0mvr1f7sx6ch-bar",
"hashAlgo": "sha256",
"hash": "f3f3c4763037e059b4d834eaf68595bbc02ba19f6d2a500dce06d124e2cd99bb"
}
},
[...]
}
```
Because we specified the hashing algorithm and provided a hash, they appear in the `.drv` file.
### Conclusion
Nix first hashes the contents, then creates a string description, and the final store path is the hash of this string.
The hash of a derivation only depends on its input. Fixed-output derivations are especially used by the nixpkgs repository for downloading and verifying source tarballs.
## Chapter 19: [Fundamentals of Stdenv](https://nixos.org/guides/nix-pills/fundamentals-of-stdenv.html)
Only has two folders: `nix-support/` and `setup/`. The latter contains hardcoded dependecies of all the toolchain and basic tools.
`stdenv setup` is similar to the `builder.sh` we did in a previous pill, although this one is much more complete and contains over a thousand lines of code. `stdenv setup` works in phases:
- unpackPhase
- patchPhase
- configurePhase
- buildPhase
- checkPhase
- installPhase
- fixupPhase
- installCheckPhase
- distPhase
All these phases can be found in the `genericBuild` function in `setup`.
How to use `stdenv setup`:
```bash
$ nix-shell -E 'derivation { name = "fake"; builder = "fake"; system = "x86_64-linux"; }'
nix-shell$ unset PATH
nix-shell$ source /nix/store/k4jklkcag4zq4xkqhkpy156mgfm34ipn-stdenv/setup
nix-shell$ tar -xf hello-2.10.tar.gz
nix-shell$ cd hello-2.10
nix-shell$ configurePhase
...
nix-shell$ buildPhase
...
```
To be more pratical, the packages in `<nixpkgs>` use a wrapper called `stdenv.mkDerivation` instead of the raw derivation.
Let us remake `hello.nix` using `stdenv`:
```nix
with import <nixpkgs> {};
stdenv.mkDerivation {
name = "hello";
src = ./hello-2.10.tar.gz;
}
```
It builds, and runs fine:
```bash
$ nix-build hello.nix
...
/nix/store/6y0mzdarm5qxfafvn2zm9nr01d1j0a72-hello
$ result/bin/hello
Hello, world!
```
The overall process is simple:
- `nix-build`
- `bash -e default-builder.sh`
- `source $stdenv/setup`
- `genericBuild`
## Chapter 20: [Basic Dependencies and Hooks](https://nixos.org/guides/nix-pills/basic-dependencies-and-hooks.html)
### The buildInputs Attribute
The **buildInputs** covers direct dependencies.
```nix
# hello.nix (again)
let
nixpkgs = import <nixpkgs> {};
inherit (nixpkgs) stdenv fetchurl which;
actualHello = stdenv.mkDerivation {
name = "hello-2.3";
src = fetchurl {
url = mirror://gnu/hello/hello-2.3.tar.bz2;
sha256 = "0c7vijq8y68bpr7g6dh1gny0bff8qq81vnp4ch8pjzvg56wb3js1";
};
};
wrappedHello = stdenv.mkDerivation {
name = "hello-wrapper";
buildInputs = [ actualHello which ];
unpackPhase = "true";
installPhase = ''
mkdir -p "$out/bin"
echo "#! ${stdenv.shell}" >> "$out/bin/hello"
echo "exec $(which hello)" >> "$out/bin/hello"
'';
};
in wrappedHello
```
Notice that the `wrappedHello` derivation finds the `hello` binary from the `PATH`. This works because `stdenv` goes through each `buildInput` and call the function `findInputs`. It also relies on a function `addToEnv` to add them to the `PATH`.
### The propagatedBuildInputs Attribute
The **propagatedBuildInputs** covers indirect dependencies.
```nix
let
nixpkgs = import <nixpkgs> {};
inherit (nixpkgs) stdenv fetchurl which;
actualHello = stdenv.mkDerivation {
name = "hello-2.3";
src = fetchurl {
url = mirror://gnu/hello/hello-2.3.tar.bz2;
sha256 = "0c7vijq8y68bpr7g6dh1gny0bff8qq81vnp4ch8pjzvg56wb3js1";
};
};
intermediary = stdenv.mkDerivation {
name = "middle-man";
propagatedBuildInputs = [ actualHello ];
unpackPhase = "true";
installPhase = ''
mkdir -p "$out"
'';
};
wrappedHello = stdenv.mkDerivation {
name = "hello-wrapper";
buildInputs = [ intermediary which ];
unpackPhase = "true";
installPhase = ''
mkdir -p "$out/bin"
echo "#! ${stdenv.shell}" >> "$out/bin/hello"
echo "exec $(which hello)" >> "$out/bin/hello"
'';
};
in wrappedHello
```
Only the `intermediary` package needs `propagatedBuildInputs`. How does it work? Look at the **fixupPhase**:
```bash
fixupPhase() {
## Elided
if test -n "$propagatedBuildInputs"; then
mkdir -p "$out/nix-support"
echo "$propagatedBuildInputs" > "$out/nix-support/propagated-build-inputs"
fi
## Elided
}
```
As a result, all propagated build inputs reside in `$out/nix-support/`. There is actually more to the **findInputs** function:
```bash
findInputs() {
local pkg=$1
## More goes here in reality that we can ignore for now.
if test -f $pkg/nix-support/propagated-build-inputs; then
for i in $(cat $pkg/nix-support/propagated-build-inputs); do
findInputs $i
done
fi
}
```
Recursively, it looks at the propagated build inputs of each dependency, as well as those dependencies' propagated build inputs and so on.
### Setup Hooks
The **Setup hooks** are bash callbacks used to handle arbitrary interactions.
If a package includes the path `pkg/nix-support/setup-hook`, it will be sourced by any stdenv-based build including that as a dependency.
This is strictly more general than any of the other mechanisms introduced in this chapter. For example, try writing a setup hook that has the same effect as a *propagatedBuildInputs* entry. One can almost think of this as an escape hatch around Nix's normal isolation guarantees, and the principle that dependencies are immutable and inert. We're not actually doing something unsafe or modifying dependencies, but we are allowing arbitrary ad-hoc behavior. For this reason, setup-hooks should only be used as a last resort.