A note on how to build a program using Nix.
An overview of the basic build flow.
First, create a default.nix
file in your project directory.
> mkdir myproject && cd myproject
myproject> touch default.nix
Next, write stdenv.mkDerivation
in default.nix.
mkDerivation
is a function used when creating a package on Nix.
default.nix
with import <nixpkgs> {};
stdenv.mkDerivation { ... }
After writing default.nix
, run the nix-build
command to build the package.
myproject> nix-build
After the build is finished, you will have a folder called result
in your project directory.
The built binary etc. will be included in this.
Build the following C source code.
hello.c
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello, world\n");
}
default.nix
is written like this:
default.nix
with import <nixpkgs> {};
stdenv.mkDerivation {
name = "hello";
src = ./hello.c;
buildCommand = ''
gcc -Wall -O2 -o hello $src
install -m555 -Dt $out/bin hello
'';
}
At this point, the structure of the project looks like this
myproject> tree
.
├── default.nix
└── hello.c
Let's go
myproject> nix-build
these derivations will be built:
/nix/store/l46b92csn5d9ics7zfs9yb51gb5n6y4r-hello.drv
building '/nix/store/l46b92csn5d9ics7zfs9yb51gb5n6y4r-hello.drv'...
/nix/store/0a3f6ddksy17jmfzasj2np999cgkrs8a-hello
After building, you will have a result
directory.
myproject> tree -l
.
├── default.nix
├── hello.c
└── result -> /nix/store/0a3f6ddksy17jmfzasj2np999cgkrs8a-hello
└── bin
└── hello
In fact, result
is a symbolic to nix / store / 0a3f ...-hello
.
Let's run the binary.
myproject> result/bin/hello
Hello, world
It worked fine.
default.nix
All you're doing with default.nix
is passing the necessary arguments to stdenv.mkDerivation
.
The argument of mkDerivation
is
name
src
buildCommand
Has been passed.
name
is the name of the package and src
is the source code to build.
In buildCommand
, write the shell script required for the build.
In buildCommand
, you can refer to the path passed to the argument key src
as $ src
.
Also, $ out
contains the path after the build (/ nix / store / 0a3f ...-hello
).
default.nix
To see the other features of mkDerivation
, let's rewrite default.nix
.
First, use pname
and version
instead of name
.
This will automatically name the package in the form <pname>-<version>
.
deafult.nix
with import <nixpkgs> {};
stdenv.mkDerivation {
pname = "hello";
version = "0.1.0";
src = ./hello.c;
buildCommand = ...;
}
Then stop writing buildCommand
directly to default.nix
and write the build script in a separate file.
builder.sh
source $stdenv/setup
gcc -Wall -O2 -o hello $src
install -m555 -Dt $out/bin hello
The first line, source $ stdenv / setup
, is needed to set up the build environment on Nix.
After writing the build script, pass the script path to builder
.
default.nix
with import <nixpkgs> {};
stdenv.mkDerivation {
pname = "hello";
version = "0.1.0";
src = ./hello.c;
builder = ./builder.sh;
}
Let's go
myproject> nix-build
these derivations will be built:
/nix/store/j2vxh4xm6jarf0jnm9x3sywmr1nvxdhg-hello-0.1.0.drv
building '/nix/store/j2vxh4xm6jarf0jnm9x3sywmr1nvxdhg-hello-0.1.0.drv'...
/nix/store/i8l2rmiy708sz6n486yasgzshp044kpd-hello-0.1.0
You can see that the hash has changed by rewriting default.nix. The one you just built will not be overwritten, but a new build will be created in a separate file. If you re-tension the symbol, it seems that you can always return to the previous build.
Stop using the local source code and try downloading and building the source code on the web. Let's build GNU / hello. The project page is here.
Use fetchzip
to download the source.
default.nix
stdenv.mkDerivation {
...
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz";
sha256 = "1im1gglfm4k10bh4mdaqzmx3lm3kivnsmxrvl6vyvmfqqzljq75l";
};
...
}
fetchzip
downloads the compressed source from the web and deploys it locally.
The argument requires a hash such as sha256
as well as ʻurl`.
Use the nix-prefetch-url
command to calculate the hash.
The --unpack
option is required for compressed files.
myproject> nix-prefetch-url --unpack 'https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz'
unpacking...
[0.7 MiB DL]
path is '/nix/store/xsh1xgry2vsf6674jhc26q8k1bv4p76s-hello-2.10.tar.gz'
1im1gglfm4k10bh4mdaqzmx3lm3kivnsmxrvl6vyvmfqqzljq75l
GNU / hello can be built with ./configure; make; make install
.
For a typical build like this, mkDerivation
will build nicely, so you don't have to write builder.sh
by hand.
The whole default.nix
looks like this:
default.nix
stdenv.mkDerivation rec {
pname = "hello";
version = "2.10";
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/${pname}-${version}.tar.gz";
sha256 = "1im1gglfm4k10bh4mdaqzmx3lm3kivnsmxrvl6vyvmfqqzljq75l";
};
}
It's a small detail, but use $ {}
for string interpolation in Nix. Don't forget rec
.
Let's go
myproject> ls
default.nix
myproject> nix-build
these derivations will be built:
/nix/store/q64l9f6d29zivah3sqrxlhpw8wmz3jrk-source.drv
/nix/store/936zpfpmzxliinc69a49yn4dh67pgj6x-hello-2.10.drv
...
That's the basic build method.
Building Nix is environment agnostic, so you can build it on any PC with just one default.nix
.
I think it's worth using Nix just for this peace of mind.
If you want to know more about mkDerivation, you can refer to the manual and Nix Pills.
In addition to fetchzip
, there are also functions to download sources from GitHub and GitLab.
They are also explained in the manual.
Unfortunately, the Nix manual is still evolving. Therefore, the manual is still lacking a lot of content. When actually building a program on Nix, the source of the Nixpkgs repository is very helpful.
Also, in order to handle mkDerivation
freely, it is good to read the source of stdenv / setup
.
Recommended Posts