Writing Nix ExpressionsThis chapter shows you how to write Nix expressions, which are
the things that tell Nix how to build components. It starts with a
simple example (a Nix expression for GNU Hello), and then moves
on to a more in-depth look at the Nix expression language.A simple Nix expressionThis section shows how to add and test the GNU Hello
package to the Nix Packages collection. Hello is a program
that prints out the text Hello, world!.To add a component to the Nix Packages collection, you generally
need to do three things:
Write a Nix expression for the component. This is a
file that describes all the inputs involved in building the
component, such as dependencies (other components required by the
component), sources, and so on.Write a builder. This is a
shell scriptIn fact, it can be written in any
language, but typically it's a bash shell
script. that actually builds the component from
the inputs.Add the component to the file
pkgs/system/all-packages-generic.nix. The Nix
expression written in the first step is a
function; it requires other components in order
to build it. In this step you put it all together, i.e., you call
the function with the right arguments to build the actual
component.The Nix expressionNix expression for GNU Hello
{stdenv, fetchurl, perl}:
stdenv.mkDerivation {
name = "hello-2.1.1";
builder = ./builder.sh;
src = fetchurl {
url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
md5 = "70c9ccf9fac07f762c24f2df2290784d";
};
inherit perl;
} shows a Nix expression for GNU
Hello. It's actually already in the Nix Packages collection in
pkgs/applications/misc/hello/ex-1/default.nix.
It is customary to place each package in a separate directory and call
the single Nix expression in that directory
default.nix. The file has the following elements
(referenced from the figure by number):
This states that the expression is a
function that expects to be called with three
arguments: stdenv, fetchurl,
and perl. They are needed to build Hello, but
we don't know how to build them here; that's why they are function
arguments. stdenv is a component that is used
by almost all Nix Packages components; it provides a
standard environment consisting of the things you
would expect in a basic Unix environment: a C/C++ compiler (GCC,
to be precise), the Bash shell, fundamental Unix tools such as
cp, grep,
tar, etc. (See
pkgs/stdenv/nix/path.nix to see what's in
stdenv.) fetchurl is a
function that downloads files. perl is the
Perl interpreter.Nix functions generally have the form {x, y, ...,
z}: e where x, y,
etc. are the names of the expected arguments, and where
e is the body of the function. So
here, the entire remainder of the file is the body of the
function; when given the required arguments, the body should
describe how to build an instance of the Hello component.So we have to build a component. Building something from
other stuff is called a derivation in Nix (as
opposed to sources, which are built by humans instead of
computers). We perform a derivation by calling
stdenv.mkDerivation.
mkDerivation is a function provided by
stdenv that builds a component from a set of
attributes. An attribute set is just a list
of key/value pairs where the value is an arbitrary Nix expression.
They take the general form
{name1 =
expr1; ...name1 =
expr1;.The attribute name specifies the symbolic
name and version of the component. Nix doesn't really care about
these things, but they are used by for instance nix-env
-q to show a human-readable name for
components. This attribute is required by
mkDerivation.The attribute builder specifies the
builder. This attribute can sometimes be omitted, in which case
mkDerivation will fill in a default builder
(which does a configure; make; make install, in
essence). Hello is sufficiently simple that the default builder
would suffice, but in this case, we will show an actual builder
for educational purposes. The value
./builder.sh refers to the shell script shown
in , discussed below.The builder has to know what the sources of the component
are. Here, the attribute src is bound to the
result of a call to the fetchurl function.
Given a URL and a MD5 hash of the expected contents of the file at
that URL, this function actually builds a derivation that
downloads the file and checks its hash. So the sources are a
dependency that like all other dependencies is built before Hello
itself is built.Instead of src any other name could have
been used, and in fact there can be any number of sources (bound
to different attributes). However, src is
customary, and it's also expected by the default builder (which we
don't use in this example).Since the derivation requires Perl, we have to pass the
value of the perl function argument to the
builder. All attributes in the set are actually passed as
environment variables to the builder, so declaring an attribute
perl = perl;
will do the trink: it binds an attribute perl
to the function argument which also happens to be called
perl. However, it looks a bit silly, so there
is a shorter syntax. The inherit keyword
causes the specified attributes to be bound to whatever variables
with the same name happen to be in scope.The builderBuild script for GNU Hello
. $stdenv/setup
PATH=$perl/bin:$PATH
tar xvfz $src
cd hello-*
./configure --prefix=$out
make
make install shows the builder referenced
from Hello's Nix expression (stored in
pkgs/applications/misc/hello/ex-1/builder.sh).TODOIf you are wondering about the absence of error checking on the
result of various commands called in the builder: this is because the
shell script is evaluated with Bash's option,
which causes the script to be aborted if any command fails without an
error check.