mirror of
https://github.com/NixOS/nix.git
synced 2025-11-26 04:00:59 +01:00
Merged to R9561; Fixed initial snapshot bug.
This commit is contained in:
parent
c28742f633
commit
1164d6a389
21 changed files with 1522 additions and 775 deletions
14
configure.ac
14
configure.ac
|
|
@ -99,9 +99,19 @@ static char buf[1024];]],
|
||||||
AC_LANG_POP(C++)
|
AC_LANG_POP(C++)
|
||||||
|
|
||||||
|
|
||||||
|
# Check for chroot support (requires chroot() and bind mounts).
|
||||||
|
AC_CHECK_FUNCS([chroot])
|
||||||
|
AC_CHECK_HEADERS([sys/param.h], [], [], [])
|
||||||
|
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||||
|
[#ifdef HAVE_SYS_PARAM_H
|
||||||
|
# include <sys/param.h>
|
||||||
|
# endif
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
# Check for <locale>
|
# Check for <locale>
|
||||||
AC_LANG_PUSH(C++)
|
AC_LANG_PUSH(C++)
|
||||||
AC_CHECK_HEADERS([locale])
|
AC_CHECK_HEADERS([locale], [], [], [])
|
||||||
AC_LANG_POP(C++)
|
AC_LANG_POP(C++)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -114,7 +124,7 @@ fi
|
||||||
])
|
])
|
||||||
|
|
||||||
NEED_PROG(curl, curl)
|
NEED_PROG(curl, curl)
|
||||||
NEED_PROG(shell, sh)
|
NEED_PROG(shell, bash)
|
||||||
NEED_PROG(patch, patch)
|
NEED_PROG(patch, patch)
|
||||||
AC_PATH_PROG(xmllint, xmllint, false)
|
AC_PATH_PROG(xmllint, xmllint, false)
|
||||||
AC_PATH_PROG(xsltproc, xsltproc, false)
|
AC_PATH_PROG(xsltproc, xsltproc, false)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
|
||||||
FIGURES = figures/user-environments.png
|
FIGURES = figures/user-environments.png
|
||||||
|
|
||||||
MANUAL_SRCS = manual.xml introduction.xml installation.xml \
|
MANUAL_SRCS = manual.xml introduction.xml installation.xml \
|
||||||
package-management.xml writing-nix-expressions.xml \
|
package-management.xml writing-nix-expressions.xml builtins.xml \
|
||||||
build-farm.xml \
|
build-farm.xml \
|
||||||
$(man1_MANS:.1=.xml) \
|
$(man1_MANS:.1=.xml) \
|
||||||
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
|
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
|
||||||
|
|
|
||||||
751
doc/manual/builtins.xml
Normal file
751
doc/manual/builtins.xml
Normal file
|
|
@ -0,0 +1,751 @@
|
||||||
|
<section xmlns="http://docbook.org/ns/docbook"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xml:id='ssec-builtins'>
|
||||||
|
|
||||||
|
<title>Built-in functions</title>
|
||||||
|
|
||||||
|
|
||||||
|
<para>This section lists the functions and constants built into the
|
||||||
|
Nix expression evaluator. (The built-in function
|
||||||
|
<function>derivation</function> is discussed above.) Some built-ins,
|
||||||
|
such as <function>derivation</function>, are always in scope of every
|
||||||
|
Nix expression; you can just access them right away. But to prevent
|
||||||
|
polluting the namespace too much, most built-ins are not in scope.
|
||||||
|
Instead, you can access them through the <varname>builtins</varname>
|
||||||
|
built-in value, which is an attribute set that contains all built-in
|
||||||
|
functions and values. For instance, <function>derivation</function>
|
||||||
|
is also available as <function>builtins.derivation</function>.</para>
|
||||||
|
|
||||||
|
|
||||||
|
<variablelist >
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>abort</function> <replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Abort Nix expression evaluation, print error
|
||||||
|
message <replaceable>s</replaceable>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.add</function>
|
||||||
|
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the sum of the integers
|
||||||
|
<replaceable>e1</replaceable> and
|
||||||
|
<replaceable>e2</replaceable>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.attrNames</function>
|
||||||
|
<replaceable>attrs</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the names of the attributes in the
|
||||||
|
attribute set <replaceable>attrs</replaceable> in a sorted list.
|
||||||
|
For instance, <literal>builtins.attrNames {y = 1; x =
|
||||||
|
"foo";}</literal> evaluates to <literal>["x" "y"]</literal>.
|
||||||
|
There is no built-in function <function>attrValues</function>, but
|
||||||
|
you can easily define it yourself:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs);</programlisting>
|
||||||
|
|
||||||
|
</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the <emphasis>base name</emphasis> of the
|
||||||
|
string <replaceable>s</replaceable>, that is, everything following
|
||||||
|
the final slash in the string. This is similar to the GNU
|
||||||
|
<command>basename</command> command.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><varname>builtins</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>The attribute set <varname>builtins</varname>
|
||||||
|
contains all the built-in functions and values. You can use
|
||||||
|
<varname>builtins</varname> to test for the availability of
|
||||||
|
features in the Nix installation, e.g.,
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||||
|
|
||||||
|
This allows a Nix expression to fall back gracefully on older Nix
|
||||||
|
installations that don’t have the desired built-in function.
|
||||||
|
However, in that case you should not write
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
|
||||||
|
|
||||||
|
This Nix expression will trigger an “undefined variable” error on
|
||||||
|
older Nix versions since <function>__getEnv</function> doesn’t
|
||||||
|
exist. <literal>builtins.getEnv</literal>, on the other hand, is
|
||||||
|
safe since <literal>builtins</literal> always exists and attribute
|
||||||
|
selection is lazy, so it’s only performed if the test
|
||||||
|
succeeds.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry
|
||||||
|
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>The built-in value <varname>currentSystem</varname>
|
||||||
|
evaluates to the Nix platform identifier for the Nix installation
|
||||||
|
on which the expression is being evaluated, such as
|
||||||
|
<literal>"i686-linux"</literal> or
|
||||||
|
<literal>"powerpc-darwin"</literal>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<varlistentry><term><function>currentTime</function></term>
|
||||||
|
|
||||||
|
<listitem><para>The built-in value <varname>currentTime</varname>
|
||||||
|
returns the current system time in seconds since 00:00:00 1/1/1970
|
||||||
|
UTC. Due to the evaluation model of Nix expressions
|
||||||
|
(<emphasis>maximal laziness</emphasis>), it always yields the same
|
||||||
|
value within an execution of Nix.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<varlistentry><term><function>dependencyClosure</function></term>
|
||||||
|
|
||||||
|
<listitem><para>TODO</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>derivation</function>
|
||||||
|
<replaceable>attrs</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para><function>derivation</function> is described in
|
||||||
|
<xref linkend='ssec-derivation' />.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>dirOf</function> <replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the directory part of the string
|
||||||
|
<replaceable>s</replaceable>, that is, everything before the final
|
||||||
|
slash in the string. This is similar to the GNU
|
||||||
|
<command>dirname</command> command.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.filterSource</function>
|
||||||
|
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
|
||||||
|
<para>This function allows you to copy sources into the Nix
|
||||||
|
store while filtering certain files. For instance, suppose that
|
||||||
|
you want to use the directory <filename>source-dir</filename> as
|
||||||
|
an input to a Nix expression, e.g.
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
...
|
||||||
|
src = ./source-dir;
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
However, if <filename>source-dir</filename> is a Subversion
|
||||||
|
working copy, then all those annoying <filename>.svn</filename>
|
||||||
|
subdirectories will also be copied to the store. Worse, the
|
||||||
|
contents of those directories may change a lot, causing lots of
|
||||||
|
spurious rebuilds. With <function>filterSource</function> you
|
||||||
|
can filter out the <filename>.svn</filename> directories:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
src = builtins.filterSource
|
||||||
|
(path: type: type != "directory" || baseNameOf path != ".svn")
|
||||||
|
./source-dir;
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>Thus, the first argument <replaceable>e1</replaceable>
|
||||||
|
must be a predicate function that is called for each regular
|
||||||
|
file, directory or symlink in the source tree
|
||||||
|
<replaceable>e2</replaceable>. If the function returns
|
||||||
|
<literal>true</literal>, the file is copied to the Nix store,
|
||||||
|
otherwise it is omitted. The function is called with two
|
||||||
|
arguments. The first is the full path of the file. The second
|
||||||
|
is a string that identifies the type of the file, which is
|
||||||
|
either <literal>"regular"</literal>,
|
||||||
|
<literal>"directory"</literal>, <literal>"symlink"</literal> or
|
||||||
|
<literal>"unknown"</literal> (for other kinds of files such as
|
||||||
|
device nodes or fifos — but note that those cannot be copied to
|
||||||
|
the Nix store, so if the predicate returns
|
||||||
|
<literal>true</literal> for them, the copy will fail).</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.getAttr</function>
|
||||||
|
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para><function>getAttr</function> returns the attribute
|
||||||
|
named <replaceable>s</replaceable> from the attribute set
|
||||||
|
<replaceable>attrs</replaceable>. Evaluation aborts if the
|
||||||
|
attribute doesn’t exist. This is a dynamic version of the
|
||||||
|
<literal>.</literal> operator, since <replaceable>s</replaceable>
|
||||||
|
is an expression rather than an identifier.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.getEnv</function>
|
||||||
|
<replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para><function>getEnv</function> returns the value of
|
||||||
|
the environment variable <replaceable>s</replaceable>, or an empty
|
||||||
|
string if the variable doesn’t exist. This function should be
|
||||||
|
used with care, as it can introduce all sorts of nasty environment
|
||||||
|
dependencies in your Nix expression.</para>
|
||||||
|
|
||||||
|
<para><function>getEnv</function> is used in Nix Packages to
|
||||||
|
locate the file <filename>~/.nixpkgs/config.nix</filename>, which
|
||||||
|
contains user-local settings for Nix Packages. (That is, it does
|
||||||
|
a <literal>getEnv "HOME"</literal> to locate the user’s home
|
||||||
|
directory.)</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.hasAttr</function>
|
||||||
|
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para><function>hasAttr</function> returns
|
||||||
|
<literal>true</literal> if the attribute set
|
||||||
|
<replaceable>attrs</replaceable> has an attribute named
|
||||||
|
<replaceable>s</replaceable>, and <literal>false</literal>
|
||||||
|
otherwise. This is a dynamic version of the <literal>?</literal>
|
||||||
|
operator, since <replaceable>s</replaceable> is an expression
|
||||||
|
rather than an identifier.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.head</function>
|
||||||
|
<replaceable>list</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the first element of a list; abort
|
||||||
|
evaluation if the argument isn’t a list or is an empty list. You
|
||||||
|
can test whether a list is empty by comparing it with
|
||||||
|
<literal>[]</literal>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>import</function>
|
||||||
|
<replaceable>path</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Load, parse and return the Nix expression in the
|
||||||
|
file <replaceable>path</replaceable>. Evaluation aborts if the
|
||||||
|
file doesn’t exist or contains an incorrect Nix
|
||||||
|
expression. <function>import</function> implements Nix’s module
|
||||||
|
system: you can put any Nix expression (such as an attribute set
|
||||||
|
or a function) in a separate file, and use it from Nix expressions
|
||||||
|
in other files.</para>
|
||||||
|
|
||||||
|
<para>A Nix expression loaded by <function>import</function> must
|
||||||
|
not contain any <emphasis>free variables</emphasis> (identifiers
|
||||||
|
that are not defined in the Nix expression itself and are not
|
||||||
|
built-in). Therefore, it cannot refer to variables that are in
|
||||||
|
scope at the call site. For instance, if you have a calling
|
||||||
|
expression
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
rec {
|
||||||
|
x = 123;
|
||||||
|
y = import ./foo.nix;
|
||||||
|
}</programlisting>
|
||||||
|
|
||||||
|
then the following <filename>foo.nix</filename> will give an
|
||||||
|
error:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
x + 456</programlisting>
|
||||||
|
|
||||||
|
since <varname>x</varname> is not in scope in
|
||||||
|
<filename>foo.nix</filename>. If you want <varname>x</varname>
|
||||||
|
to be available in <filename>foo.nix</filename>, you should pass
|
||||||
|
it as a function argument:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
rec {
|
||||||
|
x = 123;
|
||||||
|
y = import ./foo.nix x;
|
||||||
|
}</programlisting>
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
x: x + 456</programlisting>
|
||||||
|
|
||||||
|
(The function argument doesn’t have to be called
|
||||||
|
<varname>x</varname> in <filename>foo.nix</filename>; any name
|
||||||
|
would work.)</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.isAttrs</function>
|
||||||
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return <literal>true</literal> if
|
||||||
|
<replaceable>e</replaceable> evaluates to an attribute set, and
|
||||||
|
<literal>false</literal> otherwise.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.isList</function>
|
||||||
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return <literal>true</literal> if
|
||||||
|
<replaceable>e</replaceable> evaluates to a list, and
|
||||||
|
<literal>false</literal> otherwise.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.isFunction</function>
|
||||||
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return <literal>true</literal> if
|
||||||
|
<replaceable>e</replaceable> evaluates to a function, and
|
||||||
|
<literal>false</literal> otherwise.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>isNull</function>
|
||||||
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return <literal>true</literal> if
|
||||||
|
<replaceable>e</replaceable> evaluates to <literal>null</literal>,
|
||||||
|
and <literal>false</literal> otherwise.</para>
|
||||||
|
|
||||||
|
<warning><para>This function is <emphasis>deprecated</emphasis>;
|
||||||
|
just write <literal>e == null</literal> instead.</para></warning>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.lessThan</function>
|
||||||
|
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return <literal>true</literal> if the integer
|
||||||
|
<replaceable>e1</replaceable> is less than the integer
|
||||||
|
<replaceable>e2</replaceable>, and <literal>false</literal>
|
||||||
|
otherwise. Evaluation aborts if either
|
||||||
|
<replaceable>e1</replaceable> or <replaceable>e2</replaceable>
|
||||||
|
does not evaluate to an integer.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.listToAttrs</function>
|
||||||
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Construct an attribute set from a list specifying
|
||||||
|
the names and values of each attribute. Each element of the list
|
||||||
|
should be an attribute set consisting of a string-valued attribute
|
||||||
|
<varname>name</varname> specifying the name of the attribute, and
|
||||||
|
an attribute <varname>value</varname> specifying its value.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
builtins.listToAttrs [
|
||||||
|
{name = "foo"; value = 123;}
|
||||||
|
{name = "bar"; value = 456;}
|
||||||
|
]
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
evaluates to
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
{ foo = 123; bar = 456; }
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry><term><function>map</function>
|
||||||
|
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Apply the function <replaceable>f</replaceable> to
|
||||||
|
each element in the list <replaceable>list</replaceable>. For
|
||||||
|
example,
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
map (x: "foo" + x) ["bar" "bla" "abc"]</programlisting>
|
||||||
|
|
||||||
|
evaluates to <literal>["foobar" "foobla"
|
||||||
|
"fooabc"]</literal>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.pathExists</function>
|
||||||
|
<replaceable>path</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return <literal>true</literal> if the path
|
||||||
|
<replaceable>path</replaceable> exists, and
|
||||||
|
<literal>false</literal> otherwise. One application of this
|
||||||
|
function is to conditionally include a Nix expression containing
|
||||||
|
user configuration:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
let
|
||||||
|
fileName = builtins.getEnv "CONFIG_FILE";
|
||||||
|
config =
|
||||||
|
if fileName != "" && builtins.pathExists (builtins.toPath fileName)
|
||||||
|
then import (builtins.toPath fileName)
|
||||||
|
else { someSetting = false; }; <lineannotation># default configuration</lineannotation>
|
||||||
|
in config.someSetting</programlisting>
|
||||||
|
|
||||||
|
(Note that <envar>CONFIG_FILE</envar> must be an absolute path for
|
||||||
|
this to work.)</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<varlistentry><term><function>relativise</function></term>
|
||||||
|
|
||||||
|
<listitem><para>TODO</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>removeAttrs</function>
|
||||||
|
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Remove the attributes listed in
|
||||||
|
<replaceable>list</replaceable> from the attribute set
|
||||||
|
<replaceable>attrs</replaceable>. The attributes don’t have to
|
||||||
|
exist in <replaceable>attrs</replaceable>. For instance,
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"]</screen>
|
||||||
|
|
||||||
|
evaluates to <literal>{y = 2;}</literal>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.stringLength</function>
|
||||||
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the length of the string
|
||||||
|
<replaceable>e</replaceable>. If <replaceable>e</replaceable> is
|
||||||
|
not a string, evaluation is aborted.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.sub</function>
|
||||||
|
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the difference between the integers
|
||||||
|
<replaceable>e1</replaceable> and
|
||||||
|
<replaceable>e2</replaceable>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.substr</function>
|
||||||
|
<replaceable>start</replaceable> <replaceable>len</replaceable>
|
||||||
|
<replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the substring of
|
||||||
|
<replaceable>s</replaceable> from character position
|
||||||
|
<replaceable>start</replaceable> (zero-based) up to but not
|
||||||
|
including <replaceable>start + len</replaceable>. If
|
||||||
|
<replaceable>start</replaceable> is greater than the length of the
|
||||||
|
string, an empty string is returned, and if <replaceable>start +
|
||||||
|
len</replaceable> lies beyond the end of the string, only the
|
||||||
|
substring up to the end of the string is returned.
|
||||||
|
<replaceable>start</replaceable> must be
|
||||||
|
non-negative.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.tail</function>
|
||||||
|
<replaceable>list</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return the second to last elements of a list;
|
||||||
|
abort evaluation if the argument isn’t a list or is an empty
|
||||||
|
list.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>throw</function>
|
||||||
|
<replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Throw an error message
|
||||||
|
<replaceable>s</replaceable>. This usually aborts Nix expression
|
||||||
|
evaluation, but in <command>nix-env -qa</command> and other
|
||||||
|
commands that try to evaluate a set of derivations to get
|
||||||
|
information about those derivations, a derivation that throws an
|
||||||
|
error is silently skipped (which is not the case for
|
||||||
|
<function>abort</function>).</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry
|
||||||
|
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
|
||||||
|
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Store the string <replaceable>s</replaceable> in a
|
||||||
|
file in the Nix store and return its path. The file has suffix
|
||||||
|
<replaceable>name</replaceable>. This file can be used as an
|
||||||
|
input to derivations. One application is to write builders
|
||||||
|
“inline”. For instance, the following Nix expression combines
|
||||||
|
<xref linkend='ex-hello-nix' /> and <xref
|
||||||
|
linkend='ex-hello-builder' /> into one file:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
{stdenv, fetchurl, perl}:
|
||||||
|
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = "hello-2.1.1";
|
||||||
|
|
||||||
|
builder = builtins.toFile "builder.sh" "
|
||||||
|
source $stdenv/setup
|
||||||
|
|
||||||
|
PATH=$perl/bin:$PATH
|
||||||
|
|
||||||
|
tar xvfz $src
|
||||||
|
cd hello-*
|
||||||
|
./configure --prefix=$out
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
";
|
||||||
|
|
||||||
|
src = fetchurl {
|
||||||
|
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
|
||||||
|
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
||||||
|
};
|
||||||
|
inherit perl;
|
||||||
|
}</programlisting>
|
||||||
|
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>It is even possible for one file to refer to another, e.g.,
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
builder = let
|
||||||
|
configFile = builtins.toFile "foo.conf" "
|
||||||
|
# This is some dummy configuration file.
|
||||||
|
<replaceable>...</replaceable>
|
||||||
|
";
|
||||||
|
in builtins.toFile "builder.sh" "
|
||||||
|
source $stdenv/setup
|
||||||
|
<replaceable>...</replaceable>
|
||||||
|
cp ${configFile} $out/etc/foo.conf
|
||||||
|
";</programlisting>
|
||||||
|
|
||||||
|
Note that <literal>${configFile}</literal> is an antiquotation
|
||||||
|
(see <xref linkend='ssec-values' />), so the result of the
|
||||||
|
expression <literal>configFile</literal> (i.e., a path like
|
||||||
|
<filename>/nix/store/m7p7jfny445k...-foo.conf</filename>) will be
|
||||||
|
spliced into the resulting string.</para>
|
||||||
|
|
||||||
|
<para>It is however <emphasis>not</emphasis> allowed to have files
|
||||||
|
mutually referring to each other, like so:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
let
|
||||||
|
foo = builtins.toFile "foo" "...${bar}...";
|
||||||
|
bar = builtins.toFile "bar" "...${foo}...";
|
||||||
|
in foo</programlisting>
|
||||||
|
|
||||||
|
This is not allowed because it would cause a cyclic dependency in
|
||||||
|
the computation of the cryptographic hashes for
|
||||||
|
<varname>foo</varname> and <varname>bar</varname>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Convert the string value
|
||||||
|
<replaceable>s</replaceable> into a path value. The string
|
||||||
|
<replaceable>s</replaceable> must represent an absolute path
|
||||||
|
(i.e., must start with <literal>/</literal>). The path need not
|
||||||
|
exist. The resulting path is canonicalised, e.g.,
|
||||||
|
<literal>builtins.toPath "//foo/xyzzy/../bar/"</literal> returns
|
||||||
|
<literal>/foo/bar</literal>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Convert the expression
|
||||||
|
<replaceable>e</replaceable> to a string.
|
||||||
|
<replaceable>e</replaceable> can be a string (in which case
|
||||||
|
<function>toString</function> is a no-op) or a path (e.g.,
|
||||||
|
<literal>toString /foo/bar</literal> yields
|
||||||
|
<literal>"/foo/bar"</literal>.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Return a string containing an XML representation
|
||||||
|
of <replaceable>e</replaceable>. The main application for
|
||||||
|
<function>toXML</function> is to communicate information with the
|
||||||
|
builder in a more structured format than plain environment
|
||||||
|
variables.</para>
|
||||||
|
|
||||||
|
<!-- TODO: more formally describe the schema of the XML
|
||||||
|
representation -->
|
||||||
|
|
||||||
|
<para><xref linkend='ex-toxml' /> shows an example where this is
|
||||||
|
the case. The builder is supposed to generate the configuration
|
||||||
|
file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
|
||||||
|
servlet container</link>. A servlet container contains a number
|
||||||
|
of servlets (<filename>*.war</filename> files) each exported under
|
||||||
|
a specific URI prefix. So the servlet configuration is a list of
|
||||||
|
attribute sets containing the <varname>path</varname> and
|
||||||
|
<varname>war</varname> of the servlet (<xref
|
||||||
|
linkend='ex-toxml-co-servlets' />). This kind of information is
|
||||||
|
difficult to communicate with the normal method of passing
|
||||||
|
information through an environment variable, which just
|
||||||
|
concatenates everything together into a string (which might just
|
||||||
|
work in this case, but wouldn’t work if fields are optional or
|
||||||
|
contain lists themselves). Instead the Nix expression is
|
||||||
|
converted to an XML representation with
|
||||||
|
<function>toXML</function>, which is unambiguous and can easily be
|
||||||
|
processed with the appropriate tools. For instance, in the
|
||||||
|
example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
|
||||||
|
/>) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
|
||||||
|
generate the XML configuration file for the Jetty server. The XML
|
||||||
|
representation produced from <xref linkend='ex-toxml-co-servlets'
|
||||||
|
/> by <function>toXML</function> is shown in <xref
|
||||||
|
linkend='ex-toxml-result' />.</para>
|
||||||
|
|
||||||
|
<para>Note that <xref linkend='ex-toxml' /> uses the <function
|
||||||
|
linkend='builtin-toFile'>toFile</function> built-in to write the
|
||||||
|
builder and the stylesheet “inline” in the Nix expression. The
|
||||||
|
path of the stylesheet is spliced into the builder at
|
||||||
|
<literal>xsltproc ${stylesheet}
|
||||||
|
<replaceable>...</replaceable></literal>.</para>
|
||||||
|
|
||||||
|
<example xml:id='ex-toxml'><title>Passing information to a builder
|
||||||
|
using <function>toXML</function></title>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
{stdenv, fetchurl, libxslt, jira, uberwiki}:
|
||||||
|
|
||||||
|
stdenv.mkDerivation (rec {
|
||||||
|
name = "web-server";
|
||||||
|
|
||||||
|
buildInputs = [libxslt];
|
||||||
|
|
||||||
|
builder = builtins.toFile "builder.sh" "
|
||||||
|
source $stdenv/setup
|
||||||
|
mkdir $out
|
||||||
|
echo $servlets | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
|
||||||
|
";
|
||||||
|
|
||||||
|
stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
|
||||||
|
"<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
|
||||||
|
<xsl:template match='/'>
|
||||||
|
<Configure>
|
||||||
|
<xsl:for-each select='/expr/list/attrs'>
|
||||||
|
<Call name='addWebApplication'>
|
||||||
|
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
|
||||||
|
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
|
||||||
|
</Call>
|
||||||
|
</xsl:for-each>
|
||||||
|
</Configure>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
||||||
|
";
|
||||||
|
|
||||||
|
servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
|
||||||
|
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
|
||||||
|
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
|
||||||
|
];
|
||||||
|
})]]></programlisting>
|
||||||
|
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<example xml:id='ex-toxml-result'><title>XML representation produced by
|
||||||
|
<function>toXML</function></title>
|
||||||
|
|
||||||
|
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<expr>
|
||||||
|
<list>
|
||||||
|
<attrs>
|
||||||
|
<attr name="path">
|
||||||
|
<string value="/bugtracker" />
|
||||||
|
</attr>
|
||||||
|
<attr name="war">
|
||||||
|
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
<attrs>
|
||||||
|
<attr name="path">
|
||||||
|
<string value="/wiki" />
|
||||||
|
</attr>
|
||||||
|
<attr name="war">
|
||||||
|
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
|
||||||
|
</attr>
|
||||||
|
</attrs>
|
||||||
|
</list>
|
||||||
|
</expr>]]></programlisting>
|
||||||
|
|
||||||
|
</example>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><function>builtins.trace</function>
|
||||||
|
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Evaluate <replaceable>e1</replaceable> and print its
|
||||||
|
abstract syntax representation on standard error. Then return
|
||||||
|
<replaceable>e2</replaceable>. This function is useful for
|
||||||
|
debugging.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
@ -118,6 +118,123 @@ env-keep-derivations = false
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
|
||||||
|
<para>This option defines the maximum number of seconds that a
|
||||||
|
builder can go without producing any data on standard output or
|
||||||
|
standard error. This is useful (for instance in a automated
|
||||||
|
build system) to catch builds that are stuck in an infinite
|
||||||
|
loop, or to catch remote builds that are hanging due to network
|
||||||
|
problems. It can be overriden using the <option
|
||||||
|
linkend="opt-max-silent-time">--max-silent-time</option> command
|
||||||
|
line switch.</para>
|
||||||
|
|
||||||
|
<para>The value <literal>0</literal> means that there is no
|
||||||
|
timeout. This is also the default.</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
|
||||||
|
|
||||||
|
<listitem><para>This options specifies the Unix group containing
|
||||||
|
the Nix build user accounts. In multi-user Nix installations,
|
||||||
|
builds should not be performed by the Nix account since that would
|
||||||
|
allow users to arbitrarily modify the Nix store and database by
|
||||||
|
supplying specially crafted builders; and they cannot be performed
|
||||||
|
by the calling user since that would allow him/her to influence
|
||||||
|
the build result.</para>
|
||||||
|
|
||||||
|
<para>Therefore, if this option is non-empty and specifies a valid
|
||||||
|
group, builds will be performed under the user accounts that are a
|
||||||
|
member of the group specified here (as listed in
|
||||||
|
<filename>/etc/group</filename>). Those user accounts should not
|
||||||
|
be used for any other purpose!</para>
|
||||||
|
|
||||||
|
<para>Nix will never run two builds under the same user account at
|
||||||
|
the same time. This is to prevent an obvious security hole: a
|
||||||
|
malicious user writing a Nix expression that modifies the build
|
||||||
|
result of a legitimate Nix expression being built by another user.
|
||||||
|
Therefore it is good to have as many Nix build user accounts as
|
||||||
|
you can spare. (Remember: uids are cheap.)</para>
|
||||||
|
|
||||||
|
<para>The build users should have permission to create files in
|
||||||
|
the Nix store, but not delete them. Therefore,
|
||||||
|
<filename>/nix/store</filename> should be owned by the Nix
|
||||||
|
account, its group should be the group specified here, and its
|
||||||
|
mode should be <literal>1775</literal>.</para>
|
||||||
|
|
||||||
|
<para>If the build users group is empty, builds will be performed
|
||||||
|
under the uid of the Nix process (that is, the uid of the caller
|
||||||
|
if <envar>NIX_REMOTE</envar> is empty, the uid under which the Nix
|
||||||
|
daemon runs if <envar>NIX_REMOTE</envar> is
|
||||||
|
<literal>daemon</literal>, or the uid that owns the setuid
|
||||||
|
<command>nix-worker</command> program if <envar>NIX_REMOTE</envar>
|
||||||
|
is <literal>slave</literal>). Obviously, this should not be used
|
||||||
|
in multi-user settings with untrusted users.</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry><term><literal>build-use-chroot</literal></term>
|
||||||
|
|
||||||
|
<listitem><para>If set to <literal>true</literal>, builds will be
|
||||||
|
performed in a <emphasis>chroot environment</emphasis>, i.e., the
|
||||||
|
build will be isolated from the normal file system hierarchy and
|
||||||
|
will only see the Nix store, the temporary build directory, and
|
||||||
|
the directories configured with the <link
|
||||||
|
linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
|
||||||
|
option</link> (such as <filename>/proc</filename> and
|
||||||
|
<filename>/dev</filename>). This is useful to prevent undeclared
|
||||||
|
dependencies on files in directories such as
|
||||||
|
<filename>/usr/bin</filename>.</para>
|
||||||
|
|
||||||
|
<para>The use of a chroot requires that Nix is run as root (but
|
||||||
|
you can still use the <link
|
||||||
|
linkend='conf-build-users-group'>“build users” feature</link> to
|
||||||
|
perform builds under different users than root). Currently,
|
||||||
|
chroot builds only work on Linux because Nix uses “bind mounts” to
|
||||||
|
make the Nix store and other directories available inside the
|
||||||
|
chroot.</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||||
|
|
||||||
|
<listitem><para>When builds are performed in a chroot environment,
|
||||||
|
Nix will mount (using <command>mount --bind</command> on Linux)
|
||||||
|
some directories from the normal file system hierarchy inside the
|
||||||
|
chroot. These are the Nix store, the temporary build directory
|
||||||
|
(usually
|
||||||
|
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
|
||||||
|
and the directories listed here. The default is <literal>dev
|
||||||
|
/proc</literal>. Files in <filename>/dev</filename> (such as
|
||||||
|
<filename>/dev/null</filename>) are needed by many builds, and
|
||||||
|
some files in <filename>/proc</filename> may also be needed
|
||||||
|
occasionally.</para>
|
||||||
|
|
||||||
|
<para>The value used on NixOS is
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
build-use-chroot = /dev /proc /bin</programlisting>
|
||||||
|
|
||||||
|
to make the <filename>/bin/sh</filename> symlink available (which
|
||||||
|
is still needed by many builders).</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><literal>system</literal></term>
|
<varlistentry><term><literal>system</literal></term>
|
||||||
|
|
||||||
<listitem><para>This option specifies the canonical Nix system
|
<listitem><para>This option specifies the canonical Nix system
|
||||||
|
|
|
||||||
|
|
@ -1,135 +1,304 @@
|
||||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xml:id="chap-introduction">
|
||||||
|
|
||||||
<title>Introduction</title>
|
<title>Introduction</title>
|
||||||
|
|
||||||
<para>Nix is a system for the deployment of software. Software
|
|
||||||
deployment is concerned with the creation, distribution, and
|
|
||||||
management of software components (<quote>packages</quote>). Its main
|
|
||||||
features are:
|
|
||||||
|
|
||||||
<itemizedlist>
|
<section><title>About Nix</title>
|
||||||
|
|
||||||
<listitem><para>It helps you make sure that dependency specifications
|
<para>Nix is a <emphasis>purely functional package manager</emphasis>.
|
||||||
are complete. In general in a deployment system you have to specify
|
This means that it treats packages like values in purely functional
|
||||||
for each component what its dependencies are, but there are no
|
programming languages such as Haskell — they are built by functions
|
||||||
guarantees that this specification is complete. If you forget a
|
that don’t have side-effects, and they never change after they have
|
||||||
dependency, then the component will build and work correctly on
|
been built. Nix stores packages in the <emphasis>Nix
|
||||||
<emphasis>your</emphasis> machine if you have the dependency
|
store</emphasis>, usually the directory
|
||||||
installed, but not on the end user's machine if it's not
|
<filename>/nix/store</filename>, where each package has its own unique
|
||||||
there.</para></listitem>
|
subdirectory such as
|
||||||
|
|
||||||
<listitem><para>It is possible to have <emphasis>multiple versions or
|
<programlisting>
|
||||||
variants</emphasis> of a component installed at the same time. In
|
/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/
|
||||||
contrast, in systems such as RPM different versions of the same
|
</programlisting>
|
||||||
package tend to install to the same location in the file system, so
|
|
||||||
installing one version will remove the other. This is especially
|
|
||||||
important if you want to use applications that have conflicting
|
|
||||||
requirements on different versions of a component (e.g., application A
|
|
||||||
requires version 1.0 of library X, while application B requires a
|
|
||||||
non-backwards compatible version 1.1).</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Users can have different <quote>views</quote>
|
where <literal>r8vvq9kq…</literal> is a unique identifier for the
|
||||||
(<quote>profiles</quote> in Nix parlance) on the set of installed
|
package that captures all its dependencies (it’s a cryptographic hash
|
||||||
applications in a system. For instance, one user can have version 1.0
|
of the package’s build dependency graph). This enables many powerful
|
||||||
of some package visible, while another is using version 1.1, and a
|
features.</para>
|
||||||
third doesn't use it at all.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>It is possible to atomically
|
|
||||||
<emphasis>upgrade</emphasis> software. I.e., there is no time window
|
|
||||||
during an upgrade in which part of the old version and part of the new
|
|
||||||
version are simultaneously visible (which might well cause the
|
|
||||||
component to fail).</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Likewise, it is possible to atomically roll back after
|
<simplesect><title>Multiple versions</title>
|
||||||
an install, upgrade, or uninstall action. That is, in a fast (O(1))
|
|
||||||
operation the previous configuration of the system can be restored.
|
|
||||||
This is because upgrade or uninstall actions don't actually remove
|
|
||||||
components from the system.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Unused components can be
|
<para>You can have multiple versions or variants of a package
|
||||||
<emphasis>garbage-collected</emphasis> automatically and safely: when
|
installed at the same time. This is especially important when
|
||||||
you remove an application from a profile, its dependencies will be
|
different applications have dependencies on different versions of the
|
||||||
deleted by the garbage collector only if there are no other active
|
same package — it prevents the “DLL hell”. Because of the hashing
|
||||||
applications using them.</para></listitem>
|
scheme, different versions of a package end up in different paths in
|
||||||
|
the Nix store, so they don’t interfere with each other.</para>
|
||||||
|
|
||||||
<listitem><para>Nix supports both source-based deployment models
|
<para>An important consequence is that operations like upgrading or
|
||||||
(where you distribute <emphasis>Nix expressions</emphasis> that tell
|
uninstalling an application cannot break other applications, since
|
||||||
Nix how to build software from source) and binary-based deployment
|
these operations never “destructively” update or delete files that are
|
||||||
models. The latter is more-or-less transparent: installation of
|
used by other packages.</para>
|
||||||
components is always based on Nix expressions, but if the expressions
|
|
||||||
have been built before and Nix knows that the resulting binaries are
|
|
||||||
available somewhere, it will use those instead.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Nix is flexible in the deployment policies that it
|
</simplesect>
|
||||||
supports. There is a clear separation between the tools that
|
|
||||||
implement basic Nix <emphasis>mechanisms</emphasis> (e.g., building
|
|
||||||
Nix expressions), and the tools that implement various deployment
|
|
||||||
<emphasis>policies</emphasis>. For instance, there is a concept of
|
|
||||||
<quote>Nix channels</quote> that can be used to keep software
|
|
||||||
installations up-to-date automatically from a network source. This is
|
|
||||||
a policy that is implemented by a fairly short Perl script, which can
|
|
||||||
be adapted easily to achieve similar policies.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Nix component builds aim to be <quote>pure</quote>;
|
|
||||||
that is, unaffected by anything other than the declared dependencies.
|
|
||||||
This means that if a component was built successfully once, it can be
|
|
||||||
rebuilt again on another machine and the result will be the same. We
|
|
||||||
cannot <emphasis>guarantee</emphasis> this (e.g., if the build depends
|
|
||||||
on the time-of-day), but Nix (and the tools in the Nix Packages
|
|
||||||
collection) takes special care to help achieve this.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Nix expressions (the things that tell Nix how to build
|
<simplesect><title>Complete dependencies</title>
|
||||||
components) are self-contained: they describe not just components but
|
|
||||||
complete compositions. In other words, Nix expressions also describe
|
|
||||||
how to build all the dependencies. This is in contrast to component
|
|
||||||
specification languages like RPM spec files, which might say that a
|
|
||||||
component X depends on some other component Y, but since it does not
|
|
||||||
describe <emphasis>exactly</emphasis> what Y is, the result of
|
|
||||||
building or running X might be different on different machines.
|
|
||||||
Combined with purity, self-containedness ensures that a component that
|
|
||||||
<quote>works</quote> on one machine also works on another, when
|
|
||||||
deployed using Nix.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>The Nix expression language makes it easy to describe
|
<para>Nix helps you make sure that package dependency specifications
|
||||||
variability in components (e.g., optional features or
|
are complete. In general, when you’re making a package for a package
|
||||||
dependencies).</para></listitem>
|
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
|
||||||
|
component will build and work correctly on <emphasis>your</emphasis>
|
||||||
|
machine if you have the dependency installed, but not on the end
|
||||||
|
user's machine if it's not there.</para>
|
||||||
|
|
||||||
<listitem><para>Nix is ideal for building build farms that do
|
<para>Since Nix on the other hand doesn’t install packages in “global”
|
||||||
continuous builds of software from a version management system, since
|
locations like <filename>/usr/bin</filename> but in package-specific
|
||||||
it can take care of building all the dependencies as well. Also, Nix
|
directories, the risk of incomplete dependencies is greatly reduced.
|
||||||
only rebuilds components that have changed, so there are no
|
This is because tools such as compilers don’t search in per-packages
|
||||||
unnecessary builds. In addition, Nix can transparently distribute
|
directories such as
|
||||||
build jobs over different machines, including different
|
<filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>,
|
||||||
platforms.</para></listitem>
|
so if a package builds correctly on your system, this is because you
|
||||||
|
specified the dependency explicitly.</para>
|
||||||
|
|
||||||
<listitem><para>Nix can be used not only for software deployment, but
|
<para>Runtime dependencies are found by scanning binaries for the hash
|
||||||
also for <emphasis>service deployment</emphasis>, such as the
|
parts of Nix store paths (such as <literal>r8vvq9kq…</literal>). This
|
||||||
deployment of a complete web server with all its configuration files,
|
sounds risky, but it works extremely well.</para>
|
||||||
static pages, software dependencies, and so on. Nix's advantages for
|
|
||||||
software deployment also apply here: for instance, the ability
|
|
||||||
trivially to have multiple configurations at the same time, or the
|
|
||||||
ability to do rollbacks.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Nix can efficiently upgrade between different versions
|
</simplesect>
|
||||||
of a component through <emphasis>binary patching</emphasis>. If
|
|
||||||
patches are available on a server, and you try to install a new
|
|
||||||
version of some component, Nix will automatically apply a patch (or
|
|
||||||
sequence of patches), if available, to transform the installed
|
|
||||||
component into the new version.</para></listitem>
|
|
||||||
|
|
||||||
</itemizedlist>
|
|
||||||
|
|
||||||
</para>
|
<simplesect><title>Multi-user support</title>
|
||||||
|
|
||||||
|
<para>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 <emphasis>profile</emphasis>, a set of
|
||||||
|
packages in the Nix store that appear in the user’s
|
||||||
|
<envar>PATH</envar>. 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.</para>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<para>More details can be found in Section 3 of our <a
|
||||||
|
href="docs/papers.html#securesharing">ASE 2005 paper</a>.</para>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Atomic upgrades and rollbacks</title>
|
||||||
|
|
||||||
|
<para>Since package management operations never overwrite packages in
|
||||||
|
the Nix store but just add new versions in different paths, they are
|
||||||
|
<emphasis>atomic</emphasis>. 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.</para>
|
||||||
|
|
||||||
|
<para>And since package aren’t overwritten, the old versions are still
|
||||||
|
there after an upgrade. This means that you can <emphasis>roll
|
||||||
|
back</emphasis> to the old version:</para>
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
$ nix-env --upgrade <replaceable>some-packages</replaceable>
|
||||||
|
$ nix-env --rollback
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Garbage collection</title>
|
||||||
|
|
||||||
|
<para>When you install a package like this…
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
$ nix-env --uninstall firefox
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
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
|
||||||
|
<emphasis>garbage collector</emphasis>:
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
$ nix-collect-garbage
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
This deletes all packages that aren’t in use by any user profile or by
|
||||||
|
a currently running program.</para>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Functional package language</title>
|
||||||
|
|
||||||
|
<para>Packages are built from <emphasis>Nix expressions</emphasis>,
|
||||||
|
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 <emphasis>deterministic</emphasis>: building a Nix
|
||||||
|
expression twice should yield the same result.</para>
|
||||||
|
|
||||||
|
<para>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.</para>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Transparent source/binary deployment</title>
|
||||||
|
|
||||||
|
<para>Nix expressions generally describe how to build a package from
|
||||||
|
source, so an installation action like
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
$ nix-env --install firefox
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
<emphasis>could</emphasis> 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 <emphasis>source deployment
|
||||||
|
model</emphasis>. 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. <emphasis>Nix channels</emphasis> provide Nix
|
||||||
|
expressions along with pre-built binaries.</para>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<para>source deployment model (like <a
|
||||||
|
href="http://www.gentoo.org/">Gentoo</a>) and a binary model (like
|
||||||
|
RPM)</para>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Binary patching</title>
|
||||||
|
|
||||||
|
<para>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.</para>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Nix Packages collection</title>
|
||||||
|
|
||||||
|
<para>We provide a large set of Nix expressions containing hundreds of
|
||||||
|
existing Unix packages, the <emphasis>Nix Packages
|
||||||
|
collection</emphasis> (Nixpkgs).</para>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Service deployment</title>
|
||||||
|
|
||||||
|
<para>Nix can be used not only for rolling out packages, but also
|
||||||
|
complete <emphasis>configurations</emphasis> 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.</para>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<para>You can read more about this in our <a
|
||||||
|
href="docs/papers.html#servicecm">SCM-12 paper</a>.</para>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>Portability</title>
|
||||||
|
|
||||||
|
<para>Nix should run on most Unix systems, including Linux, FreeBSD and
|
||||||
|
Mac OS X. It is also supported on Windows using Cygwin.</para>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<simplesect><title>NixOS</title>
|
||||||
|
|
||||||
|
<para>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
|
||||||
|
<filename>/etc</filename>). 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 <link
|
||||||
|
xlink:href="http://nix.cs.uu.nl/nixos/">NixOS homepage</link>.</para>
|
||||||
|
|
||||||
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- other features:
|
||||||
|
|
||||||
|
- build farms
|
||||||
|
- reproducibility (Nix expressions allows whole configuration to be rebuilt)
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<section><title>About us</title>
|
||||||
|
|
||||||
|
<para>Nix was developed at the <link
|
||||||
|
xlink:href="http://www.cs.uu.nl/">Department of Information and
|
||||||
|
Computing Sciences</link>, Utrecht University by the <link
|
||||||
|
xlink:href="http://www.cs.uu.nl/wiki/Trace/WebHome">TraCE
|
||||||
|
project</link>. The project is funded by the Software Engineering
|
||||||
|
Research Program <link
|
||||||
|
xlink:href="http://www.jacquard.nl/">Jacquard</link> to improve the
|
||||||
|
support for variability in software systems.</para>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<section><title>About this manual</title>
|
||||||
|
|
||||||
<para>This manual tells you how to install and use Nix and how to
|
<para>This manual tells you how to install and use Nix and how to
|
||||||
write Nix expressions for software not already in the Nix Packages
|
write Nix expressions for software not already in the Nix Packages
|
||||||
collection. It also discusses some advanced topics, such as setting
|
collection. It also discusses some advanced topics, such as setting
|
||||||
up a Nix-based build farm, and doing service deployment using
|
up a Nix-based build farm.</para>
|
||||||
Nix.</para>
|
|
||||||
|
|
||||||
<note><para>Some background information on Nix can be found in a
|
</section>
|
||||||
number of papers. The ICSE 2004 paper <citetitle
|
|
||||||
|
|
||||||
|
<section><title>License</title>
|
||||||
|
|
||||||
|
<para>Nix is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the <link
|
||||||
|
xlink:href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General
|
||||||
|
Public License</link> as published by the <link
|
||||||
|
xlink:href="http://www.fsf.org/">Free Software Foundation</link>;
|
||||||
|
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.</para>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<section><title>More information</title>
|
||||||
|
|
||||||
|
<para>Some background information on Nix can be found in a number of
|
||||||
|
papers. The ICSE 2004 paper <citetitle
|
||||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/immdsd-icse2004-final.pdf'>Imposing
|
xlink:href='http://www.cs.uu.nl/~eelco/pubs/immdsd-icse2004-final.pdf'>Imposing
|
||||||
a Memory Management Discipline on Software Deployment</citetitle>
|
a Memory Management Discipline on Software Deployment</citetitle>
|
||||||
discusses the hashing mechanism used to ensure reliable dependency
|
discusses the hashing mechanism used to ensure reliable dependency
|
||||||
|
|
@ -141,10 +310,27 @@ gives a more general discussion of Nix from a system-administration
|
||||||
perspective. The CBSE 2005 paper <citetitle
|
perspective. The CBSE 2005 paper <citetitle
|
||||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
|
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
|
||||||
Upgrading in a Purely Functional Component Deployment Model
|
Upgrading in a Purely Functional Component Deployment Model
|
||||||
</citetitle> is about transparent patch deployment in Nix. Finally,
|
</citetitle> is about transparent patch deployment in Nix. The SCM-12
|
||||||
the SCM-12 paper <citetitle
|
paper <citetitle
|
||||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
|
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
|
||||||
Service Configuration Management</citetitle> shows how services (e.g.,
|
Service Configuration Management</citetitle> shows how services (e.g.,
|
||||||
web servers) can be deployed and managed through Nix.</para></note>
|
web servers) can be deployed and managed through Nix. A short
|
||||||
|
overview of NixOS is given in the HotOS XI paper <citetitle
|
||||||
|
xlink:href="http://www.cs.uu.nl/~eelco/pubs/hotos-final.pdf">Purely
|
||||||
|
Functional System Configuration Management</citetitle>. The Nix
|
||||||
|
homepage has <link
|
||||||
|
xlink:href="http://nix.cs.uu.nl/docs/papers.html">an up-to-date list
|
||||||
|
of Nix-related papers</link>.</para>
|
||||||
|
|
||||||
|
<para>Nix is the subject of Eelco Dolstra’s PhD thesis <citetitle
|
||||||
|
xlink:href="http://igitur-archive.library.uu.nl/dissertations/2006-0118-200031/index.htm">The
|
||||||
|
Purely Functional Software Deployment Model</citetitle>, which
|
||||||
|
contains most of the papers listed above.</para>
|
||||||
|
|
||||||
|
<para>Nix has a homepage at <link
|
||||||
|
xlink:href="http://nix.cs.uu.nl/"/>.</para>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@
|
||||||
</group>
|
</group>
|
||||||
<replaceable>number</replaceable>
|
<replaceable>number</replaceable>
|
||||||
</arg>
|
</arg>
|
||||||
|
<arg>
|
||||||
|
<arg><option>--max-silent-time</option></arg>
|
||||||
|
<replaceable>number</replaceable>
|
||||||
|
</arg>
|
||||||
<arg><option>--keep-going</option></arg>
|
<arg><option>--keep-going</option></arg>
|
||||||
<arg><option>-k</option></arg>
|
<arg><option>-k</option></arg>
|
||||||
<arg><option>--keep-failed</option></arg>
|
<arg><option>--keep-failed</option></arg>
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,17 @@
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry xml:id="opt-max-silent-time"><term><option>--max-silent-time</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Sets the maximum number of seconds that a builder
|
||||||
|
can go without producing any data on standard output or standard
|
||||||
|
error. The default is specified by the <link
|
||||||
|
linkend='conf-build-max-silent-time'><literal>build-max-silent-time</literal></link>
|
||||||
|
configuration setting. <literal>0</literal> means no
|
||||||
|
time-out.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry><term><option>--keep-going</option></term>
|
<varlistentry><term><option>--keep-going</option></term>
|
||||||
<term><option>-k</option></term>
|
<term><option>-k</option></term>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,8 @@
|
||||||
output is already in the Nix store or that can be substituted (i.e.,
|
output is already in the Nix store or that can be substituted (i.e.,
|
||||||
downloaded from somewhere). In other words, it shows the packages
|
downloaded from somewhere). In other words, it shows the packages
|
||||||
that can be installed “quickly”, i.e., don’t need to be built from
|
that can be installed “quickly”, i.e., don’t need to be built from
|
||||||
source.</para></listitem>
|
source. TODO: flag is also available in nix-env -i /
|
||||||
|
-u.</para></listitem>
|
||||||
|
|
||||||
|
|
||||||
<listitem><para>TODO: new built-ins
|
<listitem><para>TODO: new built-ins
|
||||||
|
|
@ -116,6 +117,9 @@
|
||||||
limited form of caching. This is used by
|
limited form of caching. This is used by
|
||||||
<command>nix-channel</command> to prevent unnecessary downloads when
|
<command>nix-channel</command> to prevent unnecessary downloads when
|
||||||
the channel hasn’t changed.</para></listitem>
|
the channel hasn’t changed.</para></listitem>
|
||||||
|
|
||||||
|
|
||||||
|
<listitem><para>TODO: chroot support.</para></listitem>
|
||||||
|
|
||||||
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,11 @@ h3 /* subsections */
|
||||||
font-size: 125%;
|
font-size: 125%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.simplesect h2
|
||||||
|
{
|
||||||
|
font-size: 110%;
|
||||||
|
}
|
||||||
|
|
||||||
div.appendix h3
|
div.appendix h3
|
||||||
{
|
{
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
xml:id='chap-writing-nix-expressions'>
|
xml:id='chap-writing-nix-expressions'
|
||||||
|
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||||
|
|
||||||
<title>Writing Nix Expressions</title>
|
<title>Writing Nix Expressions</title>
|
||||||
|
|
||||||
|
|
@ -1339,589 +1340,8 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section><title>Built-in functions</title>
|
|
||||||
|
|
||||||
<para>This section lists the functions and constants built into the
|
<xi:include href="builtins.xml" />
|
||||||
Nix expression evaluator. (The built-in function
|
|
||||||
<function>derivation</function> is discussed above.) Some built-ins,
|
|
||||||
such as <function>derivation</function>, are always in scope of every
|
|
||||||
Nix expression; you can just access them right away. But to prevent
|
|
||||||
polluting the namespace too much, most built-ins are not in scope.
|
|
||||||
Instead, you can access them through the <varname>builtins</varname>
|
|
||||||
built-in value, which is an attribute set that contains all built-in
|
|
||||||
functions and values. For instance, <function>derivation</function>
|
|
||||||
is also available as <function>builtins.derivation</function>.</para>
|
|
||||||
|
|
||||||
|
|
||||||
<variablelist>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>abort</function> <replaceable>s</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Abort Nix expression evaluation, print error
|
|
||||||
message <replaceable>s</replaceable>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.add</function>
|
|
||||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return the sum of the integers
|
|
||||||
<replaceable>e1</replaceable> and
|
|
||||||
<replaceable>e2</replaceable>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.attrNames</function>
|
|
||||||
<replaceable>attrs</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return the names of the attributes in the
|
|
||||||
attribute set <replaceable>attrs</replaceable> in a sorted list.
|
|
||||||
For instance, <literal>builtins.attrNames {y = 1; x =
|
|
||||||
"foo";}</literal> evaluates to <literal>["x" "y"]</literal>.
|
|
||||||
There is no built-in function <function>attrValues</function>, but
|
|
||||||
you can easily define it yourself:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs);</programlisting>
|
|
||||||
|
|
||||||
</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return the <emphasis>base name</emphasis> of the
|
|
||||||
string <replaceable>s</replaceable>, that is, everything following
|
|
||||||
the final slash in the string. This is similar to the GNU
|
|
||||||
<command>basename</command> command.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><varname>builtins</varname></term>
|
|
||||||
|
|
||||||
<listitem><para>The attribute set <varname>builtins</varname>
|
|
||||||
contains all the built-in functions and values. You can use
|
|
||||||
<varname>builtins</varname> to test for the availability of
|
|
||||||
features in the Nix installation, e.g.,
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
|
||||||
|
|
||||||
This allows a Nix expression to fall back gracefully on older Nix
|
|
||||||
installations that don’t have the desired built-in function.
|
|
||||||
However, in that case you should not write
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
|
|
||||||
|
|
||||||
This Nix expression will trigger an “undefined variable” error on
|
|
||||||
older Nix versions since <function>__getEnv</function> doesn’t
|
|
||||||
exist. <literal>builtins.getEnv</literal>, on the other hand, is
|
|
||||||
safe since <literal>builtins</literal> always exists and attribute
|
|
||||||
selection is lazy, so it’s only performed if the test
|
|
||||||
succeeds.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry
|
|
||||||
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
|
|
||||||
|
|
||||||
<listitem><para>The built-in value <varname>currentSystem</varname>
|
|
||||||
evaluates to the Nix platform identifier for the Nix installation
|
|
||||||
on which the expression is being evaluated, such as
|
|
||||||
<literal>"i686-linux"</literal> or
|
|
||||||
<literal>"powerpc-darwin"</literal>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<varlistentry><term><function>currentTime</function></term>
|
|
||||||
|
|
||||||
<listitem><para>The built-in value <varname>currentTime</varname>
|
|
||||||
returns the current system time in seconds since 00:00:00 1/1/1970
|
|
||||||
UTC. Due to the evaluation model of Nix expressions
|
|
||||||
(<emphasis>maximal laziness</emphasis>), it always yields the same
|
|
||||||
value within an execution of Nix.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<varlistentry><term><function>dependencyClosure</function></term>
|
|
||||||
|
|
||||||
<listitem><para>TODO</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>derivation</function>
|
|
||||||
<replaceable>attrs</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para><function>derivation</function> is described in
|
|
||||||
<xref linkend='ssec-derivation' />.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>dirOf</function> <replaceable>s</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return the directory part of the string
|
|
||||||
<replaceable>s</replaceable>, that is, everything before the final
|
|
||||||
slash in the string. This is similar to the GNU
|
|
||||||
<command>dirname</command> command.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.getAttr</function>
|
|
||||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para><function>getAttr</function> returns the attribute
|
|
||||||
named <replaceable>s</replaceable> from the attribute set
|
|
||||||
<replaceable>attrs</replaceable>. Evaluation aborts if the
|
|
||||||
attribute doesn’t exist. This is a dynamic version of the
|
|
||||||
<literal>.</literal> operator, since <replaceable>s</replaceable>
|
|
||||||
is an expression rather than an identifier.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.getEnv</function>
|
|
||||||
<replaceable>s</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para><function>getEnv</function> returns the value of
|
|
||||||
the environment variable <replaceable>s</replaceable>, or an empty
|
|
||||||
string if the variable doesn’t exist. This function should be
|
|
||||||
used with care, as it can introduce all sorts of nasty environment
|
|
||||||
dependencies in your Nix expression.</para>
|
|
||||||
|
|
||||||
<para><function>getEnv</function> is used in Nix Packages to
|
|
||||||
locate the file <filename>~/.nixpkgs/config.nix</filename>, which
|
|
||||||
contains user-local settings for Nix Packages. (That is, it does
|
|
||||||
a <literal>getEnv "HOME"</literal> to locate the user’s home
|
|
||||||
directory.)</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.hasAttr</function>
|
|
||||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para><function>hasAttr</function> returns
|
|
||||||
<literal>true</literal> if the attribute set
|
|
||||||
<replaceable>attrs</replaceable> has an attribute named
|
|
||||||
<replaceable>s</replaceable>, and <literal>false</literal>
|
|
||||||
otherwise. This is a dynamic version of the <literal>?</literal>
|
|
||||||
operator, since <replaceable>s</replaceable> is an expression
|
|
||||||
rather than an identifier.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.head</function>
|
|
||||||
<replaceable>list</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return the first element of a list; abort
|
|
||||||
evaluation if the argument isn’t a list or is an empty list. You
|
|
||||||
can test whether a list is empty by comparing it with
|
|
||||||
<literal>[]</literal>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>import</function>
|
|
||||||
<replaceable>path</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Load, parse and return the Nix expression in the
|
|
||||||
file <replaceable>path</replaceable>. Evaluation aborts if the
|
|
||||||
file doesn’t exist or contains an incorrect Nix
|
|
||||||
expression. <function>import</function> implements Nix’s module
|
|
||||||
system: you can put any Nix expression (such as an attribute set
|
|
||||||
or a function) in a separate file, and use it from Nix expressions
|
|
||||||
in other files.</para>
|
|
||||||
|
|
||||||
<para>A Nix expression loaded by <function>import</function> must
|
|
||||||
not contain any <emphasis>free variables</emphasis> (identifiers
|
|
||||||
that are not defined in the Nix expression itself and are not
|
|
||||||
built-in). Therefore, it cannot refer to variables that are in
|
|
||||||
scope at the call site. For instance, if you have a calling
|
|
||||||
expression
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
rec {
|
|
||||||
x = 123;
|
|
||||||
y = import ./foo.nix;
|
|
||||||
}</programlisting>
|
|
||||||
|
|
||||||
then the following <filename>foo.nix</filename> will give an
|
|
||||||
error:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
x + 456</programlisting>
|
|
||||||
|
|
||||||
since <varname>x</varname> is not in scope in
|
|
||||||
<filename>foo.nix</filename>. If you want <varname>x</varname>
|
|
||||||
to be available in <filename>foo.nix</filename>, you should pass
|
|
||||||
it as a function argument:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
rec {
|
|
||||||
x = 123;
|
|
||||||
y = import ./foo.nix x;
|
|
||||||
}</programlisting>
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
x: x + 456</programlisting>
|
|
||||||
|
|
||||||
(The function argument doesn’t have to be called
|
|
||||||
<varname>x</varname> in <filename>foo.nix</filename>; any name
|
|
||||||
would work.)</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.isList</function>
|
|
||||||
<replaceable>e</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return <literal>true</literal> if
|
|
||||||
<replaceable>e</replaceable> evaluates to a list, and
|
|
||||||
<literal>false</literal> otherwise.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>isNull</function>
|
|
||||||
<replaceable>e</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return <literal>true</literal> if
|
|
||||||
<replaceable>e</replaceable> evaluates to <literal>null</literal>,
|
|
||||||
and <literal>false</literal> otherwise.</para>
|
|
||||||
|
|
||||||
<warning><para>This function is <emphasis>deprecated</emphasis>;
|
|
||||||
just write <literal>e == null</literal> instead.</para></warning>
|
|
||||||
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.lessThan</function>
|
|
||||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return <literal>true</literal> if the integer
|
|
||||||
<replaceable>e1</replaceable> is less than the integer
|
|
||||||
<replaceable>e2</replaceable>, and <literal>false</literal>
|
|
||||||
otherwise. Evaluation aborts if either
|
|
||||||
<replaceable>e1</replaceable> or <replaceable>e2</replaceable>
|
|
||||||
does not evaluate to an integer.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>map</function>
|
|
||||||
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Apply the function <replaceable>f</replaceable> to
|
|
||||||
each element in the list <replaceable>list</replaceable>. For
|
|
||||||
example,
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
map (x: "foo" + x) ["bar" "bla" "abc"]</programlisting>
|
|
||||||
|
|
||||||
evaluates to <literal>["foobar" "foobla"
|
|
||||||
"fooabc"]</literal>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.pathExists</function>
|
|
||||||
<replaceable>path</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return <literal>true</literal> if the path
|
|
||||||
<replaceable>path</replaceable> exists, and
|
|
||||||
<literal>false</literal> otherwise. One application of this
|
|
||||||
function is to conditionally include a Nix expression containing
|
|
||||||
user configuration:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
let
|
|
||||||
fileName = builtins.getEnv "CONFIG_FILE";
|
|
||||||
config =
|
|
||||||
if fileName != "" && builtins.pathExists (builtins.toPath fileName)
|
|
||||||
then import (builtins.toPath fileName)
|
|
||||||
else { someSetting = false; }; <lineannotation># default configuration</lineannotation>
|
|
||||||
in config.someSetting</programlisting>
|
|
||||||
|
|
||||||
(Note that <envar>CONFIG_FILE</envar> must be an absolute path for
|
|
||||||
this to work.)</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<varlistentry><term><function>relativise</function></term>
|
|
||||||
|
|
||||||
<listitem><para>TODO</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>removeAttrs</function>
|
|
||||||
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Remove the attributes listed in
|
|
||||||
<replaceable>list</replaceable> from the attribute set
|
|
||||||
<replaceable>attrs</replaceable>. The attributes don’t have to
|
|
||||||
exist in <replaceable>attrs</replaceable>. For instance,
|
|
||||||
|
|
||||||
<screen>
|
|
||||||
removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"]</screen>
|
|
||||||
|
|
||||||
evaluates to <literal>{y = 2;}</literal>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.tail</function>
|
|
||||||
<replaceable>list</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return the second to last elements of a list;
|
|
||||||
abort evaluation if the argument isn’t a list or is an empty
|
|
||||||
list.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry
|
|
||||||
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
|
|
||||||
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Store the string <replaceable>s</replaceable> in a
|
|
||||||
file in the Nix store and return its path. The file has suffix
|
|
||||||
<replaceable>name</replaceable>. This file can be used as an
|
|
||||||
input to derivations. One application is to write builders
|
|
||||||
“inline”. For instance, the following Nix expression combines
|
|
||||||
<xref linkend='ex-hello-nix' /> and <xref
|
|
||||||
linkend='ex-hello-builder' /> into one file:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
{stdenv, fetchurl, perl}:
|
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
|
||||||
name = "hello-2.1.1";
|
|
||||||
|
|
||||||
builder = builtins.toFile "builder.sh" "
|
|
||||||
source $stdenv/setup
|
|
||||||
|
|
||||||
PATH=$perl/bin:$PATH
|
|
||||||
|
|
||||||
tar xvfz $src
|
|
||||||
cd hello-*
|
|
||||||
./configure --prefix=$out
|
|
||||||
make
|
|
||||||
make install
|
|
||||||
";
|
|
||||||
|
|
||||||
src = fetchurl {
|
|
||||||
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
|
|
||||||
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
|
||||||
};
|
|
||||||
inherit perl;
|
|
||||||
}</programlisting>
|
|
||||||
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>It is even possible for one file to refer to another, e.g.,
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
builder = let
|
|
||||||
configFile = builtins.toFile "foo.conf" "
|
|
||||||
# This is some dummy configuration file.
|
|
||||||
<replaceable>...</replaceable>
|
|
||||||
";
|
|
||||||
in builtins.toFile "builder.sh" "
|
|
||||||
source $stdenv/setup
|
|
||||||
<replaceable>...</replaceable>
|
|
||||||
cp ${configFile} $out/etc/foo.conf
|
|
||||||
";</programlisting>
|
|
||||||
|
|
||||||
Note that <literal>${configFile}</literal> is an antiquotation
|
|
||||||
(see <xref linkend='ssec-values' />), so the result of the
|
|
||||||
expression <literal>configFile</literal> (i.e., a path like
|
|
||||||
<filename>/nix/store/m7p7jfny445k...-foo.conf</filename>) will be
|
|
||||||
spliced into the resulting string.</para>
|
|
||||||
|
|
||||||
<para>It is however <emphasis>not</emphasis> allowed to have files
|
|
||||||
mutually referring to each other, like so:
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
let
|
|
||||||
foo = builtins.toFile "foo" "...${bar}...";
|
|
||||||
bar = builtins.toFile "bar" "...${foo}...";
|
|
||||||
in foo</programlisting>
|
|
||||||
|
|
||||||
This is not allowed because it would cause a cyclic dependency in
|
|
||||||
the computation of the cryptographic hashes for
|
|
||||||
<varname>foo</varname> and <varname>bar</varname>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Convert the string value
|
|
||||||
<replaceable>s</replaceable> into a path value. The string
|
|
||||||
<replaceable>s</replaceable> must represent an absolute path
|
|
||||||
(i.e., must start with <literal>/</literal>). The path need not
|
|
||||||
exist. The resulting path is canonicalised, e.g.,
|
|
||||||
<literal>builtins.toPath "//foo/xyzzy/../bar/"</literal> returns
|
|
||||||
<literal>/foo/bar</literal>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Convert the expression
|
|
||||||
<replaceable>e</replaceable> to a string.
|
|
||||||
<replaceable>e</replaceable> can be a string (in which case
|
|
||||||
<function>toString</function> is a no-op) or a path (e.g.,
|
|
||||||
<literal>toString /foo/bar</literal> yields
|
|
||||||
<literal>"/foo/bar"</literal>.</para></listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
|
|
||||||
|
|
||||||
<listitem><para>Return a string containing an XML representation
|
|
||||||
of <replaceable>e</replaceable>. The main application for
|
|
||||||
<function>toXML</function> is to communicate information with the
|
|
||||||
builder in a more structured format than plain environment
|
|
||||||
variables.</para>
|
|
||||||
|
|
||||||
<!-- TODO: more formally describe the schema of the XML
|
|
||||||
representation -->
|
|
||||||
|
|
||||||
<para><xref linkend='ex-toxml' /> shows an example where this is
|
|
||||||
the case. The builder is supposed to generate the configuration
|
|
||||||
file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
|
|
||||||
servlet container</link>. A servlet container contains a number
|
|
||||||
of servlets (<filename>*.war</filename> files) each exported under
|
|
||||||
a specific URI prefix. So the servlet configuration is a list of
|
|
||||||
attribute sets containing the <varname>path</varname> and
|
|
||||||
<varname>war</varname> of the servlet (<xref
|
|
||||||
linkend='ex-toxml-co-servlets' />). This kind of information is
|
|
||||||
difficult to communicate with the normal method of passing
|
|
||||||
information through an environment variable, which just
|
|
||||||
concatenates everything together into a string (which might just
|
|
||||||
work in this case, but wouldn’t work if fields are optional or
|
|
||||||
contain lists themselves). Instead the Nix expression is
|
|
||||||
converted to an XML representation with
|
|
||||||
<function>toXML</function>, which is unambiguous and can easily be
|
|
||||||
processed with the appropriate tools. For instance, in the
|
|
||||||
example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
|
|
||||||
/>) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
|
|
||||||
generate the XML configuration file for the Jetty server. The XML
|
|
||||||
representation produced from <xref linkend='ex-toxml-co-servlets'
|
|
||||||
/> by <function>toXML</function> is shown in <xref
|
|
||||||
linkend='ex-toxml-result' />.</para>
|
|
||||||
|
|
||||||
<para>Note that <xref linkend='ex-toxml' /> uses the <function
|
|
||||||
linkend='builtin-toFile'>toFile</function> built-in to write the
|
|
||||||
builder and the stylesheet “inline” in the Nix expression. The
|
|
||||||
path of the stylesheet is spliced into the builder at
|
|
||||||
<literal>xsltproc ${stylesheet}
|
|
||||||
<replaceable>...</replaceable></literal>.</para>
|
|
||||||
|
|
||||||
<example xml:id='ex-toxml'><title>Passing information to a builder
|
|
||||||
using <function>toXML</function></title>
|
|
||||||
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
{stdenv, fetchurl, libxslt, jira, uberwiki}:
|
|
||||||
|
|
||||||
stdenv.mkDerivation (rec {
|
|
||||||
name = "web-server";
|
|
||||||
|
|
||||||
buildInputs = [libxslt];
|
|
||||||
|
|
||||||
builder = builtins.toFile "builder.sh" "
|
|
||||||
source $stdenv/setup
|
|
||||||
mkdir $out
|
|
||||||
echo $servlets | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
|
|
||||||
";
|
|
||||||
|
|
||||||
stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
|
|
||||||
"<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
|
|
||||||
<xsl:template match='/'>
|
|
||||||
<Configure>
|
|
||||||
<xsl:for-each select='/expr/list/attrs'>
|
|
||||||
<Call name='addWebApplication'>
|
|
||||||
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
|
|
||||||
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
|
|
||||||
</Call>
|
|
||||||
</xsl:for-each>
|
|
||||||
</Configure>
|
|
||||||
</xsl:template>
|
|
||||||
</xsl:stylesheet>
|
|
||||||
";
|
|
||||||
|
|
||||||
servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
|
|
||||||
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
|
|
||||||
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
|
|
||||||
];
|
|
||||||
})]]></programlisting>
|
|
||||||
|
|
||||||
</example>
|
|
||||||
|
|
||||||
<example xml:id='ex-toxml-result'><title>XML representation produced by
|
|
||||||
<function>toXML</function></title>
|
|
||||||
|
|
||||||
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<expr>
|
|
||||||
<list>
|
|
||||||
<attrs>
|
|
||||||
<attr name="path">
|
|
||||||
<string value="/bugtracker" />
|
|
||||||
</attr>
|
|
||||||
<attr name="war">
|
|
||||||
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
|
|
||||||
</attr>
|
|
||||||
</attrs>
|
|
||||||
<attrs>
|
|
||||||
<attr name="path">
|
|
||||||
<string value="/wiki" />
|
|
||||||
</attr>
|
|
||||||
<attr name="war">
|
|
||||||
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
|
|
||||||
</attr>
|
|
||||||
</attrs>
|
|
||||||
</list>
|
|
||||||
</expr>]]></programlisting>
|
|
||||||
|
|
||||||
</example>
|
|
||||||
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
|
|
||||||
</variablelist>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
svn merge -r 9476:9506 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk
|
svn merge -r 9549:9561 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk
|
||||||
|
|
||||||
#already done:
|
#already done:
|
||||||
# 8628
|
# 8628
|
||||||
|
|
@ -23,3 +23,6 @@ svn merge -r 9476:9506 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk
|
||||||
# 9445
|
# 9445
|
||||||
# 9476
|
# 9476
|
||||||
# 9506
|
# 9506
|
||||||
|
# 9536
|
||||||
|
# 9549
|
||||||
|
# 9561
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
|
|
||||||
### Option `build-max-silent-time'
|
### Option `build-max-silent-time'
|
||||||
#
|
#
|
||||||
# This option defines the maximum number of seconds that builder can
|
# This option defines the maximum number of seconds that a builder can
|
||||||
# go without producing any data on standard output or standard error.
|
# go without producing any data on standard output or standard error.
|
||||||
# This is useful (for instance in a automated build system) to catch
|
# This is useful (for instance in a automated build system) to catch
|
||||||
# builds that are stuck in an infinite loop, or to catch remote builds
|
# builds that are stuck in an infinite loop, or to catch remote builds
|
||||||
|
|
@ -135,6 +135,44 @@
|
||||||
#build-users-group =
|
#build-users-group =
|
||||||
|
|
||||||
|
|
||||||
|
### Option `build-use-chroot'
|
||||||
|
#
|
||||||
|
# If set to `true', builds will be performed in a chroot environment,
|
||||||
|
# i.e., the build will be isolated from the normal file system
|
||||||
|
# hierarchy and will only see the Nix store, the temporary build
|
||||||
|
# directory, and the directories configured with the
|
||||||
|
# `build-chroot-dirs' option (such as /proc and /dev). This is useful
|
||||||
|
# to prevent undeclared dependencies on files in directories such as
|
||||||
|
# /usr/bin.
|
||||||
|
#
|
||||||
|
# The use of a chroot requires that Nix is run as root (but you can
|
||||||
|
# still use the "build users" feature to perform builds under
|
||||||
|
# different users than root). Currently, chroot builds only work on
|
||||||
|
# Linux because Nix uses "bind mounts" to make the Nix store and other
|
||||||
|
# directories available inside the chroot.
|
||||||
|
#
|
||||||
|
# The default is `false'.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# build-use-chroot = true
|
||||||
|
#build-use-chroot = false
|
||||||
|
|
||||||
|
|
||||||
|
### Option `build-chroot-dirs'
|
||||||
|
#
|
||||||
|
# When builds are performed in a chroot environment, Nix will mount
|
||||||
|
# (using `mount --bind' on Linux) some directories from the normal
|
||||||
|
# file system hierarchy inside the chroot. These are the Nix store,
|
||||||
|
# the temporary build directory (usually /tmp/nix-<pid>-<number>) and
|
||||||
|
# the directories listed here. The default is "/dev /proc". Files
|
||||||
|
# in /dev (such as /dev/null) are needed by many builds, and some
|
||||||
|
# files in /proc may also be needed occasionally.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# build-use-chroot = /dev /proc /bin
|
||||||
|
#build-chroot-dirs = /dev /proc
|
||||||
|
|
||||||
|
|
||||||
### Option `system'
|
### Option `system'
|
||||||
#
|
#
|
||||||
# This option specifies the canonical Nix system name of the current
|
# This option specifies the canonical Nix system name of the current
|
||||||
|
|
|
||||||
|
|
@ -298,18 +298,19 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args)
|
||||||
return makeStr(getEnv(name));
|
return makeStr(getEnv(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for debugging purposes. print the first arg on stdout (perhaps stderr should be used?)
|
|
||||||
* and return the second
|
/* Evaluate the first expression, and print its abstract syntax tree
|
||||||
|
on standard error. Then return the second expression. Useful for
|
||||||
|
debugging.
|
||||||
*/
|
*/
|
||||||
static Expr prim_trace(EvalState & state, const ATermVector & args)
|
static Expr prim_trace(EvalState & state, const ATermVector & args)
|
||||||
{
|
{
|
||||||
//string str = evalStringNoCtx(state, args[0]);
|
Expr e = evalExpr(state, args[0]);
|
||||||
|
printMsg(lvlError, format("trace: %1%") % e);
|
||||||
Expr a = evalExpr(state, args[0]);
|
|
||||||
printf("traced value: %s\n", atPrint(a).c_str());
|
|
||||||
return evalExpr(state, args[1]);
|
return evalExpr(state, args[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr prim_relativise(EvalState & state, const ATermVector & args)
|
static Expr prim_relativise(EvalState & state, const ATermVector & args)
|
||||||
{
|
{
|
||||||
PathSet context; /* !!! what to do? */
|
PathSet context; /* !!! what to do? */
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,19 @@
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Includes required for chroot support. */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if HAVE_SYS_PARAM_H
|
||||||
|
#include <sys/param.h>
|
||||||
|
#endif
|
||||||
|
#if HAVE_SYS_MOUNT_H
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND)
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
using std::map;
|
using std::map;
|
||||||
|
|
@ -585,6 +598,89 @@ void deletePathWrapped(const Path & path)
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/* Helper RAII class for automatically unmounting bind-mounts in
|
||||||
|
chroots. */
|
||||||
|
struct BindMount
|
||||||
|
{
|
||||||
|
Path source, target;
|
||||||
|
Paths created;
|
||||||
|
|
||||||
|
BindMount()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BindMount(const Path & source, const Path & target)
|
||||||
|
{
|
||||||
|
bind(source, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
~BindMount()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
unbind();
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(const Path & source, const Path & target)
|
||||||
|
{
|
||||||
|
#if CHROOT_ENABLED
|
||||||
|
debug(format("bind mounting `%1%' to `%2%'") % source % target);
|
||||||
|
|
||||||
|
this->source = source;
|
||||||
|
this->target = target;
|
||||||
|
|
||||||
|
created = createDirs(target);
|
||||||
|
|
||||||
|
if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
|
||||||
|
throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbind()
|
||||||
|
{
|
||||||
|
#if CHROOT_ENABLED
|
||||||
|
if (source == "") return;
|
||||||
|
|
||||||
|
debug(format("unmount bind-mount `%1%'") % target);
|
||||||
|
|
||||||
|
/* Urgh. Unmount sometimes doesn't succeed right away because
|
||||||
|
the mount point is still busy. It shouldn't be, because
|
||||||
|
we've killed all the build processes by now (at least when
|
||||||
|
using a build user; see the check in killUser()). But
|
||||||
|
maybe this is because those processes are still zombies and
|
||||||
|
are keeping some kernel structures busy (open files,
|
||||||
|
current directories, etc.). So retry a few times
|
||||||
|
(actually, a 1 second sleep is almost certainly enough for
|
||||||
|
the zombies to be cleaned up). */
|
||||||
|
unsigned int tries = 0;
|
||||||
|
while (umount(target.c_str()) == -1) {
|
||||||
|
if (errno == EBUSY && ++tries < 10) {
|
||||||
|
printMsg(lvlError, format("unmounting `%1%' failed, retrying after 1 second...") % target);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw SysError(format("unmounting bind-mount `%1%' failed") % target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get rid of the directories for the mount point created in
|
||||||
|
bind(). */
|
||||||
|
for (Paths::reverse_iterator i = created.rbegin(); i != created.rend(); ++i) {
|
||||||
|
debug(format("deleting `%1%'") % *i);
|
||||||
|
if (remove(i->c_str()) == -1)
|
||||||
|
throw SysError(format("cannot unlink `%1%'") % *i);
|
||||||
|
}
|
||||||
|
|
||||||
|
source = "";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
class DerivationGoal : public Goal
|
class DerivationGoal : public Goal
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
@ -635,6 +731,17 @@ private:
|
||||||
Pipe toHook;
|
Pipe toHook;
|
||||||
Pipe fromHook;
|
Pipe fromHook;
|
||||||
|
|
||||||
|
/* Whether we're currently doing a chroot build. */
|
||||||
|
bool useChroot;
|
||||||
|
|
||||||
|
/* A RAII object to delete the chroot directory. */
|
||||||
|
boost::shared_ptr<AutoDelete> autoDelChroot;
|
||||||
|
|
||||||
|
/* In chroot builds, the list of bind mounts currently active.
|
||||||
|
The destructor of BindMount will cause the binds to be
|
||||||
|
unmounted. */
|
||||||
|
list<boost::shared_ptr<BindMount> > bindMounts;
|
||||||
|
|
||||||
typedef void (DerivationGoal::*GoalState)();
|
typedef void (DerivationGoal::*GoalState)();
|
||||||
GoalState state;
|
GoalState state;
|
||||||
|
|
||||||
|
|
@ -690,7 +797,7 @@ private:
|
||||||
void openLogFile();
|
void openLogFile();
|
||||||
|
|
||||||
/* Common initialisation to be performed in child processes (i.e.,
|
/* Common initialisation to be performed in child processes (i.e.,
|
||||||
both in builders and in build hooks. */
|
both in builders and in build hooks). */
|
||||||
void initChild();
|
void initChild();
|
||||||
|
|
||||||
/* Delete the temporary directory, if we have one. */
|
/* Delete the temporary directory, if we have one. */
|
||||||
|
|
@ -1038,6 +1145,9 @@ void DerivationGoal::buildDone()
|
||||||
|
|
||||||
deleteTmpDir(true);
|
deleteTmpDir(true);
|
||||||
|
|
||||||
|
/* In chroot builds, unmount the bind mounts ASAP. */
|
||||||
|
bindMounts.clear(); /* the destructors will do the rest */
|
||||||
|
|
||||||
/* Compute the FS closure of the outputs and register them as
|
/* Compute the FS closure of the outputs and register them as
|
||||||
being valid. */
|
being valid. */
|
||||||
computeClosure();
|
computeClosure();
|
||||||
|
|
@ -1208,7 +1318,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||||
throw SysError(format("executing `%1%'") % buildHook);
|
throw SysError(format("executing `%1%'") % buildHook);
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
std::cerr << format("build hook error: %1%\n") % e.what();
|
std::cerr << format("build hook error: %1%") % e.what() << std::endl;
|
||||||
}
|
}
|
||||||
quickExit(1);
|
quickExit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -1596,6 +1706,56 @@ void DerivationGoal::startBuilder()
|
||||||
% buildUser.getGID() % nixStore);
|
% buildUser.getGID() % nixStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Are we doing a chroot build? */
|
||||||
|
useChroot = queryBoolSetting("build-use-chroot", false);
|
||||||
|
Path tmpRootDir;
|
||||||
|
|
||||||
|
if (useChroot) {
|
||||||
|
#if CHROOT_ENABLED
|
||||||
|
/* Create a temporary directory in which we set up the chroot
|
||||||
|
environment using bind-mounts.
|
||||||
|
|
||||||
|
!!! Big danger here: since we're doing this in /tmp, there
|
||||||
|
is a risk that the admin does something like "rm -rf
|
||||||
|
/tmp/chroot-nix-*" to clean up aborted builds, and if some
|
||||||
|
of the bind-mounts are still active, then "rm -rf" will
|
||||||
|
happily recurse into those mount points (thereby deleting,
|
||||||
|
say, /nix/store). Ideally, tmpRootDir should be created in
|
||||||
|
some special location (maybe in /nix/var/nix) where Nix
|
||||||
|
takes care of unmounting / deleting old chroots
|
||||||
|
automatically. */
|
||||||
|
tmpRootDir = createTempDir("", "chroot-nix");
|
||||||
|
|
||||||
|
/* Clean up the chroot directory automatically, but don't
|
||||||
|
recurse; that would be very very bad if the unmount of a
|
||||||
|
bind-mount fails. Instead BindMount::unbind() unmounts and
|
||||||
|
deletes exactly those directories that it created to
|
||||||
|
produce the mount point, so that after all the BindMount
|
||||||
|
destructors have run, tmpRootDir should be empty. */
|
||||||
|
autoDelChroot = boost::shared_ptr<AutoDelete>(new AutoDelete(tmpRootDir, false));
|
||||||
|
|
||||||
|
printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % tmpRootDir);
|
||||||
|
|
||||||
|
Paths defaultDirs;
|
||||||
|
defaultDirs.push_back("/dev");
|
||||||
|
defaultDirs.push_back("/proc");
|
||||||
|
Paths dirsInChroot = querySetting("build-chroot-dirs", defaultDirs);
|
||||||
|
|
||||||
|
dirsInChroot.push_front(nixStore);
|
||||||
|
dirsInChroot.push_front(tmpDir);
|
||||||
|
|
||||||
|
/* Push BindMounts at the front of the list so that they get
|
||||||
|
unmounted in LIFO order. (!!! Does the C++ standard
|
||||||
|
guarantee that list elements are destroyed in order?) */
|
||||||
|
for (Paths::iterator i = dirsInChroot.begin(); i != dirsInChroot.end(); ++i)
|
||||||
|
bindMounts.push_front(boost::shared_ptr<BindMount>(new BindMount(*i, tmpRootDir + *i)));
|
||||||
|
|
||||||
|
#else
|
||||||
|
throw Error("chroot builds are not supported on this platform");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Run the builder. */
|
/* Run the builder. */
|
||||||
printMsg(lvlChatty, format("executing builder `%1%'") %
|
printMsg(lvlChatty, format("executing builder `%1%'") %
|
||||||
|
|
@ -1621,6 +1781,17 @@ void DerivationGoal::startBuilder()
|
||||||
|
|
||||||
try { /* child */
|
try { /* child */
|
||||||
|
|
||||||
|
#if CHROOT_ENABLED
|
||||||
|
/* If building in a chroot, do the chroot right away.
|
||||||
|
initChild() will do a chdir() to the temporary build
|
||||||
|
directory to make sure the current directory is in the
|
||||||
|
chroot. (Actually the order doesn't matter, since due
|
||||||
|
to the bind mount tmpDir and tmpRootDit/tmpDir are the
|
||||||
|
same directories.) */
|
||||||
|
if (useChroot && chroot(tmpRootDir.c_str()) == -1)
|
||||||
|
throw SysError(format("cannot change root directory to `%1%'") % tmpRootDir);
|
||||||
|
#endif
|
||||||
|
|
||||||
initChild();
|
initChild();
|
||||||
|
|
||||||
/* Fill in the environment. */
|
/* Fill in the environment. */
|
||||||
|
|
@ -1684,7 +1855,7 @@ void DerivationGoal::startBuilder()
|
||||||
% drv.builder);
|
% drv.builder);
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
std::cerr << format("build error: %1%\n") % e.what();
|
std::cerr << format("build error: %1%") % e.what() << std::endl;
|
||||||
}
|
}
|
||||||
quickExit(1);
|
quickExit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -1916,13 +2087,6 @@ void DerivationGoal::computeClosure()
|
||||||
state_stateReferences,
|
state_stateReferences,
|
||||||
drvPath, 0);
|
drvPath, 0);
|
||||||
|
|
||||||
//Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO)
|
|
||||||
RevisionClosure rivisionMapping;
|
|
||||||
rivisionMapping[statePath] = commitStatePathTxn(txn, statePath);
|
|
||||||
|
|
||||||
//Save the new revision
|
|
||||||
setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision.");
|
|
||||||
|
|
||||||
//Convert stateInfo from drv to DB format
|
//Convert stateInfo from drv to DB format
|
||||||
//And set all interval-ed paths to zero to begin with
|
//And set all interval-ed paths to zero to begin with
|
||||||
DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs;
|
DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs;
|
||||||
|
|
@ -1953,6 +2117,13 @@ void DerivationGoal::computeClosure()
|
||||||
//register state options that may change
|
//register state options that may change
|
||||||
DerivationStateOutput drvso = drv.stateOutputs["state"];
|
DerivationStateOutput drvso = drv.stateOutputs["state"];
|
||||||
setStateOptionsTxn(txn, statePath, queryCallingUsername(), "nixbld", 700, drvso.runtimeStateArgs);
|
setStateOptionsTxn(txn, statePath, queryCallingUsername(), "nixbld", 700, drvso.runtimeStateArgs);
|
||||||
|
|
||||||
|
//Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO)
|
||||||
|
RevisionClosure rivisionMapping;
|
||||||
|
rivisionMapping[statePath] = commitStatePathTxn(txn, statePath);
|
||||||
|
|
||||||
|
//Save the new revision
|
||||||
|
setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision.");
|
||||||
|
|
||||||
//Shared state
|
//Shared state
|
||||||
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
|
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
|
||||||
|
|
@ -2332,7 +2503,7 @@ void SubstitutionGoal::tryToRun()
|
||||||
throw SysError(format("executing `%1%'") % sub);
|
throw SysError(format("executing `%1%'") % sub);
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
std::cerr << format("substitute error: %1%\n") % e.what();
|
std::cerr << format("substitute error: %1%") % e.what() << std::endl;
|
||||||
}
|
}
|
||||||
quickExit(1);
|
quickExit(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -185,13 +185,15 @@ Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath)
|
||||||
CommitIntervals intervals = getStatePathsIntervalTxn(txn, statePath);
|
CommitIntervals intervals = getStatePathsIntervalTxn(txn, statePath);
|
||||||
|
|
||||||
Snapshots revisions_list;
|
Snapshots revisions_list;
|
||||||
|
|
||||||
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i){
|
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i){
|
||||||
|
|
||||||
string thisdir = (*i).path;
|
string thisdir = (*i).path;
|
||||||
string type = (*i).type;
|
string type = (*i).type;
|
||||||
unsigned int interval = (*i).interval;
|
unsigned int interval = (*i).interval;
|
||||||
|
|
||||||
|
//printMsg(lvlError, format("maybe ssing %1% %2%") % thisdir % type);
|
||||||
|
|
||||||
if(type == "none"){
|
if(type == "none"){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -319,19 +319,19 @@ void makePathReadOnly(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Path tempName(const Path & tmpRoot)
|
static Path tempName(const Path & tmpRoot, const Path & prefix)
|
||||||
{
|
{
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
|
Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
|
||||||
return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str();
|
return (format("%1%/%2%-%3%-%4%") % tmpRoot2 % prefix % getpid() % counter++).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path createTempDir(const Path & tmpRoot)
|
Path createTempDir(const Path & tmpRoot, const Path & prefix)
|
||||||
{
|
{
|
||||||
while (1) {
|
while (1) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
Path tmpDir = tempName(tmpRoot);
|
Path tmpDir = tempName(tmpRoot, prefix);
|
||||||
if (mkdir(tmpDir.c_str(), 0777) == 0) {
|
if (mkdir(tmpDir.c_str(), 0777) == 0) {
|
||||||
/* Explicitly set the group of the directory. This is to
|
/* Explicitly set the group of the directory. This is to
|
||||||
work around around problems caused by BSD's group
|
work around around problems caused by BSD's group
|
||||||
|
|
@ -350,13 +350,16 @@ Path createTempDir(const Path & tmpRoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createDirs(const Path & path)
|
Paths createDirs(const Path & path)
|
||||||
{
|
{
|
||||||
if (path == "/") return;
|
if (path == "/") return Paths();
|
||||||
createDirs(dirOf(path));
|
Paths created = createDirs(dirOf(path));
|
||||||
if (!pathExists(path))
|
if (!pathExists(path)) {
|
||||||
if (mkdir(path.c_str(), 0777) == -1)
|
if (mkdir(path.c_str(), 0777) == -1)
|
||||||
throw SysError(format("creating directory `%1%'") % path);
|
throw SysError(format("creating directory `%1%'") % path);
|
||||||
|
created.push_back(path);
|
||||||
|
}
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -511,14 +514,25 @@ string drainFD(int fd)
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
AutoDelete::AutoDelete(const string & p) : path(p)
|
AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
|
||||||
{
|
{
|
||||||
del = true;
|
del = true;
|
||||||
|
this->recursive = recursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoDelete::~AutoDelete()
|
AutoDelete::~AutoDelete()
|
||||||
{
|
{
|
||||||
if (del) deletePath(path);
|
try {
|
||||||
|
if (del)
|
||||||
|
if (recursive)
|
||||||
|
deletePath(path);
|
||||||
|
else {
|
||||||
|
if (remove(path.c_str()) == -1)
|
||||||
|
throw SysError(format("cannot unlink `%1%'") % path);
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoDelete::cancel()
|
void AutoDelete::cancel()
|
||||||
|
|
@ -754,10 +768,10 @@ void killUser(uid_t uid)
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
std::cerr << format("killing processes beloging to uid `%1%': %1%\n")
|
std::cerr << format("killing processes beloging to uid `%1%': %1%")
|
||||||
% uid % e.what();
|
% uid % e.what() << std::endl;
|
||||||
quickExit(1);
|
quickExit(1);
|
||||||
}
|
}
|
||||||
quickExit(0);
|
quickExit(0);
|
||||||
|
|
@ -812,7 +826,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
|
||||||
throw SysError(format("executing `%1%'") % program);
|
throw SysError(format("executing `%1%'") % program);
|
||||||
|
|
||||||
} catch (std::exception & e) {
|
} catch (std::exception & e) {
|
||||||
std::cerr << "error: " << e.what() << std::endl;
|
std::cerr << "error: " << e.what() << std::endl; //TODO does not give the full error message
|
||||||
}
|
}
|
||||||
quickExit(1);
|
quickExit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -1201,21 +1215,6 @@ bool IsSymlink(const string FileName)
|
||||||
return (S_ISLNK(my_stat.st_mode) != 0);
|
return (S_ISLNK(my_stat.st_mode) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
string getCallingUserName()
|
|
||||||
{
|
|
||||||
//Linux
|
|
||||||
Strings empty;
|
|
||||||
string username = runProgram("whoami", true, empty); //the username of the user that is trying to build the component
|
|
||||||
//TODO Can be faked, so this is clearly unsafe ... :(
|
|
||||||
//Remove the trailing \n
|
|
||||||
int pos = username.find("\n",0);
|
|
||||||
username.erase(pos,1);
|
|
||||||
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* adds the second PathSet after the first, but removing doubles from the second (union)
|
/* adds the second PathSet after the first, but removing doubles from the second (union)
|
||||||
* (We assume the first PathSet has no duplicates)
|
* (We assume the first PathSet has no duplicates)
|
||||||
* UNTESTED !!!!!!!!!!!!!!
|
* UNTESTED !!!!!!!!!!!!!!
|
||||||
|
|
@ -1321,8 +1320,15 @@ void symlinkPath(const Path & existingDir, const Path & newLinkName) //TODO bool
|
||||||
* We do -snf for:
|
* We do -snf for:
|
||||||
* -s : symlinking
|
* -s : symlinking
|
||||||
* -f : To remove existing destination files (this does NOT always overwrite the newLinkName !!!!)
|
* -f : To remove existing destination files (this does NOT always overwrite the newLinkName !!!!)
|
||||||
* -n : Treat destination that is a symlink to a directory as if it were a normal file (This makes sure
|
* -n : Treat destination that is a symlink to a directory as if it were a normal file:
|
||||||
* that newLinkName is really overwritten)
|
* When the destination is an actual directory (not a symlink to one), there is no ambiguity.
|
||||||
|
* The link is created in that directory. But when the specified destination is a symlink to a directory,
|
||||||
|
* there are two ways to treat the user's request. ln can treat the destination just as it would a normal
|
||||||
|
* directory and create the link in it. On the other hand, the destination can be viewed as a non-directory
|
||||||
|
* - as the symlink itself. In that case, ln must delete or backup that symlink before creating the new link.
|
||||||
|
* The default is to treat a destination that is a symlink to a directory just like a directory.
|
||||||
|
*
|
||||||
|
* ((This makes sure that newLinkName is really overwritten)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,10 +79,11 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
|
||||||
void makePathReadOnly(const Path & path);
|
void makePathReadOnly(const Path & path);
|
||||||
|
|
||||||
/* Create a temporary directory. */
|
/* Create a temporary directory. */
|
||||||
Path createTempDir(const Path & tmpRoot = "");
|
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix");
|
||||||
|
|
||||||
/* Create a directory and all its parents, if necessary. */
|
/* Create a directory and all its parents, if necessary. Returns the
|
||||||
void createDirs(const Path & path);
|
list of created directories, in order of creation. */
|
||||||
|
Paths createDirs(const Path & path);
|
||||||
|
|
||||||
/* Create a file and write the given text to it. The file is written
|
/* Create a file and write the given text to it. The file is written
|
||||||
in binary mode (i.e., no end-of-line conversions). The path should
|
in binary mode (i.e., no end-of-line conversions). The path should
|
||||||
|
|
@ -175,8 +176,9 @@ class AutoDelete
|
||||||
{
|
{
|
||||||
Path path;
|
Path path;
|
||||||
bool del;
|
bool del;
|
||||||
|
bool recursive;
|
||||||
public:
|
public:
|
||||||
AutoDelete(const Path & p);
|
AutoDelete(const Path & p, bool recursive = true);
|
||||||
~AutoDelete();
|
~AutoDelete();
|
||||||
void cancel();
|
void cancel();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,6 @@ Query flags:
|
||||||
--out-path: print path of derivation output
|
--out-path: print path of derivation output
|
||||||
--description: print description
|
--description: print description
|
||||||
--meta: print all meta attributes (only with --xml)
|
--meta: print all meta attributes (only with --xml)
|
||||||
--prebuilt-only: only show derivations whose prebuilt binaries are
|
|
||||||
available on this machine or are downloadable
|
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
|
@ -74,3 +72,5 @@ Options:
|
||||||
--keep-failed / -K: keep temporary directories of failed builds
|
--keep-failed / -K: keep temporary directories of failed builds
|
||||||
--preserve-installed: do not replace currently installed versions in `-i'
|
--preserve-installed: do not replace currently installed versions in `-i'
|
||||||
--system-filter SYSTEM: only use derivations for specified platform
|
--system-filter SYSTEM: only use derivations for specified platform
|
||||||
|
--prebuilt-only / -b: only use derivations whose prebuilt binaries are
|
||||||
|
available on this machine or are downloadable
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,9 @@ struct InstallSourceInfo
|
||||||
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
|
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
|
||||||
Path profile; /* for srcProfile */
|
Path profile; /* for srcProfile */
|
||||||
string systemFilter; /* for srcNixExprDrvs */
|
string systemFilter; /* for srcNixExprDrvs */
|
||||||
|
bool prebuiltOnly;
|
||||||
ATermMap autoArgs;
|
ATermMap autoArgs;
|
||||||
InstallSourceInfo() : autoArgs() { };
|
InstallSourceInfo() : prebuiltOnly(false) { };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -96,6 +97,8 @@ static bool parseInstallSourceOptions(Globals & globals,
|
||||||
}
|
}
|
||||||
else if (arg == "--attr" || arg == "-A")
|
else if (arg == "--attr" || arg == "-A")
|
||||||
globals.instSource.type = srcAttrPath;
|
globals.instSource.type = srcAttrPath;
|
||||||
|
else if (arg == "--prebuilt-only" || arg == "-b")
|
||||||
|
globals.instSource.prebuiltOnly = true;
|
||||||
else return false;
|
else return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -339,9 +342,16 @@ static int comparePriorities(EvalState & state,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static DrvInfos filterBySelector(EvalState & state,
|
static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
|
||||||
const DrvInfos & allElems,
|
{
|
||||||
const Strings & args, bool newestOnly)
|
return
|
||||||
|
store->isValidPath(elem.queryOutPath(state)) ||
|
||||||
|
store->hasSubstitutes(elem.queryOutPath(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
||||||
|
const Strings & args, bool newestOnly, bool prebuiltOnly)
|
||||||
{
|
{
|
||||||
DrvNames selectors = drvNamesFromArgs(args);
|
DrvNames selectors = drvNamesFromArgs(args);
|
||||||
|
|
||||||
|
|
@ -360,7 +370,8 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||||
DrvName drvName(j->name);
|
DrvName drvName(j->name);
|
||||||
if (i->matches(drvName)) {
|
if (i->matches(drvName)) {
|
||||||
i->hits++;
|
i->hits++;
|
||||||
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
|
if (!prebuiltOnly || isPrebuilt(state, *j))
|
||||||
|
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,7 +461,8 @@ static void queryInstSources(EvalState & state,
|
||||||
loadDerivations(state, instSource.nixExprPath,
|
loadDerivations(state, instSource.nixExprPath,
|
||||||
instSource.systemFilter, instSource.autoArgs, "", allElems);
|
instSource.systemFilter, instSource.autoArgs, "", allElems);
|
||||||
|
|
||||||
elems = filterBySelector(state, allElems, args, newestOnly);
|
elems = filterBySelector(state, allElems, args,
|
||||||
|
newestOnly, instSource.prebuiltOnly);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -518,7 +530,7 @@ static void queryInstSources(EvalState & state,
|
||||||
case srcProfile: {
|
case srcProfile: {
|
||||||
elems = filterBySelector(state,
|
elems = filterBySelector(state,
|
||||||
queryInstalled(state, instSource.profile),
|
queryInstalled(state, instSource.profile),
|
||||||
args, newestOnly);
|
args, newestOnly, instSource.prebuiltOnly);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1152,7 +1164,7 @@ static void opQuery(Globals & globals,
|
||||||
|
|
||||||
DrvInfos elems = filterBySelector(globals.state,
|
DrvInfos elems = filterBySelector(globals.state,
|
||||||
source == sInstalled ? installedElems : availElems,
|
source == sInstalled ? installedElems : availElems,
|
||||||
remaining, false);
|
remaining, false, prebuiltOnly);
|
||||||
|
|
||||||
DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
|
DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
|
||||||
|
|
||||||
|
|
@ -1193,12 +1205,6 @@ static void opQuery(Globals & globals,
|
||||||
/* For XML output. */
|
/* For XML output. */
|
||||||
XMLAttrs attrs;
|
XMLAttrs attrs;
|
||||||
|
|
||||||
if (prebuiltOnly) {
|
|
||||||
if (!store->isValidPath(i->queryOutPath(globals.state)) &&
|
|
||||||
!store->hasSubstitutes(i->queryOutPath(globals.state)))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (printStatus) {
|
if (printStatus) {
|
||||||
bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
|
bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
|
||||||
bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();
|
bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,6 @@ static void revertToRevision(Strings opFlags, Strings opArgs)
|
||||||
static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs)
|
static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs)
|
||||||
{
|
{
|
||||||
Path statePath;
|
Path statePath;
|
||||||
|
|
||||||
if(store->isValidStatePath(*(opArgs.begin())))
|
if(store->isValidStatePath(*(opArgs.begin())))
|
||||||
statePath = *(opArgs.begin());
|
statePath = *(opArgs.begin());
|
||||||
else{
|
else{
|
||||||
|
|
@ -292,7 +291,18 @@ static void opShowSharedPaths(Strings opFlags, Strings opArgs)
|
||||||
|
|
||||||
static void opUnshare(Strings opFlags, Strings opArgs)
|
static void opUnshare(Strings opFlags, Strings opArgs)
|
||||||
{
|
{
|
||||||
Path statePath = *(opArgs.begin());
|
Path statePath;
|
||||||
|
if(store->isValidStatePath(*(opArgs.begin())))
|
||||||
|
statePath = *(opArgs.begin());
|
||||||
|
else{
|
||||||
|
Path componentPath;
|
||||||
|
string binary;
|
||||||
|
string derivationPath;
|
||||||
|
bool isStateComponent;
|
||||||
|
Strings program_args;
|
||||||
|
getPathInfo_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args);
|
||||||
|
}
|
||||||
|
|
||||||
if(!store->isValidStatePath(statePath))
|
if(!store->isValidStatePath(statePath))
|
||||||
throw UsageError(format("Path '%1%' is not a valid state path.") % statePath);
|
throw UsageError(format("Path '%1%' is not a valid state path.") % statePath);
|
||||||
|
|
||||||
|
|
@ -784,10 +794,10 @@ void run(Strings args)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//Manipulate options....
|
//Manipulate options TODO only allow for root user ...
|
||||||
else if (arg.substr(0,13) == "--identifier=")
|
else if (arg.substr(0,13) == "--identifier=")
|
||||||
stateIdentifier = arg.substr(13,arg.length());
|
stateIdentifier = arg.substr(13,arg.length());
|
||||||
else if (arg.substr(0,7) == "--user=")
|
else if (arg.substr(0,7) == "--nix-user=")
|
||||||
username = arg.substr(7,arg.length());
|
username = arg.substr(7,arg.length());
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -240,8 +240,8 @@ static void performOp(Source & from, Sink & to, unsigned int op)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
case wopIsValidPath: {
|
case wopIsValidPath: { //we do a readString at isValidXXXX
|
||||||
Path path = readStorePath(from);
|
Path path = readString(from);
|
||||||
startWork();
|
startWork();
|
||||||
bool result = store->isValidPath(path);
|
bool result = store->isValidPath(path);
|
||||||
stopWork();
|
stopWork();
|
||||||
|
|
@ -250,7 +250,7 @@ static void performOp(Source & from, Sink & to, unsigned int op)
|
||||||
}
|
}
|
||||||
|
|
||||||
case wopIsValidStatePath: {
|
case wopIsValidStatePath: {
|
||||||
Path path = readStatePath(from);
|
Path path = readString(from);
|
||||||
startWork();
|
startWork();
|
||||||
bool result = store->isValidStatePath(path);
|
bool result = store->isValidStatePath(path);
|
||||||
stopWork();
|
stopWork();
|
||||||
|
|
@ -259,7 +259,7 @@ static void performOp(Source & from, Sink & to, unsigned int op)
|
||||||
}
|
}
|
||||||
|
|
||||||
case wopIsValidComponentOrStatePath: {
|
case wopIsValidComponentOrStatePath: {
|
||||||
Path path = readStoreOrStatePath(from);
|
Path path = readString(from);
|
||||||
startWork();
|
startWork();
|
||||||
bool result = store->isValidComponentOrStatePath(path);
|
bool result = store->isValidComponentOrStatePath(path);
|
||||||
stopWork();
|
stopWork();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue