Overview This chapter provides a guided tour of Nix. Basic package management Let's start from the perspective of an end user. Common operations at this level are to install and remove packages, ask what packages are installed or available for installation, and so on. These are operations on the user environment: the set of packages that a user sees. In a command line Unix environment, this means the set of programs that are available through the PATH environment variable. (In other environments it might mean the set of programs available on the desktop, through the start menu, and so on.) The terms installation and uninstallation are used in this context to denote the act of adding or removing packages from the user environment. In Nix, these operations are dissociated from the physical copying or deleting of files. Installation requires that the files constituting the package are present, but they may be present beforehand. Likewise, uninstallation does not actually delete any files; this is done automatically by running a garbage collector. User environments are manipulated through the nix-env command. The query operation can be used to see what packages are currently installed. $ nix-env -q MozillaFirebird-0.7 sylpheed-0.9.7 pan-0.14.2 ( is actually short for .) The package names are symbolic: they don't have any particular significance to Nix (as they shouldn't, since they are not unique—there can be many derivations with the same name). Note that these packages have many dependencies (e.g., Mozilla uses the gtk+ package) but these have not been installed in the user environment, though they are present on the system. Generally, there is no need to install such packages; only packages containing programs should be installed. To install packages, a Nix expression is required that tells Nix how to build that package. There is a set of standard of Nix expressions for many common packages. Assuming that you have downloaded and unpacked these, you can view the set of available packages: $ nix-env -qaf pkgs/system/i686-linux.nix gettext-0.12.1 sylpheed-0.9.7 aterm-2.0 gtk+-1.2.10 apache-httpd-2.0.48 pan-0.14.2 ... The Nix expression in the file i686-linux.nix yields the set of packages for a Linux system running on x86 hardware. For other platforms, copy and modify this file for your platform as appropriate. [TODO: improve this] It is also possible to see the status of available packages, i.e., whether they are installed into the user environment and/or present in the system: $ nix-env -qasf pkgs/system/i686-linux.nix -P gettext-0.12.1 IP sylpheed-0.9.7 -- aterm-2.0 -P gtk+-1.2.10 This reveals that the sylpheed package is already installed, or more precisely, that exactly the same instantiation of sylpheed is installed. This guarantees that the available package is exactly the same as the installed package with regard to sources, dependencies, build flags, and so on. Similarly, we see that the gettext and gtk+ packages are present but not installed in the user environment, while the aterm package is not installed or present at all (so, if we were to install it, it would have to be built or downloaded first). The install operation is used install available packages from a Nix environment. To install the pan package (a newsreader), you would do: $ nix-env -if pkgs/system/i686-linux.nix pan Since installation may take a long time, depending on whether any packages need to be built or downloaded, it's a good idea to make nix-env run verbosely by using the () option. This option may be repeated to increase the level of verbosity. A good value is 3 (). In fact, if you run this command verbosely you will observe that Nix starts to build many packages, including large and fundamental ones such as glibc and gcc. I.e., you are performing a source installation. This is generally undesirable, since installation from sources may require large amounts of disk and CPU resources. Therefore a binary installation is generally preferable. Rather than provide different mechanisms to create and perform the installation of binary packages, Nix supports binary deployment transparently through a generic mechanism of substitute expressions. If an request is made to build some Nix expression, Nix will first try to build any substitutes for that expression. These substitutes presumably perform an identical build operation with respect to the result, but require less resources. For instance, a substitute that downloads a pre-built package from the network requires less CPU and disk resources, and possibly less time. Nix's use of cryptographic hashes makes this entirely safe. It is not possible, for instance, to accidentally substitute a build of some package for a Solaris or Windows system for a build on a SuSE/x86 system. While the substitute mechanism is a generic mechanism, Nix provides two standard tools called nix-pull and nix-push that maintain and use a shared cache of prebuilt derivations on some network site (reachable through HTTP). If you attempt to install some package that someone else has previously built and pushed into the cache, and you have done a pull to register substitutes that download these prebuilt packages, then the installation will automatically use these. For example, to pull from our cache of prebuilt packages (at the time of writing, for SuSE Linux/x86), use the following command: $ nix-pull http://catamaran.labs.cs.uu.nl/~eelco/nix/nixpkgs-version/ obtaining list of Nix archives at http://catamaran.labs.cs.uu.nl/~eelco/nix/nixpkgs-version... ... If nix-pull is run without any arguments, it will pull from the URLs specified in the file prefix/etc/nix/prebuilts.conf. Assuming that the pan installation produced no errors, it can be used immediately, that is, it now appears in a directory in the PATH environment variable. Specifically, PATH includes the entry prefix/var/nix/links/current/bin, where prefix/var/nix/links/current is just a symlink to the current user environment: $ ls -l /nix/var/nix/links/ ... lrwxrwxrwx 1 eelco ... 15 -> /nix/store/1871...12b0-user-environment lrwxrwxrwx 1 eelco ... 16 -> /nix/store/59ba...df6b-user-environment lrwxrwxrwx 1 eelco ... current -> /nix/var/nix/links/16 That is, current in this example is a link to 16, which is the current user environment. Before the installation, it pointed to 15. Note that this means that you can atomically roll-back to the previous user environment by pointing the symlink current at 15 again. This also shows that operations such as installation are atomic in the Nix system: any arbitrarily complex set of installation, uninstallation, or upgrade actions eventually boil down to the single operation of pointing a symlink somewhere else (which can be implemented atomically in Unix). What's in a user environment? It's just a set of symlinks to the files that constitute the installed packages. For instance: $ ls -l /nix/var/nix/links/16/bin lrwxrwxrwx 1 eelco ... MozillaFirebird -> /nix/store/35f8...4ae6-MozillaFirebird-0.7/bin/MozillaFirebird lrwxrwxrwx 1 eelco ... svn -> /nix/store/3829...fb5d-subversion-0.32.1/bin/svn ... Note that, e.g., svn = /nix/var/nix/links/current/bin/svn = /nix/var/nix/links/16/bin/svn = /nix/store/59ba...df6b-user-environment/bin/svn = /nix/store/3829...fb5d-subversion-0.32.1/bin/svn. Naturally, packages can also be uninstalled: $ nix-env -e pan This means that the package is removed from the user environment. It is not yet removed from the system. When a package is uninstalled from a user environment, it may still be used by other packages, or may still be present in other user environments. Deleting it under such conditions would break those other packages or user environments. To prevent this, packages are only physically deleted by running the Nix garbage collector, which searches for all packages in the Nix store that are no longer reachable from outside the store. Thus, uninstalling a package is always safe: it cannot break other packages. Upgrading packages is easy. Given a Nix expression that contains newer versions of installed packages (that is, packages with the same package name, but a higher version number), nix-env -u will replace the installed package in the user environment with the newer package. For example, $ nix-env -uf pkgs/system/i686-linux.nix pan looks for a newer version of Pan, and installs it if found. Also useful is the ability to upgrade all packages: $ nix-env -uf pkgs/system/i686-linux.nix '*' The asterisk matches all installed packagesNo, we don't support arbitrary regular expressions. Note that * must be quoted to prevent shell globbing. Writing Nix expressions A simple Nix expression This section shows how to write simple Nix expressions—the things that describe how to build a package. Nix expression for GNU Hello {stdenv, fetchurl, perl}: derivation { name = "hello-2.1.1"; system = stdenv.system; builder = ./builder.sh; src = fetchurl { url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz; md5 = "70c9ccf9fac07f762c24f2df2290784d"; }; stdenv = stdenv; perl = perl; } A simple Nix expression is shown in . It describes how to the build the GNU Hello package. This package has several dependencies. First, it requires a number of other packages, such as a C compiler, standard Unix shell tools, and Perl. Rather than have this Nix expression refer to and use specific versions of these packages, it should be generic; that is, it should be a function that takes the required packages as inputs and yield a build of the GNU Hello package as a result. This Nix expression defines a function with three arguments , namely: stdenv, which should be a standard environment package. The standard environment is a set of tools and other components that would be expected in a fairly minimal Unix-like environment: a C compiler and linker, Unix shell tools, and so on. fetchurl, which should be a function that given parameters url and md5, will fetch a file from the specified location and check that this file has the given MD5 hash code. The hash is required because build operations must be pure: given the same inputs they should always yield the same output. Since network resources can change at any time, we must in some way guarantee what the result will be. perl, which should be a Perl interpreter. The remainder of the file is the body of the function, which happens to be a derivation , which is the built-in function derivation applied to a set of attributes that encode all the necessary information for building the GNU Hello package. Build script (<filename>builder.sh</filename>) for GNU Hello #! /bin/sh buildinputs="$perl" . $stdenv/setup || exit 1 tar xvfz $src || exit 1 cd hello-* || exit 1 ./configure --prefix=$out || exit 1 make || exit 1 make install || exit 1 A more complex Nix expression Nix expression for Subversion { localServer ? false , httpServer ? false , sslSupport ? false , swigBindings ? false , stdenv, fetchurl , openssl ? null, httpd ? null, db4 ? null, expat, swig ? null }: assert !isNull expat; assert localServer -> !isNull db4; assert httpServer -> !isNull httpd && httpd.expat == expat; assert sslSupport -> !isNull openssl && (httpServer -> httpd.openssl == openssl); assert swigBindings -> !isNull swig; derivation { name = "subversion-0.32.1"; system = stdenv.system; builder = ./builder.sh; src = fetchurl { url = http://svn.collab.net/tarballs/subversion-0.32.1.tar.gz; md5 = "b06717a8ef50db4b5c4d380af00bd901"; }; localServer = localServer; httpServer = httpServer; sslSupport = sslSupport; swigBindings = swigBindings; stdenv = stdenv; openssl = if sslSupport then openssl else null; httpd = if httpServer then httpd else null; expat = expat; db4 = if localServer then db4 else null; swig = if swigBindings then swig else null; } This example shows several features. Default parameters can be used to simplify call sites: if an argument that has a default is omitted, its default value is used. You can use assertions to test whether arguments satisfy certain constraints. The simple assertion tests whether the expat argument is not a null value. The more complex assertion says that if Subversion is built with Apache support, then httpd (the Apache package) must not be null and it must have been built using the same instance of the expat library as was passed to the Subversion expression. This is since the Subversion code is dynamically linked against the Apache code and they both use Expat, they must be linked against the same instance—otherwise a conflict might occur.