Nixifying QOI
using nix to build simple C programs
Tim McNamara runs a discord channel for folks, and weekly there are challenges posted. This week's challenge was to code in Rust a lossless image format called QOI (Quite OK Image format). You can find the repo here.
The code is written in a single header file for compilation with C/C++ and one dependency: stb. Given the relative simplicity of the program's dependencies and build, I wanted to walk through building it with nix.
So, we start by writing a mkDerivation in a default.nix at the root of the project. Here's what I wrote, and I'll walk through each item step-by-step!
with import <nixpkgs> {};
stdenv.mkDerivation {
version = "0.0.1";
name = "qoiconv";
buildInputs = [ stb ];
src = ./.;
buildPhase = ''
NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -isystem ${stb.src}"
make
'';
installPhase = ''
mkdir -p $out/bin
cp qoiconv $out/bin/qoiconv
'';
doCheck = false;
}
So, we first set some version information, a name for the project, our source directory (current git repo root folder), and a list of build inputs. The reason I knew on line 6 that stb was in Nixpkgs was thanks to the lovely nixpkgs search.
With the inputs specified, we now need to adapt our build process to have some new includes. Typically, pkg-config in nativeBuildInputs and the proper buildInputs would solve this, but stb is special in that it is also a header only C library, and the includes are under the stb subdir of the output derivation. Given that, we need to adjust our NIX_CFLAGS_COMPILE to have a new system include, and to point it to the src folder of the stb dependency. Once adjusted, we then call make. Here is the Makefile:
qoiconv: qoiconv.c
$(CC) -o qoiconv -std=c99 -O3 qoiconv.c
So, we have hijacked the buildPhase for our custom behavior, and now we need to customize the installPhase. By using the $out variable in the install phase, we can force the availability of the compiled binary in the output of our derivation (under a result symlink in the root project directory). Lines 12-15 do this by specifying where to install the compiled binary.
Finally, we don't have any tests defined, so set doCheck = false
to stop
checkPhase from running in the stdenv builder.
When we run our process via:
nix-build
We now get a result/
symlink in the current directory, which has a bin/
folder and our qoiconv program inside it. Great! Now that we've compiled the
existing work, its time to convert the process into a Rust Library and
Executable. I'm continuing this work on a
fork which you can
gitpod, so stay
tuned!