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.nixAll you're doing with default.nix is passing the necessary arguments to stdenv.mkDerivation.
The argument of mkDerivation is
namesrcbuildCommandHas 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.nixTo 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