From 6ca9c7f0a91a2998ecd0063ff68f01e6eca12759 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Nov 2004 13:10:08 +0000 Subject: [PATCH] * Finished GNU Hello walkthrough. --- doc/manual/Makefile.am | 3 +- doc/manual/bugs.xml | 11 - doc/manual/writing-nix-expressions.xml | 300 ++++++++++++++++++++++++- 3 files changed, 293 insertions(+), 21 deletions(-) diff --git a/doc/manual/Makefile.am b/doc/manual/Makefile.am index 98d85ecf7a..e577be8c6f 100644 --- a/doc/manual/Makefile.am +++ b/doc/manual/Makefile.am @@ -4,7 +4,8 @@ XMLLINT = $(ENV) $(xmllint) $(xmlflags) --catalogs XSLTPROC = $(ENV) $(xsltproc) $(xmlflags) --catalogs \ --param section.autolabel 1 \ --param section.label.includes.component.label 1 \ - --param html.stylesheet \'style.css\' + --param html.stylesheet \'style.css\' \ + --param xref.with.number.and.title 0 man1_MANS = nix-env.1 nix-store.1 nix-instantiate.1 \ nix-collect-garbage.1 nix-push.1 nix-pull.1 \ diff --git a/doc/manual/bugs.xml b/doc/manual/bugs.xml index b479c13541..6097b2aa00 100644 --- a/doc/manual/bugs.xml +++ b/doc/manual/bugs.xml @@ -52,17 +52,6 @@ - - - The current garbage collector is a hack. It should be - integrated into nix-store. It should - delete derivations in an order determined by topologically - sorting derivations under the points-to relation. This - ensures that no store paths ever exist that point to - non-existant store paths. - - - There are race conditions between the garbage collector and diff --git a/doc/manual/writing-nix-expressions.xml b/doc/manual/writing-nix-expressions.xml index b16d00b927..d3514b6259 100644 --- a/doc/manual/writing-nix-expressions.xml +++ b/doc/manual/writing-nix-expressions.xml @@ -44,7 +44,8 @@ need to do three things: The Nix expression -Nix expression for GNU Hello +Nix expression for GNU Hello +(<filename>default.nix</filename>) {stdenv, fetchurl, perl}: @@ -189,24 +190,108 @@ perl = perl; The builder -Build script for GNU Hello +Build script for GNU Hello +(<filename>builder.sh</filename>) -. $stdenv/setup +. $stdenv/setup -PATH=$perl/bin:$PATH +PATH=$perl/bin:$PATH -tar xvfz $src +tar xvfz $src cd hello-* -./configure --prefix=$out -make +./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). +pkgs/applications/misc/hello/ex-1/builder.sh). +The builder can actually be made a lot shorter by using the +generic builder functions provided by +stdenv, but here we write out the build steps to +elucidate what a builder does. It performs the following +steps: -TODO + + + + + When Nix runs a builder, it initially completely clears the + environment. For instance, the PATH variable is + emptyActually, it's initialised to + /path-not-set to prevent Bash from setting it + to a default value.. This is done to prevent + undeclared inputs from being used in the build process. If for + example the PATH contained + /usr/bin, then you might accidentally use + /usr/bin/gcc. + + So the first step is to set up the environment. This is + done by calling the setup script of the + standard environment. The environment variable + stdenv points to the location of the standard + environment being used. (It wasn't specified explicitly as an + attribute in , but + mkDerivation adds it automatically.) + + + + + + Since Hello needs Perl, we have to make sure that Perl is in + the PATH. The perl environment + variable points to the location of the Perl component (since it + was passed in as an attribute to the derivation), so + $perl/bin is the + directory containing the Perl interpreter. + + + + + + Now we have to unpack the sources. The + src attribute was bound to the result of + fetching the Hello source tarball from the network, so the + src environment variable points to the location in + the Nix store to which the tarball was downloaded. After + unpacking, we cd to the resulting source + directory. + + The whole build is performed in a temporary directory + created in /tmp, by the way. This directory is + removed after the builder finishes, so there is no need to clean + up the sources afterwards. Also, the temporary directory is + always newly created, so you don't have to worry about files from + previous builds interfering with the current build. + + + + + + GNU Hello is a typical Autoconf-based package, so we first + have to run its configure script. In Nix + every component is stored in a separate location in the Nix store, + for instance + /nix/store/9a54ba97fb71b65fda531012d0443ce2-hello-2.1.1. + Nix computes this path by cryptographically hashing all attributes + of the derivation. The path is passed to the builder through the + out environment variable. So here we give + configure the parameter + --prefix=$out to cause Hello to be installed in + the expected location. + + + + + + Finally we build Hello (make) and install + it into the location specified by out + (make install). + + + + If you are wondering about the absence of error checking on the result of various commands called in the builder: this is because the @@ -217,6 +302,203 @@ error check. +Composition + +Composing GNU Hello +(<filename>all-packages-generic.nix</filename>) + +... + +rec { + + hello = (import ../applications/misc/hello/ex-1 ) { + inherit fetchurl stdenv perl; + }; + + perl = (import ../development/interpreters/perl) { + inherit fetchurl stdenv; + }; + + fetchurl = (import ../build-support/fetchurl) { + inherit stdenv; ... + }; + + stdenv = ...; + +} + + + +The Nix expression in is a +function; it is missing some arguments that have to be filled in +somewhere. In the Nix Packages collection this is done in the file +pkgs/system/all-packages-generic.nix, where all +Nix expressions for components are imported and called with the +appropriate arguments. shows +some fragments of +all-packages-generic.nix. + + + + + + This file defines a set of attributes, all of which are + concrete derivations (i.e., not functions). In fact, we define a + mutually recursive set of attributes. That + is, the attributes can refer to each other. This is precisely + what we want since we want to plug the + various components into each other. + + + + + + Here we import the Nix expression for + GNU Hello. The import operation just loads and returns the + specified Nix expression. In fact, we could just have put the + contents of in + all-packages-generic.nix at this point. That + would be completely equivalent, but it would make the file rather + bulky. + + Note that we refer to + ../applications/misc/hello/ex-1, not + ../applications/misc/hello/ex-1/default.nix. + When you try to import a directory, Nix automatically appends + /default.nix to the file name. + + + + + + This is where the actual composition takes place. Here we + call the function imported from + ../applications/misc/hello/ex-1 with an + attribute set containing the things that the function expects, + namely fetchurl, stdenv, and + perl. We use inherit again to use the + attributes defined in the surrounding scope (we could also have + written fetchurl = fetchurl;, etc.). + + The result of this function call is an actual derivation + that can be built by Nix (since when we fill in the arguments of + the function, what we get is its body, which is the call to + stdenv.mkDerivation in ). + + + + + + Likewise, we have to instantiate Perl, + fetchurl, and the standard environment. + + + + + + + + +Testing + +You can now try to build Hello. The simplest way to do that is +by using nix-env: + + +$ nix-env -f pkgs/system/i686-linux.nix -i hello +installing `hello-2.1.1' +building path `/nix/store/632d2b22514dcebe704887c3da15448d-hello-2.1.1' +hello-2.1.1/ +hello-2.1.1/intl/ +hello-2.1.1/intl/ChangeLog +... + + +This will build Hello and install it into your profile. The file +i686-linux is just a simple Nix expression that +imports all-packages-generic.nix and instantiates +it for Linux on the x86 platform. + +Note that the hello argument here refers to +the symbolic name given to the Hello derivation (the +name attribute in ), +not the hello attribute in +all-packages-generic.nix. +nix-env simply walks through all derivations +defined in the latter file, looking for one with a +name attribute matching the command-line +argument. + +You can test whether it works: + + +$ hello +Hello, world! + + + +Generally, however, using nix-env is not the +best way to test components, since you may not want to install them +into your profile right away (they might not work properly, after +all). A better way is to write a short file containging the +following: + + +(import pkgs/system/i686-linux.nix).hello + +Call it test.nix. Then you can build it without +installing it using the command nix-build: + + +$ nix-build ./test.nix +... +/nix/store/632d2b22514dcebe704887c3da15448d-hello-2.1.1 + +nix-build will build the derivation and print the +output path. It also creates a symlink to the output path called +result in the current directory. This is +convenient for testing the component: + + +$ ./result/bin/hello +Hello, world! + + + +Nix has a transactional semantics. Once a build finishes +succesfully, Nix makes a note of this in its database: it registers +that the path denoted by out is now +valid. If you try to build the derivation again, Nix +will see that the path is already valid and finish immediately. If a +build fails, either because it returns a non-zero exit code, because +Nix or the builder are killed, or because the machine crashes, then +the output path will not be registered as valid. If you try to build +the derivation again, Nix will remove the output path if it exists +(e.g., because the builder died half-way through make +install) and try again. Note that there is no +negative caching: Nix doesn't remember that a build +failed, and so a failed build can always be repeated. This is because +Nix cannot distinguish between permanent failures (e.g., a compiler +error due to a syntax error in the source) and transient failures +(e.g., a disk full condition). + +Nix also performs locking. If you run multiple Nix builds +simultaneously, and they try to build the same derivation, the first +Nix instance that gets there will perform the build, while the others +block (or perform other derivations if available) until the build +finishes. So it is always safe to run multiple instances of Nix in +parallel (contrary to, say, make). + +If you have a system with multiple CPUs, you may want to have +Nix build different derivations in parallel (insofar as possible). +Just pass the option , +where N is the maximum number of jobs to be +run in parallel. Typically this should be the number of CPUs. + + + +