Table of Contents
List of Figures
List of Tables
List of Examples
default.nix)builder.sh)all-packages.nix)toXMLtoXMLremote-systems.confNix is a purely functional package manager.
This means that it treats packages like values in purely functional
programming languages such as Haskell — they are built by functions
that don’t have side-effects, and they never change after they have
been built. Nix stores packages in the Nix
store, usually the directory
/nix/store, where each package has its own unique
subdirectory such as
/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/
where r8vvq9kq… is a unique identifier for the
package that captures all its dependencies (it’s a cryptographic hash
of the package’s build dependency graph). This enables many powerful
features.
You can have multiple versions or variants of a package installed at the same time. This is especially important when different applications have dependencies on different versions of the same package — it prevents the “DLL hell”. Because of the hashing scheme, different versions of a package end up in different paths in the Nix store, so they don’t interfere with each other.
An important consequence is that operations like upgrading or uninstalling an application cannot break other applications, since these operations never “destructively” update or delete files that are used by other packages.
Nix helps you make sure that package dependency specifications are complete. In general, when you’re making a package for a package management system like RPM, you have to specify for each package what its dependencies are, but there are no guarantees that this specification is complete. If you forget a dependency, then the package will build and work correctly on your machine if you have the dependency installed, but not on the end user's machine if it's not there.
Since Nix on the other hand doesn’t install packages in “global”
locations like /usr/bin but in package-specific
directories, the risk of incomplete dependencies is greatly reduced.
This is because tools such as compilers don’t search in per-packages
directories such as
/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include,
so if a package builds correctly on your system, this is because you
specified the dependency explicitly.
Runtime dependencies are found by scanning binaries for the hash
parts of Nix store paths (such as r8vvq9kq…). This
sounds risky, but it works extremely well.
Starting at version 0.11, Nix has multi-user support. This
means that non-privileged users can securely install software. Each
user can have a different profile, a set of
packages in the Nix store that appear in the user’s
PATH. If a user installs a package that another user
has already installed previously, the package won’t be built or
downloaded a second time. At the same time, it is not possible for
one user to inject a Trojan horse into a package that might be used by
another user.
Since package management operations never overwrite packages in the Nix store but just add new versions in different paths, they are atomic. So during a package upgrade, there is no time window in which the package has some files from the old version and some files from the new version — which would be bad because a program might well crash if it’s started during that period.
And since package aren’t overwritten, the old versions are still there after an upgrade. This means that you can roll back to the old version:
$ nix-env --upgrade some-packages
$ nix-env --rollback
When you install a package like this…
$ nix-env --uninstall firefox
the package isn’t deleted from the system right away (after all, you might want to do a rollback, or it might be in the profiles of other users). Instead, unused packages can be deleted safely by running the garbage collector:
$ nix-collect-garbage
This deletes all packages that aren’t in use by any user profile or by a currently running program.
Packages are built from Nix expressions, which is a simple functional language. A Nix expression describes everything that goes into a package build action (a “derivation”): other packages, sources, the build script, environment variables for the build script, etc. Nix tries very hard to ensure that Nix expressions are deterministic: building a Nix expression twice should yield the same result.
Because it’s a functional language, it’s easy to support building variants of a package: turn the Nix expression into a function and call it any number of times with the appropriate arguments. Due to the hashing scheme, variants don’t conflict with each other in the Nix store.
Nix expressions generally describe how to build a package from source, so an installation action like
$ nix-env --install firefox
could cause quite a bit of build activity, as not only Firefox but also all its dependencies (all the way up to the C library and the compiler) would have to built, at least if they are not already in the Nix store. This is a source deployment model. For most users, building from source is not very pleasant as it takes far too long. However, Nix can automatically skip building from source and download a pre-built binary instead if it knows about it. Nix channels provide Nix expressions along with pre-built binaries.
In addition to downloading binaries automatically if they’re available, Nix can download binary deltas that patch an existing package in the Nix store into a new version. This speeds up upgrades.
We provide a large set of Nix expressions containing hundreds of existing Unix packages, the Nix Packages collection (Nixpkgs).
Nix can be used not only for rolling out packages, but also complete configurations of services. This is done by treating all the static bits of a service (such as software packages, configuration files, control scripts, static web pages, etc.) as “packages” that can be built by Nix expressions. As a result, all the features above apply to services as well: for instance, you can roll back a web server configuration if a configuration change turns out to be undesirable, you can easily have multiple instances of a service (e.g., a test and production server), and because the whole service is built in a purely functional way from a Nix expression, it is repeatable so you can easily reproduce the service on another machine.
Nix should run on most Unix systems, including Linux, FreeBSD and Mac OS X. It is also supported on Windows using Cygwin.
NixOS is a Linux distribution based on Nix. It uses Nix not
just for package management but also to manage the system
configuration (e.g., to build configuration files in
/etc). This means, among other things, that it’s
possible to easily roll back the entire configuration of the system to
an earlier state. Also, users can install software without root
privileges. For more information and downloads, see the NixOS homepage.
Nix was developed at the Department of Information and Computing Sciences, Utrecht University by the TraCE project. The project is funded by the Software Engineering Research Program Jacquard to improve the support for variability in software systems.
This manual tells you how to install and use Nix and how to write Nix expressions for software not already in the Nix Packages collection. It also discusses some advanced topics, such as setting up a Nix-based build farm.
Nix is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. Nix is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
Some background information on Nix can be found in a number of papers. The ICSE 2004 paper Imposing a Memory Management Discipline on Software Deployment discusses the hashing mechanism used to ensure reliable dependency identification and non-interference between different versions and variants of packages. The LISA 2004 paper Nix: A Safe and Policy-Free System for Software Deployment gives a more general discussion of Nix from a system-administration perspective. The CBSE 2005 paper Efficient Upgrading in a Purely Functional Component Deployment Model is about transparent patch deployment in Nix. The SCM-12 paper Service Configuration Management shows how services (e.g., web servers) can be deployed and managed through Nix. A short overview of NixOS is given in the HotOS XI paper Purely Functional System Configuration Management. The Nix homepage has an up-to-date list of Nix-related papers.
Nix is the subject of Eelco Dolstra’s PhD thesis The Purely Functional Software Deployment Model, which contains most of the papers listed above.
Nix has a homepage at http://nix.cs.uu.nl/.
This chapter is for impatient people who don't like reading documentation. For more in-depth information you are kindly referred to the following chapters.
Download a source tarball or RPM from http://nix.cs.uu.nl/. Build source distributions using the regular sequence:
$ tar xvfj nix-version.tar.bz2
$ ./configure
$ make
$ make install (as root)
This will install Nix in /nix. You shouldn't
change the prefix if at all possible since that will make it
impossible to use pre-built binaries from the Nixpkgs channel and
other channels. Alternatively, you could grab an RPM if you're on an
RPM-based system. You should also add
/nix/etc/profile.d/nix.sh to your
~/.bashrc (or some other login
file).
Subscribe to the Nix Packages channel.
$ nix-channel --add \
http://nix.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable
Download the latest Nix expressions available in the channel.
$ nix-channel --update
Note that this in itself doesn't download any packages, it just
downloads the Nix expressions that build them and stores them
somewhere (under ~/.nix-defexpr, in case you're
curious). Also, it registers the fact that pre-built binaries are
available remotely.
See what installable packages are currently available in the channel:
$ nix-env -qa ’*’ (mind the quotes!)
docbook-xml-4.2
firefox-1.0pre-PR-0.10.1
hello-2.1.1
libxslt-1.1.0
...
Install some packages from the channel:
$ nix-env -i hello firefox ... This should download pre-built packages; it should not build them locally (if it does, something went wrong).
Test that they work:
$ which hello
/home/eelco/.nix-profile/bin/hello
$ hello
Hello, world!
$ firefox
(read Slashdot or something)
Uninstall a package:
$ nix-env -e hello
To keep up-to-date with the channel, do:
$ nix-channel --update $ nix-env -u '*'
The latter command will upgrade each installed package for which there is a “newer” version (as determined by comparing the version numbers).
You can also install specific packages directly from
your web browser. For instance, you can go to http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/
and click on any link for the individual packages for your platform.
Associate application/nix-package with the program
/nix/bin/nix-install-package. A window should
appear asking you whether it’s okay to install the package. Say
Y. The package and all its dependencies will be
installed.
If you're unhappy with the result of a nix-env action (e.g., an upgraded package turned out not to work properly), you can go back:
$ nix-env --rollback
You should periodically run the Nix garbage collector to get rid of unused packages, since uninstalls or upgrades don't actually delete them:
$ nix-collect-garbage -d
Table of Contents
Nix is currently supported on the following platforms:
Linux (particularly on x86, x86_64, and PowerPC).
Mac OS X, both on Intel and PowerPC.
FreeBSD (only tested on Intel).
Windows through Cygwin.
On Cygwin, Nix must be installed on an NTFS partition. It will not work correctly on a FAT partition.
Nix is pretty portable, so it should work on most other Unix platforms as well.
The easiest way to obtain Nix is to download a source distribution. RPMs for Red Hat, SuSE, and Fedora Core are also available.
Alternatively, the most recent sources of Nix can be obtained
from its Subversion
repository. For example, the following command will check out
the latest revision into a directory called
nix:
$ svn checkout https://svn.cs.uu.nl:12443/repos/trace/nix/trunk nix
Likewise, specific releases can be obtained from the tags directory of the repository. If you don't have Subversion, you can also download an automatically generated compressed tar-file of the head revision of the trunk.
The following prerequisites only apply when you build from source. Binary releases (e.g., RPMs) have no prerequisites.
A fairly recent version of GCC/G++ is required. Version 2.95 and higher should work.
To build this manual and the man-pages you need the
xmllint and xsltproc programs,
which are part of the libxml2 and
libxslt packages, respectively. You also need the
DocBook XSL
stylesheets and optionally the DocBook 5.0 RELAX NG
schemas. Note that these are only required if you modify the
manual sources or when you are building from the Subversion
repository.
To build the parser, very recent versions of Bison and Flex are required. (This is because Nix needs GLR support in Bison and reentrancy support in Flex.) For Bison, you need version 2.3 or higher (1.875 does not work), which can be obtained from the GNU FTP server. For Flex, you need version 2.5.33, which is available on SourceForge. Slightly older versions may also work, but ancient versions like the ubiquitous 2.5.4a won't. Note that these are only required if you modify the parser or when you are building from the Subversion repository.
Nix uses Sleepycat's Berkeley DB, CWI's ATerm library and the
bzip2 compressor (including the bzip2 library). These are included in
the Nix source distribution. If you build from the Subversion
repository, you must download them yourself and place them in the
externals/ directory. See
externals/Makefile.am for the precise URLs of
these packages. Alternatively, if you already have them installed,
you can use configure's
--with-bdb, --with-aterm and
--with-bzip2 options to point to their respective
locations. Note that Berkeley DB must be version
4.5; other versions may not have compatible database formats.
After unpacking or checking out the Nix sources, issue the following commands:
$ ./configure options...
$ make
$ make install
When building from the Subversion repository, these should be preceded by the command:
$ ./bootstrap
The installation path can be specified by passing the
--prefix= to
configure. The default installation directory is
prefix/nix. You can change this to any location you
like. You must have write permission to the
prefix path.
It is best not to change the installation prefix from its default, since doing so makes it impossible to use pre-built binaries from the standard Nixpkgs channels.
If you want to rebuilt the documentation, pass the full path to
the DocBook RELAX NG schemas and to the DocBook XSL stylesheets using
the
--with-docbook-rng=
and
path--with-docbook-xsl=
options.path
RPM packages of Nix can be downloaded from http://nix.cs.uu.nl/. These RPMs should work for most
fairly recent releases of SuSE and Red Hat Linux. They have been
known to work work on SuSE Linux 8.1 and 9.0, and Red Hat 9.0. In
fact, it should work on any RPM-based Linux distribution based on
glibc 2.3 or later.
Once downloaded, the RPMs can be installed or upgraded using rpm -U. For example,
$ rpm -U nix-0.5pre664-1.i386.rpm
The RPMs install into the directory /nix.
Nix can be uninstalled using rpm -e nix. After
this it will be necessary to manually remove the Nix store and other
auxiliary data:
$ rm -rf /nix/store $ rm -rf /nix/var
You can install the latest stable version of Nix through Nix itself by subscribing to the channel http://nix.cs.uu.nl/dist/nix/channels-v3/nix-stable, or the latest unstable version by subscribing to the channel http://nix.cs.uu.nl/dist/nix/channels-v3/nix-unstable. You can also do a one-click installation by clicking on the package links at http://nix.cs.uu.nl/dist/nix/.
Nix has two basic security models. First, it can be used in
“single-user mode”, which is similar to what most other package
management tools do: there is a single user (typically root) who performs all package
management operations. All other users can then use the installed
packages, but they cannot perform package management operations
themselves.
Alternatively, you can configure Nix in “multi-user mode”. In this model, all users can perform package management operations — for instance, every user can install software without requiring root privileges. Nix ensures that this is secure. For instance, it’s not possible for one user to overwrite a package used by another user with a Trojan horse.
In single-user mode, all Nix operations that access the database
in
or modify the Nix store in
prefix/var/nix/db must be
performed under the user ID that owns those directories. This is
typically prefix/storeroot. (If you
install from RPM packages, that’s in fact the default ownership.)
However, on single-user machines, it is often convenient to
chown those directories to your normal user account
so that you don’t have to su to root all the time.
To allow a Nix store to be shared safely among multiple users, it is important that users are not able to run builders that modify the Nix store or database in arbitrary ways, or that interfere with builds started by other users. If they could do so, they could install a Trojan horse in some package and compromise the accounts of other users.
To prevent this, the Nix store and database are owned by some
privileged user (usually root) and builders are
executed under special user accounts (usually named
nixbld1, nixbld2, etc.). When a
unprivileged user runs a Nix command, actions that operate on the Nix
store (such as builds) are forwarded to a Nix
daemon running under the owner of the Nix store/database
that performs the operation.
Multi-user mode has one important limitation: only
root can run nix-pull to register the availability
of pre-built binaries. However, those registrations are shared by all
users, so they still get the benefit from nix-pulls
done by root.
The build users are the special UIDs under
which builds are performed. They should all be members of the
build users group (usually called
nixbld). This group should have no other members.
The build users should not be members of any other group.
Here is a typical /etc/group definition of
the build users group with 10 build users:
nixbld:!:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10
In this example the nixbld group has UID 30000, but
of course it can be anything that doesn’t collide with an existing
group.
Here is the corresponding part of
/etc/passwd:
nixbld1:x:30001:65534:Nix build user 1:/var/empty:/noshell nixbld2:x:30002:65534:Nix build user 2:/var/empty:/noshell nixbld3:x:30003:65534:Nix build user 3:/var/empty:/noshell ... nixbld10:x:30010:65534:Nix build user 10:/var/empty:/noshell
The home directory of the build users should not exist or should be an empty directory to which they do not have write access.
The build users should have write access to the Nix store, but
they should not have the right to delete files. Thus the Nix store’s
group should be the build users group, and it should have the sticky
bit turned on (like /tmp):
$ chgrp nixbld /nix/store $ chmod 1777 /nix/store
Finally, you should tell Nix to use the build users by
specifying the build users group in the build-users-group
option in the Nix configuration
file (/nix/etc/nix/nix.conf):
build-users-group = nixbld
The simplest setup is to let root own the Nix
store and database. I.e.,
$ chown -R root /nix/store /nix/var/nix
The Nix daemon should be started as follows (as
root):
$ nix-worker --daemon
You’ll want to put that line somewhere in your system’s boot scripts.
To let unprivileged users use the daemon, they should set the
NIX_REMOTE environment
variable to daemon. So you should put a
line like
export NIX_REMOTE=daemon
into the users’ login scripts.
It is also possible to let the Nix store and database be owned
by a non-root user, which should be more secure[1]. Typically, this user
is a special account called nix, but it can be
named anything. It should own the Nix store and database:
$ chown -R root /nix/store /nix/var/nix
and of course nix-worker --daemon should be started under that user, e.g.,
$ su - nix -c "exec /nix/bin/nix-worker --daemon"
There is a catch, though: non-root users
cannot start builds under the build user accounts, since the
setuid system call is obviously privileged. To
allow a non-root Nix daemon to use the build user
feature, it calls a setuid-root helper program,
nix-setuid-helper. This program is installed in
.
To set the permissions properly (Nix’s make install
doesn’t do this, since we don’t want to ship setuid-root programs
out-of-the-box):
prefix/libexec/nix-setuid-helper
$ chown root.root /nix/libexec/nix-setuid-helper $ chmod 4755 /nix/libexec/nix-setuid-helper
(This example assumes that the Nix binaries are installed in
/nix.)
Of course, the nix-setuid-helper command
should not be usable by just anybody, since then anybody could run
commands under the Nix build user accounts. For that reason there is
a configuration file /etc/nix-setuid.conf that
restricts the use of the helper. This file should be a text file
containing precisely two lines, the first being the Nix daemon user
and the second being the build users group, e.g.,
nix nixbld
The setuid-helper barfs if it is called by a user other than the one
specified on the first line, or if it is asked to execute a build
under a user who is not a member of the group specified on the second
line. The file /etc/nix-setuid.conf must be
owned by root, and must not be group- or world-writable. The
setuid-helper barfs if this is not the case.
To limit which users can perform Nix operations, you can use the
permissions on the directory
/nix/var/nix/daemon-socket. For instance, if you
want to restrict the use of Nix to the members of a group called
nix-users, do
$ chgrp nix-users /nix/var/nix/daemon-socket $ chmod ug=rwx,o= /nix/var/nix/daemon-socket
This way, users who are not in the nix-users group
cannot connect to the Unix domain socket
/nix/var/nix/daemon-socket/socket, so they cannot
perform Nix operations.
To use Nix, some environment variables should be set. In
particular, PATH should contain the directories
and
prefix/bin~/.nix-profile/bin. The first directory contains
the Nix tools themselves, while ~/.nix-profile is
a symbolic link to the current user environment
(an automatically generated package consisting of symlinks to
installed packages). The simplest way to set the required environment
variables is to include the file
in your prefix/etc/profile.d/nix.sh~/.bashrc (or similar), like this:
source prefix/etc/profile.d/nix.sh[1] Note
however that even when the Nix daemon runs as root, not
that much code is executed as root: Nix
expression evaluation is performed by the calling (unprivileged) user,
and builds are performed under the special build user accounts. So
only the code that accesses the database and starts builds is executed
as root.
Table of Contents
This chapter discusses how to do package management with Nix, i.e., how to obtain, install, upgrade, and erase packages. This is the “user’s” perspective of the Nix system — people who want to create packages should consult Chapter 5, Writing Nix Expressions.
The main command for package management is nix-env. You can use it to install, upgrade, and erase packages, and to query what packages are installed or are available for installation.
In Nix, different users can have different “views”
on the set of installed applications. That is, there might be lots of
applications present on the system (possibly in many different
versions), but users can have a specific selection of those active —
where “active” just means that it appears in a directory
in the user’s PATH. Such a view on the set of
installed applications is called a user
environment, which is just a directory tree consisting of
symlinks to the files of the active applications.
Components are installed from a set of Nix expressions that tell Nix how to build those packages, including, if necessary, their dependencies. There is a collection of Nix expressions called the Nix Package collection that contains packages ranging from basic development stuff such as GCC and Glibc, to end-user applications like Mozilla Firefox. (Nix is however not tied to the Nix Package collection; you could write your own Nix expressions based on it, or completely new ones.) You can download the latest version from http://nix.cs.uu.nl/dist/nix.
Assuming that you have downloaded and unpacked a release of Nix Packages, you can view the set of available packages in the release:
$ nix-env -qaf nixpkgs-version '*'
ant-blackdown-1.4.2
aterm-2.2
bash-3.0
binutils-2.15
bison-1.875d
blackdown-1.4.2
bzip2-1.0.2
...
where nixpkgs- is
where you’ve unpacked the release. The flag version-q
specifies a query operation; -a means that you want
to show the “available” (i.e., installable) packages, as opposed to
the installed packages; and -f
nixpkgs-
specifies the source of the packages. The argument
version'*' shows all installable packages. (The quotes are
necessary to prevent shell expansion.) You can also select specific
packages by name:
$ nix-env -qaf nixpkgs-version gcc
gcc-3.4.6
gcc-4.0.3
gcc-4.1.1
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 nixpkgs-version '*'
...
-PS bash-3.0
--S binutils-2.15
IPS bison-1.875d
...
The first character (I) indicates whether the
package is installed in your current user environment. The second
(P) indicates whether it is present on your system
(in which case installing it into your user environment would be a
very quick operation). The last one (S) indicates
whether there is a so-called substitute for the
package, which is Nix’s mechanism for doing binary deployment. It
just means that Nix knows that it can fetch a pre-built package from
somewhere (typically a network server) instead of building it
locally.
So now that we have a set of Nix expressions we can build the
packages contained in them. This is done using nix-env
-i. For instance,
$ nix-env -f nixpkgs-version -i subversion
will install the package called subversion (which
is, of course, the Subversion version
management system).
When you do this for the first time, Nix will start building Subversion and all its dependencies. This will take quite a while — typically an hour or two on modern machines. Fortunately, there is a faster way (so do a Ctrl-C on that install operation!): you just need to tell Nix that pre-built binaries of all those packages are available somewhere. This is done using the nix-pull command, which must be supplied with a URL containing a manifest describing what binaries are available. This URL should correspond to the Nix Packages release that you’re using. For instance, if you obtained a release from http://nix.cs.uu.nl/dist/nix/nixpkgs-0.6pre1554/, then you should do:
$ nix-pull http://nix.cs.uu.nl/dist/nix/nixpkgs-0.6pre1554/MANIFEST
If you then issue the installation command, it should start
downloading binaries from nix.cs.uu.nl, instead of building
them from source. This might still take a while since all
dependencies must be downloaded, but on a reasonably fast connection
such as an DSL line it’s on the order of a few minutes.
Naturally, packages can also be uninstalled:
$ nix-env -e subversion
Upgrading to a new version is just as easy. If you have a new release of Nix Packages, you can do:
$ nix-env -f nixpkgs-version -u subversion
This will only upgrade Subversion if there is a
“newer” version in the new set of Nix expressions, as
defined by some pretty arbitrary rules regarding ordering of version
numbers (which generally do what you’d expect of them). To just
unconditionally replace Subversion with whatever version is in the Nix
expressions, use -i instead of
-u; -i will remove
whatever version is already installed.
You can also upgrade all packages for which there are newer versions:
$ nix-env -f nixpkgs-version -u '*'
Sometimes it’s useful to be able to ask what
nix-env would do, without actually doing it. For
instance, to find out what packages would be upgraded by
nix-env -u '*', you can do
$ nix-env ... -u '*' --dry-run (dry run; not doing anything) upgrading `libxslt-1.1.0' to `libxslt-1.1.10' upgrading `graphviz-1.10' to `graphviz-1.12' upgrading `coreutils-5.0' to `coreutils-5.2.1'
If you grow bored of specifying the Nix expressions using
-f all the time, you can set a default
location:
$ nix-env -I nixpkgs-version
After this you can just say, for instance, nix-env -u
'*'.[2]
Profiles and user environments are Nix’s mechanism for
implementing the ability to allow different users to have different
configurations, and to do atomic upgrades and rollbacks. To
understand how they work, it’s useful to know a bit about how Nix
works. In Nix, packages are stored in unique locations in the
Nix store (typically,
/nix/store). For instance, a particular version
of the Subversion package might be stored in a directory
/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/,
while another version might be stored in
/nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2.
The long strings prefixed to the directory names are cryptographic
hashes[3] of
all inputs involved in building the package —
sources, dependencies, compiler flags, and so on. So if two
packages differ in any way, they end up in different locations in
the file system, so they don’t interfere with each other. Figure 4.1, “User environments” shows a part of a typical Nix
store.
Of course, you wouldn’t want to type
$ /nix/store/dpmvp969yhdq...-subversion-1.1.3/bin/svn
every time you want to run Subversion. Of course we could set up the
PATH environment variable to include the
bin directory of every package we want to use,
but this is not very convenient since changing PATH
doesn’t take effect for already existing processes. The solution Nix
uses is to create directory trees of symlinks to
activated packages. These are called
user environments and they are packages
themselves (though automatically generated by
nix-env), so they too reside in the Nix store. For
instance, in Figure 4.1, “User environments” the user
environment /nix/store/5mq2jcn36ldl...-user-env
contains a symlink to just Subversion 1.1.2 (arrows in the figure
indicate symlinks). This would be what we would obtain if we had done
$ nix-env -i subversion
on a set of Nix expressions that contained Subversion 1.1.2.
This doesn’t in itself solve the problem, of course; you
wouldn’t want to type
/nix/store/0c1p5z4kda11...-user-env/bin/svn
either. That’s why there are symlinks outside of the store that point
to the user environments in the store; for instance, the symlinks
default-42-link and
default-43-link in the example. These are called
generations since every time you perform a
nix-env operation, a new user environment is
generated based on the current one. For instance, generation 43 was
created from generation 42 when we did
$ nix-env -i subversion mozilla
on a set of Nix expressions that contained Mozilla and a new version of Subversion.
Generations are grouped together into profiles so that different users don’t interfere with each other if they don’t want to. For example:
$ ls -l /nix/var/nix/profiles/ ... lrwxrwxrwx 1 eelco ... default-42-link -> /nix/store/0c1p5z4kda11...-user-env lrwxrwxrwx 1 eelco ... default-43-link -> /nix/store/3aw2pdyx2jfc...-user-env lrwxrwxrwx 1 eelco ... default -> default-43-link
This shows a profile called default. The file
default itself is actually a symlink that points
to the current generation. When we do a nix-env
operation, a new user environment and generation link are created
based on the current one, and finally the default
symlink is made to point at the new generation. This last step is
atomic on Unix, which explains how we can do atomic upgrades. (Note
that the building/installing of new packages doesn’t interfere in
any way with old packages, since they are stored in different
locations in the Nix store.)
If you find that you want to undo a nix-env operation, you can just do
$ nix-env --rollback
which will just make the current generation link point at the previous
link. E.g., default would be made to point at
default-42-link. You can also switch to a
specific generation:
$ nix-env --switch-generation 43
which in this example would roll forward to generation 43 again. You can also see all available generations:
$ nix-env --list-generations
Actually, there is another level of indirection not shown in the
figure above. You generally wouldn’t have
/nix/var/nix/profiles/
in your some-profile/binPATH. Rather, there is a symlink
~/.nix-profile that points to your current
profile. This means that you should put
~/.nix-profile/bin in your PATH
(and indeed, that’s what the initialisation script
/nix/etc/profile.d/nix.sh does). This makes it
easier to switch to a different profile. You can do that using the
command nix-env --switch-profile:
$ nix-env --switch-profile /nix/var/nix/profiles/my-profile $ nix-env --switch-profile /nix/var/nix/profiles/default
These commands switch to the my-profile and
default profile, respectively. If the profile doesn’t exist, it will
be created automatically. You should be careful about storing a
profile in another location than the profiles
directory, since otherwise it might not be used as a root of the
garbage collector (see section Section 4.3, “Garbage collection”).
All nix-env operations work on the profile
pointed to by ~/.nix-profile, but you can override
this using the --profile option (abbreviation
-p):
$ nix-env -p /nix/var/nix/profiles/other-profile -i subversion
This will not change the ~/.nix-profile symlink.
nix-env operations such as upgrades
(-u) and uninstall (-e) never
actually delete packages from the system. All they do (as shown
above) is to create a new user environment that no longer contains
symlinks to the “deleted” packages.
Of course, since disk space is not infinite, unused packages should be removed at some point. You can do this by running the Nix garbage collector. It will remove from the Nix store any package not used (directly or indirectly) by any generation of any profile.
Note however that as long as old generations reference a package, it will not be deleted. After all, we wouldn’t be able to do a rollback otherwise. So in order for garbage collection to be effective, you should also delete (some) old generations. Of course, this should only be done if you are certain that you will not need to roll back.
To delete all old (non-current) generations of your current profile:
$ nix-env --delete-generations old
Instead of old you can also specify a list of
generations, e.g.,
$ nix-env --delete-generations 10 11 14
After removing appropriate old generations you can run the garbage collector as follows:
$ nix-store --gc
If you are feeling uncertain, you can also first view what files would be deleted:
$ nix-store --gc --print-dead
Likewise, the option --print-live will show the paths
that won’t be deleted.
There is also a convenient little utility
nix-collect-garbage, which when invoked with the
-d (--delete-old) switch deletes all
old generations of all profiles in
/nix/var/nix/profiles. So
$ nix-collect-garbage -d
is a quick and easy way to clean up your system.
The roots of the garbage collector are all store paths to which
there are symlinks in the directory
.
For instance, the following command makes the path
prefix/nix/var/nix/gcroots/nix/store/d718ef...-foo a root of the collector:
$ ln -s /nix/store/d718ef...-foo /nix/var/nix/gcroots/bar
That is, after this command, the garbage collector will not remove
/nix/store/d718ef...-foo or any of its
dependencies.
Subdirectories of
are also searched for symlinks. Symlinks to non-store paths are
followed and searched for roots, but symlinks to non-store paths
inside the paths reached in that way are not
followed to prevent infinite recursion.prefix/nix/var/nix/gcroots
If you want to stay up to date with a set of packages, it’s not very convenient to manually download the latest set of Nix expressions for those packages, use nix-pull to register pre-built binaries (if available), and upgrade using nix-env. Fortunately, there’s a better way: Nix channels.
A Nix channel is just a URL that points to a place that contains a set of Nix expressions and a manifest. Using the command nix-channel you can automatically stay up to date with whatever is available at that URL.
You can “subscribe” to a channel using nix-channel --add, e.g.,
$ nix-channel --add http://nix.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable
subscribes you to a channel that always contains that latest version
of the Nix Packages collection. (Instead of
nixpkgs-unstable you could also subscribe to
nixpkgs-stable, which should have a higher level of
stability, but right now is just outdated.) Subscribing really just
means that the URL is added to the file
~/.nix-channels. Right now there is no command
to “unsubscribe”; you should just edit that file manually
and delete the offending URL.
To obtain the latest Nix expressions available in a channel, do
$ nix-channel --update
This downloads the Nix expressions in every channel (downloaded from
)
and registers any available pre-built binaries in every channel
(by nix-pulling
url/nixexprs.tar.bz2). It also
makes the union of each channel’s Nix expressions the default for
nix-env operations. Consequently, you can then say
url/MANIFEST
$ nix-env -u '*'
to upgrade all packages in your profile to the latest versions available in the subscribed channels.
Often, when you want to install a specific package (e.g., from the Nix Packages collection or from our release server), subscribing to a channel is a bit cumbersome. And channels don’t help you at all if you want to install an older version of a package than the one provided by the current contents of the channel, or a package that has been removed from the channel. That’s when one-click installs come in handy: you can just go to the web page that contains the package, click on it, and it will be installed with all the necessary dependencies.
For instance, you can go to http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/
— or to any older release of Nix Packages — and click on any link for
the individual packages for your platform (say, subversion-1.4.0
for i686-linux). The first time you do
this, your browser will ask what to do with
application/nix-package files. You should open
them with /nix/bin/nix-install-package. This
will open a window that asks you to confirm that you want to install
the package. When you answer Y, the package and
all its dependencies will be installed. This is a binary deployment
mechanism — you get packages pre-compiled for the selected platform
type.
You can also install application/nix-package
files from the command line directly. See Section A.5.6, “nix-install-package” for details.
[2] Setting a default using
-I currently clashes with using Nix channels,
since nix-channel --update calls nix-env
-I to set the default to the Nix expressions it downloaded
from the channel, replacing whatever default you had
set.
[3] 160-bit truncations of SHA-256 hashes encoded in a base-32 notation, to be precise.
Table of Contents
This chapter shows you how to write Nix expressions, which are the things that tell Nix how to build packages. 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.
This 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 package to the Nix Packages collection, you generally need to do three things:
Write a Nix expression for the package. This is a file that describes all the inputs involved in building the package, such as dependencies, sources, and so on.
Write a builder. This is a shell script[4] that actually builds the package from the inputs.
Add the package to the file
pkgs/top-level/all-packages.nix. The Nix
expression written in the first step is a
function; it requires other packages 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
package.
Example 5.1, “Nix expression for GNU Hello
(default.nix)” 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: Nix functions generally have the form | |
So we have to build a package. 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
| |
The attribute | |
The attribute | |
The builder has to know what the sources of the package
are. Here, the attribute Instead of | |
Since the derivation requires Perl, we have to pass the
value of the perl = perl;
will do the trick: it binds an attribute |
Example 5.2, “Build script for GNU Hello
(builder.sh)” shows the builder referenced
from Hello's Nix expression (stored in
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:
When Nix runs a builder, it initially completely clears the
environment (except for the attributes declared in the
derivation). For instance, the So the first step is to set up the environment. This is
done by calling the | |
Since Hello needs Perl, we have to make sure that Perl is in
the | |
Now we have to unpack the sources. The
The whole build is performed in a temporary directory
created in | |
GNU Hello is a typical Autoconf-based package, so we first
have to run its | |
Finally we build Hello ( |
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 -e option,
which causes the script to be aborted if any command fails without an
error check.
The Nix expression in Example 5.1, “Nix expression for GNU Hello
(default.nix)” 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/top-level/all-packages.nix, where all
Nix expressions for packages are imported and called with the
appropriate arguments. Example 5.3, “Composing GNU Hello
(all-packages.nix)” shows
some fragments of
all-packages.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 packages 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 Example 5.1, “Nix expression for GNU Hello
( Note that we refer to
| |
This is where the actual composition takes place. Here we
call the function imported from
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
| |
Likewise, we have to instantiate Perl,
|
You can now try to build Hello. Of course, you could do
nix-env -f pkgs/top-level/all-packages.nix -i hello,
but you may not want to install a possibly broken package just yet.
The best way to test the package is by using the command nix-build, which builds a Nix
expression and creates a symlink named result in
the current directory:
$ nix-build pkgs/top-level/all-packages.nix -A hello
building path `/nix/store/632d2b22514d...-hello-2.1.1'
hello-2.1.1/
hello-2.1.1/intl/
hello-2.1.1/intl/ChangeLog
...
$ ls -l result
lrwxrwxrwx ... 2006-09-29 10:43 result -> /nix/store/632d2b22514d...-hello-2.1.1
$ ./result/bin/hello
Hello, world!
The -A option selects
the hello attribute from
all-packages.nix. This is faster than using the
symbolic package name specified by the name
attribute (which also happens to be hello) and is
unambiguous (there can be multiple packages with the symbolic name
hello, but there can be only one attribute in a set
named hello).
nix-build registers the
./result symlink as a garbage collection root, so
unless and until you delete the ./result symlink,
the output of the build will be safely kept on your system. You can
use nix-build’s -o switch to give the symlink another
name.
Nix has a transactional semantics. Once a build finishes
successfully, 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:
$ nix-build pkgs/top-level/all-packages.nix -A hello waiting for lock on `/nix/store/0h5b7hp8d4hqfrw8igvx97x1xawrjnac-hello-2.1.1x'
So it is always safe to run multiple instances of Nix in parallel (which isn’t the case with, 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 -j
, where
NN is the maximum number of jobs to be run
in parallel, or set. Typically this should be the number of
CPUs.
Recall from Example 5.2, “Build script for GNU Hello
(builder.sh)” that the builder
looked something like this:
PATH=$perl/bin:$PATH tar xvfz $src cd hello-* ./configure --prefix=$out make make install
The builders for almost all Unix packages look like this — set up some environment variables, unpack the sources, configure, build, and install. For this reason the standard environment provides some Bash functions that automate the build process. A builder using the generic build facilities in shown in Example 5.4, “Build script using the generic build functions”.
The | |
The function | |
The final step calls the shell function
|
Discerning readers will note that the
buildInputs could just as well have been set in the Nix
expression, like this:
buildInputs = [perl];
The perl attribute can then be removed, and the
builder becomes even shorter:
source $stdenv/setup genericBuild
In fact, mkDerivation provides a default builder
that looks exactly like that, so it is actually possible to omit the
builder for Hello entirely.
The Nix expression language is a pure, lazy, functional language. Purity means that operations in the language don't have side-effects (for instance, there is no variable assignment). Laziness means that arguments to functions are evaluated only when they are needed. Functional means that functions are “normal” values that can be passed around and manipulated in interesting ways. The language is not a full-featured, general purpose language. It's main job is to describe packages, compositions of packages, and the variability within packages.
This section presents the various features of the language.
Nix has the following basic data types:
Strings can be written in three ways.
The most common way is to enclose the string between double
quotes, e.g., "foo bar". Strings can span
multiple lines. The special characters " and
\ and the character sequence
${ must be escaped by prefixing them with a
backslash (\). Newlines, carriage returns and
tabs can be written as \n,
\r and \t,
respectively.
You can include the result of an expression into a string by
enclosing it in
${, a feature
known as antiquotation. The enclosed
expression must evaluate to something that can be coerced into a
string (meaning that it must be a string, a path, or a
derivation). For instance, rather than writing
...}
"--with-freetype2-library=" + freetype + "/lib"
(where freetype is a derivation), you can
instead write the more natural
"--with-freetype2-library=${freetype}/lib"The latter is automatically translated to the former. A more complicated example (from the Nix expression for Qt):
configureFlags = "
-system-zlib -system-libpng -system-libjpeg
${if openglSupport then "-dlopen-opengl
-L${mesa}/lib -I${mesa}/include
-L${libXmu}/lib -I${libXmu}/include" else ""}
${if threadSupport then "-thread" else "-no-thread"}
";
Note that Nix expressions and strings can be arbitrarily nested;
in this case the outer string contains various antiquotations that
themselves contain strings (e.g., "-thread"),
some of which in turn contain expressions (e.g.,
${mesa}).
The second way to write string literals is as an indented string, which is enclosed between pairs of double single-quotes, like so:
''
This is the first line.
This is the second line.
This is the third line.
''This kind of string literal intelligently strips indentation from the start of each line. To be precise, it strips from each line a number of spaces equal to the minimal indentation of the string as a whole (disregarding the indentation of empty lines). For instance, the first and second line are indented two space, while the third line is indented three spaces. Thus, two spaces are stripped from each line, so the resulting string is
"This is the first line.\nThis is the second line.\n This is the third line.\n"
Note that the whitespace and newline following the opening
'' is ignored if there is no non-whitespace
text on the initial line.
Antiquotation
(${) is
supported in indented strings.expr}}
Since ${ and '' have
special meaning in indented strings, you need a way to quote them.
${ can be escaped by prefixing it with
'', i.e., ''${.
'' can be escaped by prefixing it with
', i.e., '''. Finally,
linefeed, carriage-return and tab characters can be writted as
''\n, ''\r,
''\t.
Indented strings are primarily useful in that they allow
multi-line string literals to follow the indentation of the
enclosing Nix expression, and that less escaping is typically
necessary for strings representing languages such as shell scripts
and configuration files because '' is much less
common than ". Example:
stdenv.mkDerivation {
...
postInstall =
''
mkdir $out/bin $out/etc
cp foo $out/bin
echo "Hello World" > $out/etc/foo.conf
${if enableBar then "cp bar $out/bin" else ""}
'';
...
}
Finally, as a convenience, URIs as
defined in appendix B of RFC 2396
can be written as is, without quotes. For
instance, the string
"https://svn.cs.uu.nl:12443/dist/trace/trace-nix-trunk.tar.bz2"
can also be written as
https://svn.cs.uu.nl:12443/dist/trace/trace-nix-trunk.tar.bz2.
Integers, e.g.,
123.
Paths, e.g.,
/bin/sh or ./builder.sh.
A path must contain at least one slash to be recognised as such; for
instance, builder.sh is not a
path[7]. If the file name is
relative, i.e., if it does not begin with a slash, it is made
absolute at parse time relative to the directory of the Nix
expression that contained it. For instance, if a Nix expression in
/foo/bar/bla.nix refers to
../xyzzy/fnord.nix, the absolutised path is
/foo/xyzzy/fnord.nix.
Booleans with values
true and
false.
Lists are formed by enclosing a whitespace-separated list of values between square brackets. For example,
[ 123 ./foo.nix "abc" (f {x=y;}) ]
defines a list of four elements, the last being the result of a call
to the function f. Note that function calls have
to be enclosed in parentheses. If they had been omitted, e.g.,
[ 123 ./foo.nix "abc" f {x=y;} ]the result would be a list of five elements, the fourth one being a function and the fifth being an attribute set.
Attribute sets are really the core of the language, since ultimately it's all about creating derivations, which are really just sets of attributes to be passed to build scripts.
Attribute sets are just a list of name/value pairs enclosed in curly brackets, where each value is an arbitrary expression terminated by a semicolon. For example:
{ x = 123;
text = "Hello";
y = f { bla = 456; };
}
This defines an attribute set with attributes named
x, test, y.
The order of the attributes is irrelevant. An attribute name may only
occur once.
Attributes can be selected from an attribute set using the
. operator. For instance,
{ a = "Foo"; b = "Bar"; }.a
evaluates to "Foo".
Recursive attribute sets are just normal attribute sets, but the attributes can refer to each other. For example,
rec {
x = y;
y = 123;
}.x
evaluates to 123. Note that without
rec the binding x = y; would
refer to the variable y in the surrounding scope,
if one exists, and would be invalid if no such variable exists. That
is, in a normal (non-recursive) attribute set, attributes are not
added to the lexical scope; in a recursive set, they are.
Recursive attribute sets of course introduce the danger of infinite recursion. For example,
rec {
x = y;
y = x;
}.xdoes not terminate[8].
A let-expression allows you define local variables for an expression. For instance,
let x = "foo"; y = "bar"; in x + y
evaluates to "foobar".
There is also an obsolete form of let-expression,
let { , which is
translated to attrs }rec { . That is, the body of the let-expression is the
attrs
}.bodybody attribute of the attribute set.
When defining an attribute set it is often convenient to copy
variables from the surrounding lexical scope (e.g., when you want to
propagate attributes). This can be shortened using the
inherit keyword. For instance,
let
x = 123;
in
{
inherit x;
y = 456;
}
evaluates to {x = 123; y = 456;}. (Note that this
works because x is added to the lexical scope by
the let construct.) It is also possible to inherit
attributes from another attribute set. For instance, in this fragment
from all-packages.nix,
graphviz = (import ../tools/graphics/graphviz) {
inherit fetchurl stdenv libpng libjpeg expat x11 yacc;
inherit (xlibs) libXaw;
};
xlibs = {
libX11 = ...;
libXaw = ...;
...
}
libpng = ...;
libjpg = ...;
...
the attribute set used in the function call to the function defined in
../tools/graphics/graphviz inherits a number of
variables from the surrounding scope (fetchurl
... yacc), but also inherits
libXaw (the X Athena Widgets) from the
xlibs (X11 client-side libraries) attribute
set.
Functions have the following form:
{params}: body
This defines a function that must be called with an attribute set
containing the attributes listed in params,
which is a comma-separated list of attribute names. Optionally, for
each parameter a default value may be specified
by writing , where
param ?
ee is an arbitrary expression. If a
parameter has a default, the corresponding attribute may be omitted in
function calls.
Note that functions do not have names. If you want to give them a name, you can bind them to an attribute, e.g.,
let concat = {x, y}: x + y;
in concat {x = "foo"; y = "bar";}
It is also possible to define a function that takes a single argument and that does not need to be called with an attribute set as argument. The syntax is
var:body
where var is the name of the argument. It
is not possible to define a default. Example:
let negate = x: !x;
concat = x: y: x + y;
in if negate true then concat "foo" "bar" else ""
Note that concat is a function that takes one
arguments and returns a function that takes another argument. This
allows partial parameterisation (i.e., only filling some of the
arguments of a function); e.g.,
map (concat "foo") ["bar" "bla" "abc"]
evaluates to ["foobar" "foobla" "fooabc"].
Conditionals look like this:
ife1thene2elsee3
where e1 is an expression that should
evaluate to a Boolean value (true or
false).
Assertions are generally used to check that certain requirements on or between features and dependencies hold. They look like this:
asserte1;e2
where e1 is an expression that should
evaluate to a Boolean value. If it evaluates to
true, e2 is returned;
otherwise expression evaluation is aborted and a backtrace is printed.
Example 5.5. Nix expression for Subversion
{ localServer ? false
, httpServer ? false
, sslSupport ? false
, pythonBindings ? false
, javaSwigBindings ? false
, javahlBindings ? false
, stdenv, fetchurl
, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
}:
assert localServer -> db4 != null;
assert httpServer -> httpd != null && httpd.expat == expat;
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl);
assert pythonBindings -> swig != null && swig.pythonSupport;
assert javaSwigBindings -> swig != null && swig.javaSupport;
assert javahlBindings -> j2sdk != null;
stdenv.mkDerivation {
name = "subversion-1.1.1";
...
openssl = if sslSupport then openssl else null;
...
}Example 5.5, “Nix expression for Subversion” show how assertions are used in the Nix expression for Subversion.
This assertion states that if Subversion is to have support
for local repositories, then Berkeley DB is needed. So if the
Subversion function is called with the
| |
This is a more subtle condition: if Subversion is built with
Apache ( | |
This assertion says that in order for Subversion to have SSL
support (so that it can access | |
The conditional here is not really related to assertions,
but is worth pointing out: it ensures that if SSL support is
disabled, then the Subversion derivation is not dependent on
OpenSSL, even if a non- |
A with-expression,
withe1;e2
introduces the attribute set e1 into the
lexical scope of the expression e2. For
instance,
let as = {x = "foo"; y = "bar";};
in with as; x + y
evaluates to "foobar" since the
with adds the x and
y attributes of as to the
lexical scope in the expression x + y. The most
common use of with is in conjunction with the
import function. E.g.,
with (import ./definitions.nix); ...
makes all attributes defined in the file
definitions.nix available as if they were defined
locally in a rec-expression.