mirror of
https://github.com/NixOS/nix-pills
synced 2024-11-14 15:47:10 +00:00
330 lines
11 KiB
XML
330 lines
11 KiB
XML
<chapter xmlns="http://docbook.org/ns/docbook"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
|
version="5.0"
|
|
xml:id="generic-builders">
|
|
|
|
<title>Generic Builders</title>
|
|
|
|
<para>
|
|
Welcome to the 8th Nix pill. In the previous
|
|
<link linkend="working-derivation">7th pill</link> we successfully built a
|
|
derivation. We wrote a builder script that compiled a C file and installed
|
|
the binary under the nix store.
|
|
</para>
|
|
|
|
<para>
|
|
In this post, we will generalize the builder script, write a Nix expression
|
|
for <link
|
|
xlink:href="http://www.gnu.org/software/hello/">GNU hello world</link>
|
|
and create a wrapper around the derivation built-in function.
|
|
</para>
|
|
|
|
<section>
|
|
<title>Packaging GNU hello world</title>
|
|
|
|
<para>
|
|
In the previous pill we packaged a simple .c file, which was being
|
|
compiled with a raw gcc call. That's not a good example of project. Many
|
|
use autotools, and since we're going to generalize our builder, better do
|
|
it with the most used build system.
|
|
</para>
|
|
|
|
<para>
|
|
<link xlink:href="http://www.gnu.org/software/hello/">GNU hello world</link>,
|
|
despite its name, is a simple yet complete project using autotools.
|
|
Fetch the latest tarball here:
|
|
<link xlink:href="http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz">http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz</link>.
|
|
</para>
|
|
|
|
<para>
|
|
Let's create a builder script for GNU hello world, hello_builder.sh:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/hello-builder.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
And the derivation hello.nix:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/hello-nix.txt" parse="text" /></screen>
|
|
<note><title>Nix on darwin</title>
|
|
<para>The darwin (i.e. macOS) <literal>stdenv</literal> diverges from the Linux <literal>stdenv</literal> in several ways. A main difference is that the darwin <literal>stdenv</literal> relies upon <literal>clang</literal> rather than <literal>gcc</literal> as its C compiler. We can adapt this early example of how a <literal>stdenv</literal> works for darwin by using this modified version of <filename>hello.nix</filename>:
|
|
<screen><xi:include href="./08/hello-nix-darwin.txt" parse="text" /></screen>
|
|
Please be aware that similar changes may be needed in what follows.
|
|
</para>
|
|
</note>
|
|
|
|
<para>
|
|
Now build it with <command>nix-build hello.nix</command> and you can
|
|
launch <filename>result/bin/hello</filename>. Nothing easier, but do we
|
|
have to create a builder.sh for each package? Do we always have to pass
|
|
the dependencies to the <literal>derivation</literal> function?
|
|
</para>
|
|
|
|
<para>
|
|
Please note the <command>--prefix=$out</command> we were talking about in
|
|
the <link linkend="working-derivation">previous pill</link>.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>A generic builder</title>
|
|
|
|
<para>
|
|
Let's a create a generic <filename>builder.sh</filename> for autotools
|
|
projects:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/generic-builder.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
What do we do here?
|
|
</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
Exit the build on any error with <command>set -e</command>.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
First <command>unset PATH</command>, because it's initially set to a
|
|
non-existant path.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
We'll see this below in detail, however for each path in
|
|
<code>$buildInputs</code>, we append <code>bin</code> to
|
|
<code>PATH</code>.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Unpack the source.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Find a directory where the source has been unpacked and
|
|
<command>cd</command> into it.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Once we're set up, compile and install.
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>
|
|
As you can see, there's no reference to "hello" in the builder anymore.
|
|
It still does several assumptions, but it's certainly more generic.
|
|
</para>
|
|
|
|
<para>
|
|
Now let's rewrite <filename>hello.nix</filename>:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/hello-nix-rev-1.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
All clear, except that buildInputs. However it's easier than any black
|
|
magic you are thinking in this moment.
|
|
</para>
|
|
|
|
<para>
|
|
Nix is able to convert a list to a string. It first converts the elements
|
|
to strings, and then concatenates them separated by a space:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/to-string.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
Recall that derivations can be converted to a string, hence:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/to-string-nixpkgs.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
Simple! The buildInputs variable is a string with out paths separated by
|
|
space, perfect for bash usage in a for loop.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>A more convenient derivation function</title>
|
|
|
|
<para>
|
|
We managed to write a builder that can be used for multiple autotools
|
|
projects. But in the hello.nix expression we are specifying tools that
|
|
are common to more projects; we don't want to pass them everytime.
|
|
</para>
|
|
|
|
<para>
|
|
A natural approach would be to create a function that accepts an
|
|
attribute set, similar to the one used by the derivation function, and
|
|
merge it with another attribute set containing values common to many
|
|
projects.
|
|
</para>
|
|
|
|
<para>
|
|
Create <filename>autotools.nix</filename>:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/autotools-nix.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
Ok now we have to remember a little about
|
|
<link linkend="functions-and-imports">Nix functions</link>. The whole nix
|
|
expression of this <filename>autotools.nix</filename> file will evaluate
|
|
to a function. This function accepts a parameter <code>pkgs</code>, then
|
|
returns a function which accepts a parameter <code>attrs</code>.
|
|
</para>
|
|
|
|
<para>
|
|
The body of the function is simple, yet at first sight it might be hard
|
|
to grasp:
|
|
</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
First drop in the scope the magic <code>pkgs</code> attribute set.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Within a let expression we define a helper variable,
|
|
<code>defaultAttrs</code>, which serves as a set of common attributes
|
|
used in derivations.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Finally we create the derivation with that strange expression,
|
|
(<code>defaultAttrs // attrs</code>).
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>
|
|
The
|
|
<link xlink:href="http://nixos.org/nix/manual/#idm47361539098656">// operator</link>
|
|
is an operator between two sets. The result is the union of the two sets.
|
|
In case of conflicts between attribute names, the value on the right set
|
|
is preferred.
|
|
</para>
|
|
|
|
<para>
|
|
So we use <code>defaultAttrs</code> as base set, and add (or override) the
|
|
attributes from <code>attrs</code>.
|
|
</para>
|
|
|
|
<para>
|
|
A couple of examples ought to be enough to clear out the behavior of the
|
|
operator:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/set-union.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
<emphasis role="bold">Exercise:</emphasis>
|
|
Complete the new <filename>builder.sh</filename> by adding
|
|
<code>$baseInputs</code> in the <code>for</code> loop together with
|
|
<code>$buildInputs</code>. As you noticed, we passed that new variable in
|
|
the derivation. Instead of merging buildInputs with the base ones, we
|
|
prefer to preserve buildInputs as seen by the caller, so we keep them
|
|
separated. Just a matter of choice.
|
|
</para>
|
|
|
|
<para>
|
|
Then we rewrite <filename>hello.nix</filename> as follows:
|
|
</para>
|
|
|
|
<screen><xi:include href="./08/hello-nix-rev-2.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
Finally! We got a very simple description of a package! A couple of
|
|
remarks that you may find useful to keep understanding the nix language:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
We assigned to pkgs the import that we did in the previous expressions
|
|
in the "with", don't be afraid. It's that straightforward.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The mkDerivation variable is a nice example of partial application,
|
|
look at it as (<code>import ./autotools.nix</code>) <code>pkgs</code>.
|
|
First we import the expression, then we apply the <code>pkgs</code>
|
|
parameter. That will give us a function that accepts the attribute
|
|
set <code>attrs</code>.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
We create the derivation specifying only name and src. If the project
|
|
eventually needed other dependencies to be in PATH, then we would
|
|
simply add those to buildInputs (not specified in hello.nix because
|
|
empty).
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
Note we didn't use any other library. Special C flags may be needed to
|
|
find include files of other libraries at compile time, and ld flags at
|
|
link time.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Conclusion</title>
|
|
|
|
<para>
|
|
Nix gives us the bare metal tools for creating derivations, setting up a
|
|
build environment and storing the result in the nix store.
|
|
</para>
|
|
|
|
<para>
|
|
Out of this we managed to create a generic builder for autotools projects,
|
|
and a function <code>mkDerivation</code> that composes by default the
|
|
common components used in autotools projects instead of repeating them
|
|
in all the packages we would write.
|
|
</para>
|
|
|
|
<para>
|
|
We are feeling the way a Nix system grows up: it's about creating and
|
|
composing derivations with the Nix language.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis role="underline">Analogy</emphasis>: in C you create objects
|
|
in the heap, and then you compose them inside new objects. Pointers are
|
|
used to refer to other objects.
|
|
</para>
|
|
|
|
<para>
|
|
In Nix you create derivations stored in the nix store, and then you
|
|
compose them by creating new derivations. Store paths are used to refer
|
|
to other derivations.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Next pill</title>
|
|
|
|
<para>
|
|
...we will talk a little about runtime dependencies. Is the GNU hello
|
|
world package self-contained? What are its runtime dependencies? We only
|
|
specified build dependencies by means of using other derivations in the
|
|
"hello" derivation.
|
|
</para>
|
|
</section>
|
|
</chapter>
|