about summary refs log blame commit diff
path: root/doc/manual/writing-nix-expressions.xml
blob: b16d00b9277bd2f70e9d235fca5430d189412962 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                                                                 





                                                                    

                                             


































                                                                      




                                                                      
                                                   
                                                     

                                                       


                                                              
                                             


                  



































                                                                      
    





















































































                                                                      

       
        
 



                                                                        
                
               
 
                    
 




                             

          


                                                                          
 
                 
 




                                                                     
 
        
 




          
<chapter id='chap-writing-nix-expressions'><title>Writing Nix Expressions</title>

<para>This 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.</para>


<sect1><title>A simple Nix expression</title>

<para>This section shows how to add and test the <ulink
url='http://www.gnu.org/software/hello/hello.html'>GNU Hello
package</ulink> to the Nix Packages collection.  Hello is a program
that prints out the text <quote>Hello, world!</quote>.</para>

<para>To add a component to the Nix Packages collection, you generally
need to do three things:

<orderedlist>

  <listitem><para>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.</para></listitem>

  <listitem><para>Write a <emphasis>builder</emphasis>.  This is a
  shell script<footnote><para>In fact, it can be written in any
  language, but typically it's a <command>bash</command> shell
  script.</para></footnote> that actually builds the component from
  the inputs.</para></listitem>

  <listitem><para>Add the component to the file
  <filename>pkgs/system/all-packages-generic.nix</filename>.  The Nix
  expression written in the first step is a
  <emphasis>function</emphasis>; 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.</para></listitem>

</orderedlist>

</para>


<sect2><title>The Nix expression</title>

<example id='ex-hello-nix'><title>Nix expression for GNU Hello</title>
<programlisting>
{stdenv, fetchurl, perl}: <co id='ex-hello-nix-co-1' />

stdenv.mkDerivation { <co id='ex-hello-nix-co-2' />
  name = "hello-2.1.1"; <co id='ex-hello-nix-co-3' />
  builder = ./builder.sh; <co id='ex-hello-nix-co-4' />
  src = fetchurl { <co id='ex-hello-nix-co-5' />
    url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
    md5 = "70c9ccf9fac07f762c24f2df2290784d";
  };
  inherit perl; <co id='ex-hello-nix-co-6' />
}</programlisting>
</example>

<para><xref linkend='ex-hello-nix' /> shows a Nix expression for GNU
Hello.  It's actually already in the Nix Packages collection in
<filename>pkgs/applications/misc/hello/ex-1/default.nix</filename>.
It is customary to place each package in a separate directory and call
the single Nix expression in that directory
<filename>default.nix</filename>.  The file has the following elements
(referenced from the figure by number):

<calloutlist>

  <callout arearefs='ex-hello-nix-co-1'>

    <para>This states that the expression is a
    <emphasis>function</emphasis> that expects to be called with three
    arguments: <varname>stdenv</varname>, <varname>fetchurl</varname>,
    and <varname>perl</varname>.  They are needed to build Hello, but
    we don't know how to build them here; that's why they are function
    arguments.  <varname>stdenv</varname> is a component that is used
    by almost all Nix Packages components; it provides a
    <quote>standard</quote> 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
    <command>cp</command>, <command>grep</command>,
    <command>tar</command>, etc.  (See
    <filename>pkgs/stdenv/nix/path.nix</filename> to see what's in
    <command>stdenv</command>.)  <varname>fetchurl</varname> is a
    function that downloads files.  <varname>perl</varname> is the
    Perl interpreter.</para>

    <para>Nix functions generally have the form <literal>{x, y, ...,
    z}: e</literal> where <varname>x</varname>, <varname>y</varname>,
    etc. are the names of the expected arguments, and where
    <replaceable>e</replaceable> 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.</para>
    
  </callout>

  <callout arearefs='ex-hello-nix-co-2'>

    <para>So we have to build a component.  Building something from
    other stuff is called a <emphasis>derivation</emphasis> in Nix (as
    opposed to sources, which are built by humans instead of
    computers).  We perform a derivation by calling
    <varname>stdenv.mkDerivation</varname>.
    <varname>mkDerivation</varname> is a function provided by
    <varname>stdenv</varname> that builds a component from a set of
    <emphasis>attributes</emphasis>.  An attribute set is just a list
    of key/value pairs where the value is an arbitrary Nix expression.
    They take the general form
    <literal>{<replaceable>name1</replaceable> =
    <replaceable>expr1</replaceable>; <replaceable>...</replaceable>
    <replaceable>name1</replaceable> =
    <replaceable>expr1</replaceable>;</literal>.</para>

  </callout>

  <callout arearefs='ex-hello-nix-co-3'>

    <para>The attribute <varname>name</varname> specifies the symbolic
    name and version of the component.  Nix doesn't really care about
    these things, but they are used by for instance <command>nix-env
    -q</command> to show a <quote>human-readable</quote> name for
    components.  This attribute is required by
    <varname>mkDerivation</varname>.</para>

  </callout>

  <callout arearefs='ex-hello-nix-co-4'>

    <para>The attribute <varname>builder</varname> specifies the
    builder.  This attribute can sometimes be omitted, in which case
    <varname>mkDerivation</varname> will fill in a default builder
    (which does a <literal>configure; make; make install</literal>, 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
    <command>./builder.sh</command> refers to the shell script shown
    in <xref linkend='ex-hello-builder' />, discussed below.</para>

  </callout>

  <callout arearefs='ex-hello-nix-co-5'>

    <para>The builder has to know what the sources of the component
    are.  Here, the attribute <varname>src</varname> is bound to the
    result of a call to the <command>fetchurl</command> 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.</para>

    <para>Instead of <varname>src</varname> any other name could have
    been used, and in fact there can be any number of sources (bound
    to different attributes).  However, <varname>src</varname> is
    customary, and it's also expected by the default builder (which we
    don't use in this example).</para>

  </callout>

  <callout arearefs='ex-hello-nix-co-6'>

    <para>Since the derivation requires Perl, we have to pass the
    value of the <varname>perl</varname> function argument to the
    builder.  All attributes in the set are actually passed as
    environment variables to the builder, so declaring an attribute

    <programlisting>
perl = perl;</programlisting>

    will do the trink: it binds an attribute <varname>perl</varname>
    to the function argument which also happens to be called
    <varname>perl</varname>.  However, it looks a bit silly, so there
    is a shorter syntax.  The <literal>inherit</literal> keyword
    causes the specified attributes to be bound to whatever variables
    with the same name happen to be in scope.</para>

  </callout>
  
</calloutlist>

</para>

</sect2>


<sect2><title>The builder</title>

<example id='ex-hello-builder'><title>Build script for GNU Hello</title>
<programlisting>
. $stdenv/setup

PATH=$perl/bin:$PATH

tar xvfz $src
cd hello-*
./configure --prefix=$out
make
make install</programlisting>
</example>

<para><xref linkend='ex-hello-builder' /> shows the builder referenced
from Hello's Nix expression (stored in
<filename>pkgs/applications/misc/hello/ex-1/builder.sh</filename>).</para>

<para>TODO</para>

<para>If 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>-e</option> option,
which causes the script to be aborted if any command fails without an
error check.</para>

</sect2>


</sect1>


</chapter>