Practical Nix Functions
✔️ If you want to follow along with this example you'll have to place the following in your project directory. Section is collapsed to focus on functions:

# autotools.nix
pkgs: attrs:
with pkgs; 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
derivation (defaultAttrs // attrs)
setup.sh:
# setup.sh (This is a library of functions setting up the environment, not directly executable)
unset PATH
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
function unpackPhase() {
tar -xzf $src
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
}
function fixupPhase() {
find $out -type f -exec patchelf --shrink-rpath '{}' \; -exec strip '{}' \; 2>/dev/null
}
function genericBuild() {
unpackPhase
configurePhase
buildPhase
installPhase
fixupPhase
}
- And finally
builder.sh:
# builder.sh (This is the actual builder script specified in the derivation and
# what `nix-build` expects)
set -e
source $setup
genericBuild
This is another example from the Nix-Pill series shown in another way to show some powerful aspects of functions.
If you have a default.nix like this:
# default.nix
{
hello = import ./hello.nix;
graphviz = import ./graphviz.nix;
}
It expects the files that it imports to look like this:
# graphviz.nix
let
pkgs = import <nixpkgs> { };
mkDerivation = import ./autotools.nix pkgs;
in
mkDerivation {
name = "graphviz";
src = ./graphviz-2.49.3.tar.gz;
}
And hello.nix:
# hello.nix
let
pkgs = import <nixpkgs> { };
mkDerivation = import ./autotools.nix pkgs;
in
mkDerivation {
name = "hello";
src = ./hello-2.12.1.tar.gz;
}
You would build these with:
nix-build -A hello
nix-build -A graphviz
As you can see both derivations are dependendent on nixpkgs which they
both import directly. To centralize our dependencies and avoid redundant
imports, we’ll refactor our individual package definitions (hello.nix,
graphviz.nix) into functions. Our default.nix will then be responsible for
setting up the common inputs (like pkgs and mkDerivation) and passing them
as arguments when it imports and calls these package functions.
Here is what our default.nix will look like:
let
pkgs = import <nixpkgs> { };
mkDerivation = import ./autotools.nix pkgs;
in
with pkgs;
{
hello = import ./hello.nix { inherit mkDerivation; };
graphviz = import ./graphviz.nix {
inherit
mkDerivation
lib
gd
pkg-config
;
};
graphvizCore = import ./graphviz.nix {
inherit
mkDerivation
lib
gd
pkg-config
;
gdSupport = false;
};
}
We define some local variables in the let expression and pass them around.
The whole expression in the above default.nix returns an attribute set with
the keys hello, graphviz, and graphvizCore
We import hello.nix and graphviz.nix, which both return a function. We call
the functions, passing them a set of inputs with the inherit construct.
Let’s change hello.nix into a function to match what the default.nix now
expects.
# hello.nix
{mkDerivation}:
mkDerivation {
name = "hello";
src = ./hello-2.12.1.tar.gz;
}
Now our graphviz attribute expects graphviz.nix to be a function that takes
the arguments listed in the above default.nix, here’s what graphviz.nix will
look like as a function:
# graphviz.nix
{
mkDerivation,
lib,
gdSupport ? true,
gd,
pkg-config,
}:
mkDerivation {
name = "graphviz";
src = ./graphviz-2.49.3.tar.gz;
buildInputs =
if gdSupport
then [
pkg-config
(lib.getLib gd)
(lib.getDev gd)
]
else [];
}
We factorized the import of nixpkgs and mkDerivation, and also added a
variant of graphviz with gd support disabled. The result is that both
hello.nix and graphviz.nix are independent of the repository and
customizable by passing specific inputs.
Now, we can build the package with gd support disabled with the graphvizCore
attribute:
nix-build -A graphvizCore
# or we can still build the package that now defaults to gd support
nix-build -A graphviz
This example showed us how to turn expressions into functions. We saw how functions are passed around and shared between Nix expressions and derivations.