Compare commits
81 Commits
sharing-ha
...
no-bdb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
262b73e6ad | ||
|
|
54ff6c02ec | ||
|
|
f83f764218 | ||
|
|
a599708b7a | ||
|
|
fd350a1555 | ||
|
|
bcc3862331 | ||
|
|
341b2de643 | ||
|
|
4df6dc28c3 | ||
|
|
a7154c5b14 | ||
|
|
10c6e2a629 | ||
|
|
af7fd0b21f | ||
|
|
0be3520d95 | ||
|
|
8472d7eb31 | ||
|
|
95fa444587 | ||
|
|
07da660396 | ||
|
|
22c225a899 | ||
|
|
91313011f9 | ||
|
|
f5e6c9bcfd | ||
|
|
f789ea1d09 | ||
|
|
d7caac3e81 | ||
|
|
11d512e7a8 | ||
|
|
0a84137c45 | ||
|
|
0ed89c569f | ||
|
|
4066f450c2 | ||
|
|
e7bdde981f | ||
|
|
66c51dc215 | ||
|
|
5b5a3af983 | ||
|
|
5eb5c23447 | ||
|
|
7d0f6aed59 | ||
|
|
895c953817 | ||
|
|
d4117859d6 | ||
|
|
e0ca671491 | ||
|
|
6bdecfacbb | ||
|
|
454f571f87 | ||
|
|
4c82983010 | ||
|
|
f60aa2aa21 | ||
|
|
dedd62dd0c | ||
|
|
b5ddcf58ad | ||
|
|
59707975a3 | ||
|
|
de012e76e2 | ||
|
|
2196cb67c5 | ||
|
|
df303666bc | ||
|
|
1e90b4189d | ||
|
|
485d71c54a | ||
|
|
30e4653783 | ||
|
|
4ff1335b2e | ||
|
|
3a2833daff | ||
|
|
764b0115d5 | ||
|
|
b42ef9c054 | ||
|
|
d4950f207f | ||
|
|
6d6c68c0d2 | ||
|
|
633518628f | ||
|
|
12d0a1eb75 | ||
|
|
06f95dd07c | ||
|
|
c370755583 | ||
|
|
c05783ad67 | ||
|
|
6784b14241 | ||
|
|
ca210d2a58 | ||
|
|
149e273023 | ||
|
|
bfec245efa | ||
|
|
6fc60e2060 | ||
|
|
5b74a59570 | ||
|
|
5f8a203b55 | ||
|
|
1511aa11ce | ||
|
|
fa44e401a8 | ||
|
|
e007b50eb7 | ||
|
|
bfea7b1f35 | ||
|
|
0b95603595 | ||
|
|
42d80d1170 | ||
|
|
d91cd30563 | ||
|
|
dc6f373842 | ||
|
|
9397cd30c8 | ||
|
|
0b4ed64d29 | ||
|
|
cd08fb3fde | ||
|
|
d423968ba1 | ||
|
|
612b3e8fa3 | ||
|
|
35ac16e75e | ||
|
|
1db187ff69 | ||
|
|
f90d85107e | ||
|
|
f8ed2e64a5 | ||
|
|
f529a3be8a |
42
configure.ac
42
configure.ac
@@ -1,4 +1,4 @@
|
||||
AC_INIT(nix, 0.11)
|
||||
AC_INIT(nix, 0.12)
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
|
||||
@@ -16,7 +16,7 @@ if test "$STABLE" != "1"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [version])
|
||||
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [Nix version.])
|
||||
|
||||
AC_PREFIX_DEFAULT(/nix)
|
||||
|
||||
@@ -54,7 +54,7 @@ case $sys_name in
|
||||
esac
|
||||
|
||||
AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
|
||||
[platform identifier (e.g., `i686-linux')]),
|
||||
[Platform identifier (e.g., `i686-linux').]),
|
||||
system=$withval, system="${machine_name}-${sys_name}")
|
||||
AC_MSG_RESULT($system)
|
||||
AC_SUBST(system)
|
||||
@@ -94,14 +94,24 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <iostream>
|
||||
using namespace std;
|
||||
static char buf[1024];]],
|
||||
[[cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));]])],
|
||||
[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [whether pubsetbuf is available])],
|
||||
[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [Whether pubsetbuf is available.])],
|
||||
AC_MSG_RESULT(no))
|
||||
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>
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([locale])
|
||||
AC_CHECK_HEADERS([locale], [], [], [])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
@@ -114,7 +124,7 @@ fi
|
||||
])
|
||||
|
||||
NEED_PROG(curl, curl)
|
||||
NEED_PROG(shell, sh)
|
||||
NEED_PROG(shell, bash)
|
||||
NEED_PROG(patch, patch)
|
||||
AC_PATH_PROG(xmllint, xmllint, false)
|
||||
AC_PATH_PROG(xsltproc, xsltproc, false)
|
||||
@@ -125,6 +135,7 @@ AC_PATH_PROG(bison, bison, false)
|
||||
NEED_PROG(perl, perl)
|
||||
NEED_PROG(tar, tar)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(dblatex, dblatex)
|
||||
|
||||
AC_PATH_PROG(openssl_prog, openssl, openssl) # if not found, call openssl in $PATH
|
||||
AC_SUBST(openssl_prog)
|
||||
@@ -165,8 +176,13 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
|
||||
storedir=$withval, storedir='${prefix}/store')
|
||||
AC_SUBST(storedir)
|
||||
|
||||
AC_ARG_ENABLE(old-db-compat, AC_HELP_STRING([--disable-old-db-compat],
|
||||
[disable support for converting from old Berkeley DB-based Nix stores]),
|
||||
old_db_compat=$enableval, old_db_compat=yes)
|
||||
AM_CONDITIONAL(OLD_DB_COMPAT, test "$old_db_compat" = "yes")
|
||||
|
||||
AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH],
|
||||
[prefix of Berkeley DB]),
|
||||
[prefix of Berkeley DB (for Nix <= 0.11 compatibility)]),
|
||||
bdb=$withval, bdb=)
|
||||
AM_CONDITIONAL(HAVE_BDB, test -n "$bdb")
|
||||
if test -z "$bdb"; then
|
||||
@@ -176,6 +192,12 @@ else
|
||||
bdb_lib="-L$bdb/lib -ldb_cxx"
|
||||
bdb_include="-I$bdb/include"
|
||||
fi
|
||||
if test "$old_db_compat" = "no"; then
|
||||
bdb_lib=
|
||||
bdb_include=
|
||||
else
|
||||
AC_DEFINE(OLD_DB_COMPAT, 1, [Whether to support converting from old Berkeley DB-based Nix stores.])
|
||||
fi
|
||||
AC_SUBST(bdb_lib)
|
||||
AC_SUBST(bdb_include)
|
||||
|
||||
@@ -204,7 +226,7 @@ if test -n "$openssl"; then
|
||||
LDFLAGS="-L$openssl/lib -lcrypto $LDFLAGS"
|
||||
CFLAGS="-I$openssl/include $CFLAGS"
|
||||
CXXFLAGS="-I$openssl/include $CXXFLAGS"
|
||||
AC_DEFINE(HAVE_OPENSSL, 1, [whether to use OpenSSL])
|
||||
AC_DEFINE(HAVE_OPENSSL, 1, [Whether to use OpenSSL.])
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(bzip2, AC_HELP_STRING([--with-bzip2=PATH],
|
||||
@@ -245,6 +267,10 @@ AM_CONDITIONAL(INIT_STATE, test "$init_state" = "yes")
|
||||
AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||
|
||||
|
||||
# Nice to have, but not essential.
|
||||
AC_CHECK_FUNCS([strsignal])
|
||||
|
||||
|
||||
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
|
||||
# and the Nix libraries are dynamic.
|
||||
if test "$(uname)" = "Darwin"; then
|
||||
|
||||
@@ -20,7 +20,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
|
||||
FIGURES = figures/user-environments.png
|
||||
|
||||
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 \
|
||||
$(man1_MANS:.1=.xml) \
|
||||
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
|
||||
@@ -47,6 +47,14 @@ manual.html: $(MANUAL_SRCS) manual.is-valid images
|
||||
$(XSLTPROC) --nonet --xinclude --output manual.html \
|
||||
$(docbookxsl)/html/docbook.xsl manual.xml
|
||||
|
||||
manual.pdf: $(MANUAL_SRCS) manual.is-valid images
|
||||
if test "$(dblatex)" != ""; then \
|
||||
$(dblatex) manual.xml; \
|
||||
else \
|
||||
echo "Please install dblatex and rerun configure."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
NEWS_OPTS = \
|
||||
--stringparam generate.toc "article nop" \
|
||||
|
||||
@@ -36,10 +36,10 @@ build farm, since:
|
||||
builds, and Nix expressions are self-contained.</para></listitem>
|
||||
|
||||
<listitem><para>Nix will only rebuild things that have actually
|
||||
changed. For instance, if the sources of a component haven't
|
||||
changed between runs of the build farm, the component won't be
|
||||
rebuild (unless it was garbage-collected). Also, dependencies
|
||||
typically don't change very often, so they only need to be built
|
||||
changed. For instance, if the sources of a package haven't changed
|
||||
between runs of the build farm, the package won't be rebuilt (unless
|
||||
it was garbage-collected). Also, dependencies typically don't
|
||||
change very often, so they only need to be built
|
||||
once.</para></listitem>
|
||||
|
||||
<listitem><para>The results of a Nix build farm can be made
|
||||
|
||||
760
doc/manual/builtins.xml
Normal file
760
doc/manual/builtins.xml
Normal file
@@ -0,0 +1,760 @@
|
||||
<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>builtins.readFile</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Return the contents of the file
|
||||
<replaceable>path</replaceable> as a string.</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.substring</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 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>
|
||||
|
||||
<listitem><para>This option specifies the canonical Nix system
|
||||
|
||||
@@ -263,6 +263,17 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="envar-remote"><term><envar>NIX_REMOTE</envar></term>
|
||||
|
||||
<listitem><para>This variable should be set to
|
||||
<literal>daemon</literal> if you want to use the Nix daemon to
|
||||
executed Nix operations, which is necessary in <link
|
||||
linkend="ssec-multi-user">multi-user Nix installations</link>.
|
||||
Otherwise, it should be left unset.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
|
||||
<glossentry><glossterm>Nix expression</glossterm>
|
||||
|
||||
<glossdef><para>A high-level description of software components and
|
||||
<glossdef><para>A high-level description of software packages and
|
||||
compositions thereof. Deploying software using Nix entails writing
|
||||
Nix expressions for your components. Nix expressions are translated
|
||||
Nix expressions for your packages. Nix expressions are translated
|
||||
to derivations that are stored in the Nix store. These derivations
|
||||
can then be built.</para></glossdef>
|
||||
|
||||
|
||||
@@ -42,9 +42,8 @@ platforms as well.</para>
|
||||
<section><title>Obtaining Nix</title>
|
||||
|
||||
<para>The easiest way to obtain Nix is to download a <link
|
||||
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">source
|
||||
distribution</link>. RPMs for Red Hat, SuSE, and Fedora Core are also
|
||||
available.</para>
|
||||
xlink:href="http://nix.cs.uu.nl/">source distribution</link>. RPMs
|
||||
for Red Hat, SuSE, and Fedora Core are also available.</para>
|
||||
|
||||
<para>Alternatively, the most recent sources of Nix can be obtained
|
||||
from its <link
|
||||
@@ -100,14 +99,16 @@ ubiquitous 2.5.4a won't. Note that these are only required if you
|
||||
modify the parser or when you are building from the Subversion
|
||||
repository.</para>
|
||||
|
||||
<para>Nix uses Sleepycat's Berkeley DB and CWI's ATerm library. These
|
||||
are included in the Nix source distribution. If you build from the
|
||||
Subversion repository, you must download them yourself and place them
|
||||
in the <filename>externals/</filename> directory. See
|
||||
<para>Nix uses Sleepycat's Berkeley DB, CWI's ATerm library and the
|
||||
bzip2 compressor (including the bzip2 library). These are included in
|
||||
the Nix source distribution. If you build from the Subversion
|
||||
repository, you must download them yourself and place them in the
|
||||
<filename>externals/</filename> directory. See
|
||||
<filename>externals/Makefile.am</filename> for the precise URLs of
|
||||
these packages. Alternatively, if you already have them installed,
|
||||
you can use <command>configure</command>'s <option>--with-bdb</option>
|
||||
and <option>--with-aterm</option> options to point to their respective
|
||||
you can use <command>configure</command>'s
|
||||
<option>--with-bdb</option>, <option>--with-aterm</option> and
|
||||
<option>--with-bzip2</option> options to point to their respective
|
||||
locations. Note that Berkeley DB <emphasis>must</emphasis> be version
|
||||
4.5; other versions may not have compatible database formats.</para>
|
||||
|
||||
@@ -118,19 +119,21 @@ locations. Note that Berkeley DB <emphasis>must</emphasis> be version
|
||||
|
||||
<para>After unpacking or checking out the Nix sources, issue the
|
||||
following commands:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ ./configure <replaceable>options...</replaceable>
|
||||
$ make
|
||||
$ make install</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>When building from the Subversion repository, these should be
|
||||
preceded by the command:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ ./boostrap</screen>
|
||||
$ ./bootstrap</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The installation path can be specified by passing the
|
||||
<option>--prefix=<replaceable>prefix</replaceable></option> to
|
||||
@@ -157,28 +160,32 @@ options.</para>
|
||||
|
||||
<section><title>Installing from RPMs</title>
|
||||
|
||||
<para>RPM packages of Nix can be downloaded from <uri
|
||||
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">http://www.cs.uu.nl/groups/ST/Trace/Nix</uri>.
|
||||
These RPMs should work for most fairly recent releases of SuSE and Red
|
||||
Hat Linux. They have been known to work work on SuSE Linux 8.1 and
|
||||
9.0, and Red Hat 9.0. In fact, it should work on any RPM-based Linux
|
||||
distribution based on <literal>glibc</literal> 2.3 or later.</para>
|
||||
<para>RPM packages of Nix can be downloaded from <link
|
||||
xlink:href="http://nix.cs.uu.nl/" />. These RPMs should work for most
|
||||
fairly recent releases of SuSE and Red Hat Linux. They have been
|
||||
known to work work on SuSE Linux 8.1 and 9.0, and Red Hat 9.0. In
|
||||
fact, it should work on any RPM-based Linux distribution based on
|
||||
<literal>glibc</literal> 2.3 or later.</para>
|
||||
|
||||
<para>Once downloaded, the RPMs can be installed or upgraded using
|
||||
<command>rpm -U</command>. For example,</para>
|
||||
<command>rpm -U</command>. For example,
|
||||
|
||||
<screen>
|
||||
$ rpm -U nix-0.5pre664-1.i386.rpm</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The RPMs install into the directory <filename>/nix</filename>.
|
||||
Nix can be uninstalled using <command>rpm -e nix</command>. After
|
||||
this it will be necessary to manually remove the Nix store and other
|
||||
auxiliary data:</para>
|
||||
auxiliary data:
|
||||
|
||||
<screen>
|
||||
$ rm -rf /nix/store
|
||||
$ rm -rf /nix/var</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -187,7 +194,7 @@ $ rm -rf /nix/var</screen>
|
||||
<para>You can install the latest stable version of Nix through Nix
|
||||
itself by subscribing to the channel <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-stable" />,
|
||||
or the latest unstable version by subscribing to the channel<link
|
||||
or the latest unstable version by subscribing to the channel <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-unstable" />.
|
||||
You can also do a <link linkend="sec-one-click">one-click
|
||||
installation</link> by clicking on the package links at <link
|
||||
@@ -231,33 +238,215 @@ class="username">root</systemitem> all the time.</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Multi-user mode</title>
|
||||
<section xml:id="ssec-multi-user"><title>Multi-user mode</title>
|
||||
|
||||
<para></para>
|
||||
<para>To allow a Nix store to be shared safely among multiple users,
|
||||
it is important that users are not able to run builders that modify
|
||||
the Nix store or database in arbitrary ways, or that interfere with
|
||||
builds started by other users. If they could do so, they could
|
||||
install a Trojan horse in some package and compromise the accounts of
|
||||
other users.</para>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
warning: the nix-builders group should contain *only* the Nix
|
||||
builders, and nothing else. If the Nix account is compromised, you
|
||||
can execute programs under the accounts in the nix-builders group, so
|
||||
it obviously shouldn’t contain any “real” user accounts. So don’t use
|
||||
an existing group like <literal>users</literal> — just create a new
|
||||
one.
|
||||
|
||||
-->
|
||||
<para>To prevent this, the Nix store and database are owned by some
|
||||
privileged user (usually <literal>root</literal>) and builders are
|
||||
executed under special user accounts (usually named
|
||||
<literal>nixbld1</literal>, <literal>nixbld2</literal>, etc.). When a
|
||||
unprivileged user runs a Nix command, actions that operate on the Nix
|
||||
store (such as builds) are forwarded to a <emphasis>Nix
|
||||
daemon</emphasis> running under the owner of the Nix store/database
|
||||
that performs the operation.</para>
|
||||
|
||||
<note><para>Multi-user mode has one important limitation: only
|
||||
<systemitem class="username">root</systemitem> can run <command
|
||||
linkend="sec-nix-pull">nix-pull</command> to register the availability
|
||||
of pre-built binaries. However, those registrations
|
||||
<emphasis>are</emphasis> used by all users to speed up
|
||||
builds.</para></note>
|
||||
of pre-built binaries. However, those registrations are shared by all
|
||||
users, so they still get the benefit from <command>nix-pull</command>s
|
||||
done by <systemitem class="username">root</systemitem>.</para></note>
|
||||
|
||||
|
||||
<section><title>Setting up the build users</title>
|
||||
|
||||
<para>The <emphasis>build users</emphasis> are the special UIDs under
|
||||
which builds are performed. They should all be members of the
|
||||
<emphasis>build users group</emphasis> (usually called
|
||||
<literal>nixbld</literal>). This group should have no other members.
|
||||
The build users should not be members of any other group.</para>
|
||||
|
||||
<para>Here is a typical <filename>/etc/group</filename> definition of
|
||||
the build users group with 10 build users:
|
||||
|
||||
<programlisting>
|
||||
nixbld:!:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10
|
||||
</programlisting>
|
||||
|
||||
In this example the <literal>nixbld</literal> group has UID 30000, but
|
||||
of course it can be anything that doesn’t collide with an existing
|
||||
group.</para>
|
||||
|
||||
<para>Here is the corresponding part of
|
||||
<filename>/etc/passwd</filename>:
|
||||
|
||||
<programlisting>
|
||||
nixbld1:x:30001:65534:Nix build user 1:/var/empty:/noshell
|
||||
nixbld2:x:30002:65534:Nix build user 2:/var/empty:/noshell
|
||||
nixbld3:x:30003:65534:Nix build user 3:/var/empty:/noshell
|
||||
...
|
||||
nixbld10:x:30010:65534:Nix build user 10:/var/empty:/noshell
|
||||
</programlisting>
|
||||
|
||||
The home directory of the build users should not exist or should be an
|
||||
empty directory to which they do not have write access.</para>
|
||||
|
||||
<para>The build users should have write access to the Nix store, but
|
||||
they should not have the right to delete files. Thus the Nix store’s
|
||||
group should be the build users group, and it should have the sticky
|
||||
bit turned on (like <filename>/tmp</filename>):
|
||||
|
||||
<screen>
|
||||
$ chgrp nixbld /nix/store
|
||||
$ chmod 1777 /nix/store
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Finally, you should tell Nix to use the build users by
|
||||
specifying the build users group in the <link
|
||||
linkend="conf-build-users-group"><literal>build-users-group</literal>
|
||||
option</link> in the <link linkend="sec-conf-file">Nix configuration
|
||||
file</link> (<literal>/nix/etc/nix/nix.conf</literal>):
|
||||
|
||||
<programlisting>
|
||||
build-users-group = nixbld
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</section> <!-- end of permissions section -->
|
||||
<section><title>Nix store/database owned by root</title>
|
||||
|
||||
<para>The simplest setup is to let <literal>root</literal> own the Nix
|
||||
store and database. I.e.,
|
||||
|
||||
<screen>
|
||||
$ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The Nix daemon should be started as follows (as
|
||||
<literal>root</literal>):
|
||||
|
||||
<screen>
|
||||
$ nix-worker --daemon</screen>
|
||||
|
||||
You’ll want to put that line somewhere in your system’s boot
|
||||
scripts.</para>
|
||||
|
||||
<para>To let unprivileged users use the daemon, they should set the
|
||||
<link linkend="envar-remote"><envar>NIX_REMOTE</envar> environment
|
||||
variable</link> to <literal>daemon</literal>. So you should put a
|
||||
line like
|
||||
|
||||
<programlisting>
|
||||
export NIX_REMOTE=daemon</programlisting>
|
||||
|
||||
into the users’ login scripts.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Nix store/database not owned by root</title>
|
||||
|
||||
<para>It is also possible to let the Nix store and database be owned
|
||||
by a non-root user, which should be more secure<footnote><para>Note
|
||||
however that even when the Nix daemon runs as root, not
|
||||
<emphasis>that</emphasis> much code is executed as root: Nix
|
||||
expression evaluation is performed by the calling (unprivileged) user,
|
||||
and builds are performed under the special build user accounts. So
|
||||
only the code that accesses the database and starts builds is executed
|
||||
as <literal>root</literal>.</para></footnote>. Typically, this user
|
||||
is a special account called <literal>nix</literal>, but it can be
|
||||
named anything. It should own the Nix store and database:
|
||||
|
||||
<screen>
|
||||
$ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
and of course <command>nix-worker --daemon</command> should be started
|
||||
under that user, e.g.,
|
||||
|
||||
<screen>
|
||||
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>There is a catch, though: non-<literal>root</literal> users
|
||||
cannot start builds under the build user accounts, since the
|
||||
<function>setuid</function> system call is obviously privileged. To
|
||||
allow a non-<literal>root</literal> Nix daemon to use the build user
|
||||
feature, it calls a setuid-root helper program,
|
||||
<command>nix-setuid-helper</command>. This program is installed in
|
||||
<filename><replaceable>prefix</replaceable>/libexec/nix-setuid-helper</filename>.
|
||||
To set the permissions properly (Nix’s <command>make install</command>
|
||||
doesn’t do this, since we don’t want to ship setuid-root programs
|
||||
out-of-the-box):
|
||||
|
||||
<screen>
|
||||
$ chown root.root /nix/libexec/nix-setuid-helper
|
||||
$ chmod 4755 /nix/libexec/nix-setuid-helper
|
||||
</screen>
|
||||
|
||||
(This example assumes that the Nix binaries are installed in
|
||||
<filename>/nix</filename>.)</para>
|
||||
|
||||
<para>Of course, the <command>nix-setuid-helper</command> command
|
||||
should not be usable by just anybody, since then anybody could run
|
||||
commands under the Nix build user accounts. For that reason there is
|
||||
a configuration file <filename>/etc/nix-setuid.conf</filename> that
|
||||
restricts the use of the helper. This file should be a text file
|
||||
containing precisely two lines, the first being the Nix daemon user
|
||||
and the second being the build users group, e.g.,
|
||||
|
||||
<programlisting>
|
||||
nix
|
||||
nixbld
|
||||
</programlisting>
|
||||
|
||||
The setuid-helper barfs if it is called by a user other than the one
|
||||
specified on the first line, or if it is asked to execute a build
|
||||
under a user who is not a member of the group specified on the second
|
||||
line. The file <filename>/etc/nix-setuid.conf</filename> must be
|
||||
owned by root, and must not be group- or world-writable. The
|
||||
setuid-helper barfs if this is not the case.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Restricting access</title>
|
||||
|
||||
<para>To limit which users can perform Nix operations, you can use the
|
||||
permissions on the directory
|
||||
<filename>/nix/var/nix/daemon-socket</filename>. For instance, if you
|
||||
want to restrict the use of Nix to the members of a group called
|
||||
<literal>nix-users</literal>, do
|
||||
|
||||
<screen>
|
||||
$ chgrp nix-users /nix/var/nix/daemon-socket
|
||||
$ chmod ug=rwx,o= /nix/var/nix/daemon-socket
|
||||
</screen>
|
||||
|
||||
This way, users who are not in the <literal>nix-users</literal> group
|
||||
cannot connect to the Unix domain socket
|
||||
<filename>/nix/var/nix/daemon-socket/socket</filename>, so they cannot
|
||||
perform Nix operations.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</section> <!-- end of multi-user -->
|
||||
|
||||
|
||||
</section> <!-- end of security -->
|
||||
|
||||
|
||||
<section><title>Using Nix</title>
|
||||
|
||||
@@ -1,135 +1,304 @@
|
||||
<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>
|
||||
|
||||
<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
|
||||
are complete. In general in a deployment system you have to specify
|
||||
for each component 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 a <emphasis>purely functional package manager</emphasis>.
|
||||
This means that it treats packages like values in purely functional
|
||||
programming languages such as Haskell — they are built by functions
|
||||
that don’t have side-effects, and they never change after they have
|
||||
been built. Nix stores packages in the <emphasis>Nix
|
||||
store</emphasis>, usually the directory
|
||||
<filename>/nix/store</filename>, where each package has its own unique
|
||||
subdirectory such as
|
||||
|
||||
<listitem><para>It is possible to have <emphasis>multiple versions or
|
||||
variants</emphasis> of a component installed at the same time. In
|
||||
contrast, in systems such as RPM different versions of the same
|
||||
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>
|
||||
<programlisting>
|
||||
/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/
|
||||
</programlisting>
|
||||
|
||||
<listitem><para>Users can have different <quote>views</quote>
|
||||
(<quote>profiles</quote> in Nix parlance) on the set of installed
|
||||
applications in a system. For instance, one user can have version 1.0
|
||||
of some package visible, while another is using version 1.1, and a
|
||||
third doesn't use it at all.</para></listitem>
|
||||
where <literal>r8vvq9kq…</literal> is a unique identifier for the
|
||||
package that captures all its dependencies (it’s a cryptographic hash
|
||||
of the package’s build dependency graph). This enables many powerful
|
||||
features.</para>
|
||||
|
||||
<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
|
||||
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>
|
||||
<simplesect><title>Multiple versions</title>
|
||||
|
||||
<listitem><para>Unused components can be
|
||||
<emphasis>garbage-collected</emphasis> automatically and safely: when
|
||||
you remove an application from a profile, its dependencies will be
|
||||
deleted by the garbage collector only if there are no other active
|
||||
applications using them.</para></listitem>
|
||||
<para>You can have multiple versions or variants of a package
|
||||
installed at the same time. This is especially important when
|
||||
different applications have dependencies on different versions of the
|
||||
same package — it prevents the “DLL hell”. Because of the hashing
|
||||
scheme, different versions of a package end up in different paths in
|
||||
the Nix store, so they don’t interfere with each other.</para>
|
||||
|
||||
<listitem><para>Nix supports both source-based deployment models
|
||||
(where you distribute <emphasis>Nix expressions</emphasis> that tell
|
||||
Nix how to build software from source) and binary-based deployment
|
||||
models. The latter is more-or-less transparent: installation of
|
||||
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>
|
||||
<para>An important consequence is that operations like upgrading or
|
||||
uninstalling an application cannot break other applications, since
|
||||
these operations never “destructively” update or delete files that are
|
||||
used by other packages.</para>
|
||||
|
||||
<listitem><para>Nix is flexible in the deployment policies that it
|
||||
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>
|
||||
</simplesect>
|
||||
|
||||
<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
|
||||
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>
|
||||
<simplesect><title>Complete dependencies</title>
|
||||
|
||||
<listitem><para>The Nix expression language makes it easy to describe
|
||||
variability in components (e.g., optional features or
|
||||
dependencies).</para></listitem>
|
||||
<para>Nix helps you make sure that package dependency specifications
|
||||
are complete. In general, when you’re making a package for a package
|
||||
management system like RPM, you have to specify for each package what
|
||||
its dependencies are, but there are no guarantees that this
|
||||
specification is complete. If you forget a dependency, then the
|
||||
package will build and work correctly on <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
|
||||
continuous builds of software from a version management system, since
|
||||
it can take care of building all the dependencies as well. Also, Nix
|
||||
only rebuilds components that have changed, so there are no
|
||||
unnecessary builds. In addition, Nix can transparently distribute
|
||||
build jobs over different machines, including different
|
||||
platforms.</para></listitem>
|
||||
<para>Since Nix on the other hand doesn’t install packages in “global”
|
||||
locations like <filename>/usr/bin</filename> but in package-specific
|
||||
directories, the risk of incomplete dependencies is greatly reduced.
|
||||
This is because tools such as compilers don’t search in per-packages
|
||||
directories such as
|
||||
<filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>,
|
||||
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
|
||||
also for <emphasis>service deployment</emphasis>, such as the
|
||||
deployment of a complete web server with all its configuration files,
|
||||
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>
|
||||
<para>Runtime dependencies are found by scanning binaries for the hash
|
||||
parts of Nix store paths (such as <literal>r8vvq9kq…</literal>). This
|
||||
sounds risky, but it works extremely well.</para>
|
||||
|
||||
<listitem><para>Nix can efficiently upgrade between different versions
|
||||
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>
|
||||
</simplesect>
|
||||
|
||||
</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
|
||||
write Nix expressions for software not already in the Nix Packages
|
||||
collection. It also discusses some advanced topics, such as setting
|
||||
up a Nix-based build farm, and doing service deployment using
|
||||
Nix.</para>
|
||||
up a Nix-based build farm.</para>
|
||||
|
||||
<note><para>Some background information on Nix can be found in a
|
||||
number of papers. The ICSE 2004 paper <citetitle
|
||||
</section>
|
||||
|
||||
|
||||
<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
|
||||
a Memory Management Discipline on Software Deployment</citetitle>
|
||||
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
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
|
||||
Upgrading in a Purely Functional Component Deployment Model
|
||||
</citetitle> is about transparent patch deployment in Nix. Finally,
|
||||
the SCM-12 paper <citetitle
|
||||
</citetitle> is about transparent patch deployment in Nix. The SCM-12
|
||||
paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
|
||||
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>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<command>nix-build</command>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
|
||||
@@ -62,11 +62,11 @@ also <xref linkend="sec-channels" />.</para>
|
||||
<varlistentry><term><option>--update</option></term>
|
||||
|
||||
<listitem><para>Downloads the Nix expressions of all subscribed
|
||||
channels, makes the conjunction of these the default for
|
||||
<command>nix-env</command> operations (by calling <command>nix-env
|
||||
-I</command>), and performs a <command>nix-pull</command> on the
|
||||
manifests of all channels to make pre-built binaries
|
||||
available.</para></listitem>
|
||||
channels, makes them the default for <command>nix-env</command>
|
||||
operations (by symlinking them in the directory
|
||||
<filename>~/.nix-defexpr</filename>), and performs a
|
||||
<command>nix-pull</command> on the manifests of all channels to
|
||||
make pre-built binaries available.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -19,13 +19,7 @@
|
||||
<command>nix-env</command>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
<replaceable>attrPath</replaceable>
|
||||
</arg>
|
||||
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--file</option></arg>
|
||||
@@ -45,9 +39,6 @@
|
||||
<replaceable>system</replaceable>
|
||||
</arg>
|
||||
<arg><option>--dry-run</option></arg>
|
||||
<arg><option>--from-expression</option></arg>
|
||||
<arg><option>-E</option></arg>
|
||||
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>operation</replaceable></arg>
|
||||
<arg rep='repeat'><replaceable>options</replaceable></arg>
|
||||
<arg rep='repeat'><replaceable>arguments</replaceable></arg>
|
||||
@@ -58,7 +49,7 @@
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-env</command> is used to manipulate Nix
|
||||
user environments. User environments are sets of software components
|
||||
user environments. User environments are sets of software packages
|
||||
available to a user at some point in time. In other words, they are a
|
||||
synthesised view of the programs available in the Nix store. There
|
||||
may be many user environments: different users can have different
|
||||
@@ -151,13 +142,33 @@ linkend="sec-common-options" />.</para>
|
||||
|
||||
<varlistentry><term><filename>~/.nix-defexpr</filename></term>
|
||||
|
||||
<!-- !!! .nix-defexpr can be a directory now -->
|
||||
|
||||
<listitem><para>The default Nix expression used by the
|
||||
<option>--install</option>, <option>--upgrade</option>, and
|
||||
<option>--query --available</option> operations to obtain
|
||||
derivations. The <option>--file</option> option may be used to
|
||||
override this default.</para></listitem>
|
||||
<listitem><para>A directory that contains the default Nix
|
||||
expressions used by the <option>--install</option>,
|
||||
<option>--upgrade</option>, and <option>--query
|
||||
--available</option> operations to obtain derivations. The
|
||||
<option>--file</option> option may be used to override this
|
||||
default.</para>
|
||||
|
||||
<para>The Nix expressions in this directory are combined into a
|
||||
single attribute set, with each file as an attribute that has the
|
||||
name of the file. Thus, if <filename>~/.nix-defexpr</filename>
|
||||
contains two files, <filename>foo</filename> and
|
||||
<filename>bar</filename>, then the default Nix expression will
|
||||
essentially be
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
foo = import ~/.nix-defexpr/foo;
|
||||
bar = import ~/.nix-defexpr/bar;
|
||||
}</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The command <command>nix-channel</command> places symlinks
|
||||
to the downloaded Nix expressions from each subscribed channel in
|
||||
this directory.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -190,6 +201,7 @@ linkend="sec-common-options" />.</para>
|
||||
<arg choice='plain'><option>--install</option></arg>
|
||||
<arg choice='plain'><option>-i</option></arg>
|
||||
</group>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--preserve-installed</option></arg>
|
||||
<arg choice='plain'><option>-P</option></arg>
|
||||
@@ -221,11 +233,21 @@ number of possible ways:
|
||||
<para>If there are multiple derivations matching a name in
|
||||
<replaceable>args</replaceable> that have the same name (e.g.,
|
||||
<literal>gcc-3.3.6</literal> and <literal>gcc-4.1.1</literal>), then
|
||||
only the highest version will be installed. You can force the
|
||||
installation of multiple derivations with the same name by being
|
||||
specific about the versions. For instance, <literal>nix-env -i
|
||||
gcc-3.3.6 gcc-4.1.1</literal> will install both version of GCC (and
|
||||
will probably cause a user environment conflict!).</para></listitem>
|
||||
the derivation with the highest <emphasis>priority</emphasis> is
|
||||
used. A derivation can define a priority by declaring the
|
||||
<varname>meta.priority</varname> attribute. This attribute should
|
||||
be a number, with a higher value denoting a lower priority. The
|
||||
default priority is <literal>0</literal>.</para>
|
||||
|
||||
<para>If there are multiple matching derivations with the same
|
||||
priority, then the derivation with the highest version will be
|
||||
installed.</para>
|
||||
|
||||
<para>You can force the installation of multiple derivations with
|
||||
the same name by being specific about the versions. For instance,
|
||||
<literal>nix-env -i gcc-3.3.6 gcc-4.1.1</literal> will install both
|
||||
version of GCC (and will probably cause a user environment
|
||||
conflict!).</para></listitem>
|
||||
|
||||
<listitem><para>If <link
|
||||
linkend='opt-attr'><option>--attr</option></link>
|
||||
@@ -272,6 +294,15 @@ number of possible ways:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
|
||||
|
||||
<listitem><para>Use only derivations for which a substitute is
|
||||
registered, i.e., there is a pre-built binary available that can
|
||||
be downloaded in lieu of building the derivation. Thus, no
|
||||
packages will be built from source.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--preserve-installed</option></term>
|
||||
<term><option>-P</option></term>
|
||||
|
||||
@@ -387,7 +418,7 @@ the following paths will be substituted:
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--upgrade</option></title>
|
||||
<refsection xml:id="rsec-nix-env-upgrade"><title>Operation <option>--upgrade</option></title>
|
||||
|
||||
<refsection><title>Synopsis</title>
|
||||
|
||||
@@ -397,6 +428,7 @@ the following paths will be substituted:
|
||||
<arg choice='plain'><option>--upgrade</option></arg>
|
||||
<arg choice='plain'><option>-u</option></arg>
|
||||
</group>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--lt</option></arg>
|
||||
<arg choice='plain'><option>--leq</option></arg>
|
||||
@@ -471,6 +503,9 @@ installed.</para>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>For the other flags, see <option
|
||||
linkend="rsec-nix-env-install">--install</option>.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
@@ -580,6 +615,111 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id="rsec-nix-env-set-flag"><title>Operation <option>--set-flag</option></title>
|
||||
|
||||
<refsection><title>Synopsis</title>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>nix-env</command>
|
||||
<arg choice='plain'><option>--set-flag</option></arg>
|
||||
<arg choice='plain'><replaceable>name</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>value</replaceable></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>drvnames</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The <option>--set-flag</option> operation allows meta attributes
|
||||
of installed packages to be modified. There are several attributes
|
||||
that can be usefully modified, because they affect the behaviour of
|
||||
<command>nix-env</command> or the user environment build
|
||||
script:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><varname>priority</varname> can be changed to
|
||||
resolve filename clashes. The user environment build script uses
|
||||
the <varname>meta.priority</varname> attribute of derivations to
|
||||
resolve filename collisions between packages. Lower priority values
|
||||
denote a higher priority. For instance, the GCC wrapper package and
|
||||
the Binutils package in Nixpkgs both have a file
|
||||
<filename>bin/ld</filename>, so previously if you tried to install
|
||||
both you would get a collision. Now, on the other hand, the GCC
|
||||
wrapper declares a higher priority than Binutils, so the former’s
|
||||
<filename>bin/ld</filename> is symlinked in the user
|
||||
environment.</para></listitem>
|
||||
|
||||
<listitem><para><varname>keep</varname> can be set to
|
||||
<literal>true</literal> to prevent the package from being upgraded
|
||||
or replaced. This is useful if you want to hang on to an older
|
||||
version of a package.</para></listitem>
|
||||
|
||||
<listitem><para><varname>active</varname> can be set to
|
||||
<literal>false</literal> to “disable” the package. That is, no
|
||||
symlinks will be generated to the files of the package, but it
|
||||
remains part of the profile (so it won’t be garbage-collected). It
|
||||
can be set back to <literal>true</literal> to re-enable the
|
||||
package.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To prevent the currently installed Firefox from being upgraded:
|
||||
|
||||
<screen>
|
||||
$ nix-env --set-flag keep true firefox</screen>
|
||||
|
||||
After this, <command>nix-env -u</command> will ignore Firefox.</para>
|
||||
|
||||
<para>To disable the currently installed Firefox, then install a new
|
||||
Firefox while the old remains part of the profile:
|
||||
|
||||
<screen>
|
||||
$ nix-env -q \*
|
||||
firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
|
||||
|
||||
$ nix-env --preserve-installed -i firefox-2.0.0.11
|
||||
installing `firefox-2.0.0.11'
|
||||
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
|
||||
Collission between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
|
||||
and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
|
||||
<lineannotation>(i.e., can’t have two active at the same time)</lineannotation>
|
||||
|
||||
$ nix-env --set-flag active false firefox
|
||||
setting flag on `firefox-2.0.0.9'
|
||||
|
||||
$ nix-env --preserve-installed -i firefox-2.0.0.11
|
||||
installing `firefox-2.0.0.11'
|
||||
|
||||
$ nix-env -q \*
|
||||
firefox-2.0.0.11 <lineannotation>(the enabled one)</lineannotation>
|
||||
firefox-2.0.0.9 <lineannotation>(the disabled one)</lineannotation></screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>To make files from <literal>binutils</literal> take precedence
|
||||
over files from <literal>gcc</literal>:
|
||||
|
||||
<screen>
|
||||
$ nix-env --set-flag priority 5 binutils
|
||||
$ nix-env --set-flag priority 10 gcc</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--query</option></title>
|
||||
@@ -592,13 +732,14 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
<arg choice='plain'><option>--query</option></arg>
|
||||
<arg choice='plain'><option>-q</option></arg>
|
||||
</group>
|
||||
<arg><option>--xml</option></arg>
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--installed</option></arg>
|
||||
<arg choice='plain'><option>--available</option></arg>
|
||||
<arg choice='plain'><option>-a</option></arg>
|
||||
</group>
|
||||
|
||||
<sbr />
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--status</option></arg>
|
||||
@@ -607,8 +748,8 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
</arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
<arg choice='plain'><option>--attr-path</option></arg>
|
||||
<arg choice='plain'><option>-P</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
<arg><option>--no-name</option></arg>
|
||||
@@ -622,6 +763,28 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
<arg><option>--drv-path</option></arg>
|
||||
<arg><option>--out-path</option></arg>
|
||||
<arg><option>--description</option></arg>
|
||||
<arg><option>--meta</option></arg>
|
||||
|
||||
<sbr />
|
||||
|
||||
<arg><option>--xml</option></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--prebuilt-only</option></arg>
|
||||
<arg choice='plain'><option>-b</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
<replaceable>attribute-path</replaceable>
|
||||
</arg>
|
||||
|
||||
<sbr />
|
||||
|
||||
<arg choice='plain' rep='repeat'><replaceable>names</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
@@ -698,6 +861,16 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
|
||||
|
||||
<listitem><para>Show only derivations for which a substitute is
|
||||
registered, i.e., there is a pre-built binary available that can
|
||||
be downloaded in lieu of building the derivation. Thus, this
|
||||
shows all packages that probably can be installed
|
||||
quickly.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--status</option></term>
|
||||
<term><option>-s</option></term>
|
||||
|
||||
@@ -717,8 +890,8 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--attr</option></term>
|
||||
<term><option>-a</option></term>
|
||||
<varlistentry><term><option>--attr-path</option></term>
|
||||
<term><option>-P</option></term>
|
||||
|
||||
<listitem><para>Print the <emphasis>attribute path</emphasis> of
|
||||
the derivation, which can be used to unambiguously select it using
|
||||
@@ -741,35 +914,35 @@ user environment elements, etc. -->
|
||||
<listitem><para>Compare installed versions to available versions,
|
||||
or vice versa (if <option>--available</option> is given). This is
|
||||
useful for quickly seeing whether upgrades for installed
|
||||
components are available in a Nix expression. A column is added
|
||||
packages are available in a Nix expression. A column is added
|
||||
with the following meaning:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal><</literal> <replaceable>version</replaceable></term>
|
||||
|
||||
<listitem><para>A newer version of the component is available
|
||||
<listitem><para>A newer version of the package is available
|
||||
or installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>=</literal> <replaceable>version</replaceable></term>
|
||||
|
||||
<listitem><para>At most the same version of the component is
|
||||
<listitem><para>At most the same version of the package is
|
||||
available or installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>></literal> <replaceable>version</replaceable></term>
|
||||
|
||||
<listitem><para>Only older versions of the component are
|
||||
<listitem><para>Only older versions of the package are
|
||||
available or installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>- ?</literal></term>
|
||||
|
||||
<listitem><para>No version of the component is available or
|
||||
<listitem><para>No version of the package is available or
|
||||
installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -810,6 +983,14 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--meta</option></term>
|
||||
|
||||
<listitem><para>Print all of the meta-attributes of the
|
||||
derivation. This option is only available with
|
||||
<option>--xml</option>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -779,4 +779,178 @@ archive is read from standard input.</para>
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id='refsec-nix-store-export'><title>Operation <option>--export</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--export</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--export</option> writes a serialisation
|
||||
of the specified store paths to standard output in a format that can
|
||||
be imported into another Nix store with <command
|
||||
linkend="refsec-nix-store-import">nix-store --import</command>. This
|
||||
is like <command linkend="refsec-nix-store-dump">nix-store
|
||||
--dump</command>, except that the NAR archive produced by that command
|
||||
doesn’t contain the necessary meta-information to allow it to be
|
||||
imported into another Nix store (namely, the set of references of the
|
||||
path).</para>
|
||||
|
||||
<para>This command does not produce a <emphasis>closure</emphasis> of
|
||||
the specified paths, so if a store path references other store paths
|
||||
that are missing in the target Nix store, the import will fail. To
|
||||
copy a whole closure, do something like
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(nix-store -qR <replaceable>paths</replaceable>) > out</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>For an example of how <option>--export</option> and
|
||||
<option>--import</option> can be used, see the source of the <command
|
||||
linkend="sec-nix-copy-closure">nix-copy-closure</command>
|
||||
command.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id='refsec-nix-store-import'><title>Operation <option>--import</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--import</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--export</option> reads a serialisation of
|
||||
a set of store paths produced by <command
|
||||
linkend="refsec-nix-store-export">nix-store --import</command> from
|
||||
standard input and adds those store paths to the Nix store. Paths
|
||||
that already exist in the Nix store are ignored. If a path refers to
|
||||
another path that doesn’t exist in the Nix store, the import
|
||||
fails.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--optimise</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--optimise</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--optimise</option> reduces Nix store disk
|
||||
space usage by finding identical files in the store and hard-linking
|
||||
them to each other. It typically reduces the size of the store by
|
||||
something like 25-35%. Only regular files and symlinks are
|
||||
hard-linked in this manner. Files are considered identical when they
|
||||
have the same NAR archive serialisation: that is, regular files must
|
||||
have the same contents and permission (executable or non-executable),
|
||||
and symlinks must have the same contents.</para>
|
||||
|
||||
<para>After completion, or when the command is interrupted, a report
|
||||
on the achieved savings is printed on standard error.</para>
|
||||
|
||||
<para>Use <option>-vv</option> or <option>-vvv</option> to get some
|
||||
progress indication.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store --optimise
|
||||
hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1'
|
||||
<replaceable>...</replaceable>
|
||||
541838819 bytes (516.74 MiB) freed by hard-linking 54143 files;
|
||||
there are 114486 files with equal contents out of 215894 files in total
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--read-log</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--read-log</option></arg>
|
||||
<arg choice='plain'><option>-l</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--read-log</option> prints the build log
|
||||
of the specified store paths on standard output. The build log is
|
||||
whatever the builder of a derivation wrote to standard output and
|
||||
standard error. If a store path is not a derivation, the deriver of
|
||||
the store path is used.</para>
|
||||
|
||||
<para>Build logs are kept in
|
||||
<filename>/nix/var/log/nix/drvs</filename>. However, there is no
|
||||
guarantee that a build log is available for any particular store
|
||||
path. For instance, if the path was downloaded as a pre-built binary
|
||||
through a substitute, then the log is unavailable.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store -l $(which ktorrent)
|
||||
building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1
|
||||
unpacking sources
|
||||
unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz
|
||||
ktorrent-2.2.1/
|
||||
ktorrent-2.2.1/NEWS
|
||||
<replaceable>...</replaceable>
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!-- TODO: export, import operations -->
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
</group>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<arg><option>--max-silent-time</option></arg>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg><option>--keep-going</option></arg>
|
||||
<arg><option>-k</option></arg>
|
||||
<arg><option>--keep-failed</option></arg>
|
||||
|
||||
@@ -103,6 +103,17 @@
|
||||
</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>
|
||||
<term><option>-k</option></term>
|
||||
|
||||
@@ -257,6 +268,17 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
|
||||
|
||||
<listitem><para>This option is like <option>--arg</option>, only the
|
||||
value is not a Nix expression but a string. So instead of
|
||||
<literal>--arg system \"i686-linux\"</literal> (the outer quotes are
|
||||
to keep the shell happy) you can say <literal>--argstr system
|
||||
i686-linux</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-attr"><term><option>--attr</option> / <option>-A</option>
|
||||
<replaceable>attrPath</replaceable></term>
|
||||
|
||||
|
||||
22
doc/manual/opt-inst-syn.xml
Normal file
22
doc/manual/opt-inst-syn.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<nop xmlns="http://docbook.org/ns/docbook">
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--prebuilt-only</option></arg>
|
||||
<arg choice='plain'><option>-b</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
|
||||
<arg><option>--from-expression</option></arg>
|
||||
<arg><option>-E</option></arg>
|
||||
|
||||
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
|
||||
|
||||
</nop>
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
|
||||
<para>This chapter discusses how to do package management with Nix,
|
||||
i.e., how to obtain, install, upgrade, and erase components. This is
|
||||
i.e., how to obtain, install, upgrade, and erase packages. This is
|
||||
the “user’s” perspective of the Nix system — people
|
||||
who want to <emphasis>create</emphasis> components should consult
|
||||
who want to <emphasis>create</emphasis> packages should consult
|
||||
<xref linkend='chap-writing-nix-expressions' />.</para>
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ who want to <emphasis>create</emphasis> components should consult
|
||||
|
||||
<para>The main command for package management is <link
|
||||
linkend="sec-nix-env"><command>nix-env</command></link>. You can use
|
||||
it to install, upgrade, and erase components, and to query what
|
||||
components are installed or are available for installation.</para>
|
||||
it to install, upgrade, and erase packages, and to query what
|
||||
packages are installed or are available for installation.</para>
|
||||
|
||||
<para>In Nix, different users can have different “views”
|
||||
on the set of installed applications. That is, there might be lots of
|
||||
@@ -30,10 +30,10 @@ environment</emphasis>, which is just a directory tree consisting of
|
||||
symlinks to the files of the active applications. </para>
|
||||
|
||||
<para>Components are installed from a set of <emphasis>Nix
|
||||
expressions</emphasis> that tell Nix how to build those components,
|
||||
expressions</emphasis> that tell Nix how to build those packages,
|
||||
including, if necessary, their dependencies. There is a collection of
|
||||
Nix expressions called the Nix Package collection that contains
|
||||
components ranging from basic development stuff such as GCC and Glibc,
|
||||
packages ranging from basic development stuff such as GCC and Glibc,
|
||||
to end-user applications like Mozilla Firefox. (Nix is however not
|
||||
tied to the Nix Package collection; you could write your own Nix
|
||||
expressions based on it, or completely new ones.) You can download
|
||||
@@ -41,7 +41,7 @@ the latest version from <link
|
||||
xlink:href='http://nix.cs.uu.nl/dist/nix' />.</para>
|
||||
|
||||
<para>Assuming that you have downloaded and unpacked a release of Nix
|
||||
Packages, you can view the set of available components in the release:
|
||||
Packages, you can view the set of available packages in the release:
|
||||
|
||||
<screen>
|
||||
$ nix-env -qaf nixpkgs-<replaceable>version</replaceable> '*'
|
||||
@@ -74,7 +74,7 @@ gcc-4.1.1</screen>
|
||||
</para>
|
||||
|
||||
<para>It is also possible to see the <emphasis>status</emphasis> of
|
||||
available components, i.e., whether they are installed into the user
|
||||
available packages, i.e., whether they are installed into the user
|
||||
environment and/or present in the system:
|
||||
|
||||
<screen>
|
||||
@@ -86,24 +86,24 @@ IPS bison-1.875d
|
||||
...</screen>
|
||||
|
||||
The first character (<literal>I</literal>) indicates whether the
|
||||
component is installed in your current user environment. The second
|
||||
package is installed in your current user environment. The second
|
||||
(<literal>P</literal>) indicates whether it is present on your system
|
||||
(in which case installing it into your user environment would be a
|
||||
very quick operation). The last one (<literal>S</literal>) indicates
|
||||
whether there is a so-called <emphasis>substitute</emphasis> for the
|
||||
component, which is Nix’s mechanism for doing binary deployment. It
|
||||
just means that Nix knows that it can fetch a pre-built component from
|
||||
package, which is Nix’s mechanism for doing binary deployment. It
|
||||
just means that Nix knows that it can fetch a pre-built package from
|
||||
somewhere (typically a network server) instead of building it
|
||||
locally.</para>
|
||||
|
||||
<para>So now that we have a set of Nix expressions we can build the
|
||||
components contained in them. This is done using <literal>nix-env
|
||||
packages contained in them. This is done using <literal>nix-env
|
||||
-i</literal>. For instance,
|
||||
|
||||
<screen>
|
||||
$ nix-env -f nixpkgs-<replaceable>version</replaceable> -i subversion</screen>
|
||||
|
||||
will install the component called <literal>subversion</literal> (which
|
||||
will install the package called <literal>subversion</literal> (which
|
||||
is, of course, the <link
|
||||
xlink:href='http://subversion.tigris.org/'>Subversion version
|
||||
management system</link>).</para>
|
||||
@@ -112,7 +112,7 @@ management system</link>).</para>
|
||||
Subversion and all its dependencies. This will take quite a while —
|
||||
typically an hour or two on modern machines. Fortunately, there is a
|
||||
faster way (so do a Ctrl-C on that install operation!): you just need
|
||||
to tell Nix that pre-built binaries of all those components are
|
||||
to tell Nix that pre-built binaries of all those packages are
|
||||
available somewhere. This is done using the
|
||||
<command>nix-pull</command> command, which must be supplied with a URL
|
||||
containing a <emphasis>manifest</emphasis> describing what binaries
|
||||
@@ -153,7 +153,7 @@ expressions, use <parameter>-i</parameter> instead of
|
||||
<parameter>-u</parameter>; <parameter>-i</parameter> will remove
|
||||
whatever version is already installed.</para>
|
||||
|
||||
<para>You can also upgrade all components for which there are newer
|
||||
<para>You can also upgrade all packages for which there are newer
|
||||
versions:
|
||||
|
||||
<screen>
|
||||
@@ -199,19 +199,19 @@ set.</para></footnote></para>
|
||||
implementing the ability to allow different users to have different
|
||||
configurations, and to do atomic upgrades and rollbacks. To
|
||||
understand how they work, it’s useful to know a bit about how Nix
|
||||
works. In Nix, components are stored in unique locations in the
|
||||
works. In Nix, packages are stored in unique locations in the
|
||||
<emphasis>Nix store</emphasis> (typically,
|
||||
<filename>/nix/store</filename>). For instance, a particular version
|
||||
of the Subversion component might be stored in a directory
|
||||
of the Subversion package might be stored in a directory
|
||||
<filename>/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/</filename>,
|
||||
while another version might be stored in
|
||||
<filename>/nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2</filename>.
|
||||
The long strings prefixed to the directory names are cryptographic
|
||||
hashes<footnote><para>160-bit truncations of SHA-256 hashes encoded in
|
||||
a base-32 notation, to be precise.</para></footnote> of
|
||||
<emphasis>all</emphasis> inputs involved in building the component —
|
||||
<emphasis>all</emphasis> inputs involved in building the package —
|
||||
sources, dependencies, compiler flags, and so on. So if two
|
||||
components differ in any way, they end up in different locations in
|
||||
packages differ in any way, they end up in different locations in
|
||||
the file system, so they don’t interfere with each other. <xref
|
||||
linkend='fig-user-environments' /> shows a part of a typical Nix
|
||||
store.</para>
|
||||
@@ -231,12 +231,12 @@ $ /nix/store/dpmvp969yhdq...-subversion-1.1.3/bin/svn</screen>
|
||||
|
||||
every time you want to run Subversion. Of course we could set up the
|
||||
<envar>PATH</envar> environment variable to include the
|
||||
<filename>bin</filename> directory of every component we want to use,
|
||||
<filename>bin</filename> directory of every package we want to use,
|
||||
but this is not very convenient since changing <envar>PATH</envar>
|
||||
doesn’t take effect for already existing processes. The solution Nix
|
||||
uses is to create directory trees of symlinks to
|
||||
<emphasis>activated</emphasis> components. These are called
|
||||
<emphasis>user environments</emphasis> and they are components
|
||||
<emphasis>activated</emphasis> packages. These are called
|
||||
<emphasis>user environments</emphasis> and they are packages
|
||||
themselves (though automatically generated by
|
||||
<command>nix-env</command>), so they too reside in the Nix store. For
|
||||
instance, in <xref linkend='fig-user-environments' /> the user
|
||||
@@ -285,8 +285,8 @@ operation, a new user environment and generation link are created
|
||||
based on the current one, and finally the <filename>default</filename>
|
||||
symlink is made to point at the new generation. This last step is
|
||||
atomic on Unix, which explains how we can do atomic upgrades. (Note
|
||||
that the building/installing of new components doesn’t interfere in
|
||||
any way with old components, since they are stored in different
|
||||
that the building/installing of new packages doesn’t interfere in
|
||||
any way with old packages, since they are stored in different
|
||||
locations in the Nix store.)</para>
|
||||
|
||||
<para>If you find that you want to undo a <command>nix-env</command>
|
||||
@@ -352,18 +352,18 @@ This will <emphasis>not</emphasis> change the
|
||||
|
||||
<para><command>nix-env</command> operations such as upgrades
|
||||
(<option>-u</option>) and uninstall (<option>-e</option>) never
|
||||
actually delete components from the system. All they do (as shown
|
||||
actually delete packages from the system. All they do (as shown
|
||||
above) is to create a new user environment that no longer contains
|
||||
symlinks to the “deleted” components.</para>
|
||||
symlinks to the “deleted” packages.</para>
|
||||
|
||||
<para>Of course, since disk space is not infinite, unused components
|
||||
<para>Of course, since disk space is not infinite, unused packages
|
||||
should be removed at some point. You can do this by running the Nix
|
||||
garbage collector. It will remove from the Nix store any component
|
||||
garbage collector. It will remove from the Nix store any package
|
||||
not used (directly or indirectly) by any generation of any
|
||||
profile.</para>
|
||||
|
||||
<para>Note however that as long as old generations reference a
|
||||
component, it will not be deleted. After all, we wouldn’t be able to
|
||||
package, it will not be deleted. After all, we wouldn’t be able to
|
||||
do a rollback otherwise. So in order for garbage collection to be
|
||||
effective, you should also delete (some) old generations. Of course,
|
||||
this should only be done if you are certain that you will not need to
|
||||
@@ -486,7 +486,7 @@ makes the union of each channel’s Nix expressions the default for
|
||||
<screen>
|
||||
$ nix-env -u '*'</screen>
|
||||
|
||||
to upgrade all components in your profile to the latest versions
|
||||
to upgrade all packages in your profile to the latest versions
|
||||
available in the subscribed channels.</para>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -11,7 +11,7 @@ to the following chapters.</para>
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>Download a source tarball or RPM from <link
|
||||
xlink:href='http://www.cs.uu.nl/groups/ST/Trace/Nix'/>. Build source
|
||||
xlink:href='http://nix.cs.uu.nl/'/>. Build source
|
||||
distributions using the regular sequence:
|
||||
|
||||
<screen>
|
||||
@@ -22,8 +22,9 @@ $ make install <lineannotation>(as root)</lineannotation></screen>
|
||||
|
||||
This will install Nix in <filename>/nix</filename>. You shouldn't
|
||||
change the prefix if at all possible since that will make it
|
||||
impossible to use our pre-built components. Alternatively, you could
|
||||
grab an RPM if you're on an RPM-based system. You should also add
|
||||
impossible to use pre-built binaries from the Nixpkgs channel and
|
||||
other channels. Alternatively, you could grab an RPM if you're on an
|
||||
RPM-based system. You should also add
|
||||
<filename>/nix/etc/profile.d/nix.sh</filename> to your
|
||||
<filename>~/.bashrc</filename> (or some other login
|
||||
file).</para></listitem>
|
||||
@@ -40,14 +41,14 @@ $ nix-channel --add \
|
||||
<screen>
|
||||
$ nix-channel --update</screen>
|
||||
|
||||
Note that this in itself doesn't download any components, it just
|
||||
Note that this in itself doesn't download any packages, it just
|
||||
downloads the Nix expressions that build them and stores them
|
||||
somewhere (under <filename>~/.nix-defexpr</filename>, in case you're
|
||||
curious). Also, it registers the fact that pre-built binaries are
|
||||
available remotely.</para></listitem>
|
||||
|
||||
<listitem><para>See what installable components are currently
|
||||
available in the channel:
|
||||
<listitem><para>See what installable packages are currently available
|
||||
in the channel:
|
||||
|
||||
<screen>
|
||||
$ nix-env -qa ’*’ <lineannotation>(mind the quotes!)</lineannotation>
|
||||
@@ -59,13 +60,13 @@ libxslt-1.1.0
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Install some components from the channel:
|
||||
<listitem><para>Install some packages from the channel:
|
||||
|
||||
<screen>
|
||||
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
|
||||
|
||||
This should download the pre-built components; it should not build
|
||||
them locally (if it does, something went wrong).</para></listitem>
|
||||
This should download pre-built packages; it should not build them
|
||||
locally (if it does, something went wrong).</para></listitem>
|
||||
|
||||
<listitem><para>Test that they work:
|
||||
|
||||
@@ -92,8 +93,8 @@ $ nix-env -e hello</screen>
|
||||
$ nix-channel --update
|
||||
$ nix-env -u '*'</screen>
|
||||
|
||||
The latter command will upgrade each installed component for which
|
||||
there is a “newer” version (as determined by comparing the version
|
||||
The latter command will upgrade each installed package for which there
|
||||
is a “newer” version (as determined by comparing the version
|
||||
numbers).</para></listitem>
|
||||
|
||||
<listitem><para>You can also install specific packages directly from
|
||||
@@ -107,7 +108,7 @@ appear asking you whether it’s okay to install the package. Say
|
||||
installed.</para></listitem>
|
||||
|
||||
<listitem><para>If you're unhappy with the result of a
|
||||
<command>nix-env</command> action (e.g., an upgraded component turned
|
||||
<command>nix-env</command> action (e.g., an upgraded package turned
|
||||
out not to work properly), you can go back:
|
||||
|
||||
<screen>
|
||||
@@ -124,7 +125,7 @@ $ nix-collect-garbage -d</screen>
|
||||
|
||||
<!--
|
||||
The first command deletes old “generations” of your profile (making
|
||||
rollbacks impossible, but also making the components in those old
|
||||
rollbacks impossible, but also making the packages in those old
|
||||
generations available for garbage collection), while the second
|
||||
command actually deletes them.-->
|
||||
|
||||
|
||||
@@ -8,115 +8,271 @@
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (TBA)</title>
|
||||
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (TBA)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><command>nix-store --dump-db / --load-db</command>.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (December 31,
|
||||
2007)</title>
|
||||
|
||||
<para>Nix 0.11 has many improvements over the previous stable release.
|
||||
The most important improvement is secure multi-user support. It also
|
||||
features many usability enhancements and language extensions, many of
|
||||
them prompted by NixOS, the purely functional Linux distribution based
|
||||
on Nix. Here is an (incomplete) list:</para>
|
||||
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
|
||||
<listitem><para>TODO: multi-user support. The old setuid method for
|
||||
sharing a store between multiple users has been
|
||||
removed.</para></listitem>
|
||||
<listitem><para>Secure multi-user support. A single Nix store can
|
||||
now be shared between multiple (possible untrusted) users. This is
|
||||
an important feature for NixOS, where it allows non-root users to
|
||||
install software. The old setuid method for sharing a store between
|
||||
multiple users has been removed. Details for setting up a
|
||||
multi-user store can be found in the manual.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The new command <command>nix-copy-closure</command>
|
||||
gives you an easy and efficient way to exchange software between
|
||||
machines. It copies the missing parts of the closure of a set of
|
||||
store path to or from a remote machine.</para></listitem>
|
||||
store path to or from a remote machine via
|
||||
<command>ssh</command>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now by default
|
||||
computes the SHA-256 hash of the file instead of the MD5 hash. In
|
||||
calls to <function>fetchurl</function> you should pass an
|
||||
<literal>sha256</literal> attribute instead of
|
||||
<literal>md5</literal>. You can pass either a hexadecimal or a
|
||||
base-32 encoding of the hash.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-store</command> has a new operation
|
||||
<option>--read-log</option> (<option>-l</option>)
|
||||
<parameter>paths</parameter> that shows the build log of the given
|
||||
paths.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <varname>allowedReferences</varname> for
|
||||
checking the set of references in the output of a
|
||||
derivation.</para></listitem>
|
||||
<listitem><para>A new kind of string literal: strings between double
|
||||
single-quotes (<literal>''</literal>) have indentation
|
||||
“intelligently” removed. This allows large strings (such as shell
|
||||
scripts or configuration file fragments in NixOS) to cleanly follow
|
||||
the indentation of the surrounding expression. It also requires
|
||||
much less escaping, since <literal>''</literal> is less common in
|
||||
most languages than <literal>"</literal>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: semantic cleanups of string concatenation
|
||||
etc. (mostly in r6740).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: now using Berkeley DB 4.5.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: option <option>--reregister</option> in
|
||||
<command>nix-store --register-validity</command>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: magic <varname>exportReferencesGraph</varname>
|
||||
attribute.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: option <option>--max-silent-time</option>,
|
||||
configuration setting
|
||||
<literal>build-max-silent-time</literal>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command>
|
||||
<option>--set</option>.</para></listitem>
|
||||
<listitem><para><command>nix-env</command> <option>--set</option>
|
||||
modifies the current generation of a profile so that it contains
|
||||
exactly the specified derivation, and nothing else. For example,
|
||||
<literal>nix-env -p /nix/var/nix/profiles/browser --set
|
||||
firefox</literal> lets the profile named
|
||||
<filename>browser</filename> contain just Firefox.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <option>--argstr</option>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command> now maintains meta
|
||||
info about installed packages in user environments. <option>-q
|
||||
--xml --meta</option> to show all meta info.</para></listitem>
|
||||
<listitem><para><command>nix-env</command> now maintains
|
||||
meta-information about installed packages in profiles. The
|
||||
meta-information is the contents of the <varname>meta</varname>
|
||||
attribute of derivations, such as <varname>description</varname> or
|
||||
<varname>homepage</varname>. The command <literal>nix-env -q --xml
|
||||
--meta</literal> shows all meta-information.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command>
|
||||
<option>--set-flag</option>. Specific flags:
|
||||
<literal>active</literal>, <literal>priority</literal>,
|
||||
<literal>keep</literal>.</para></listitem>
|
||||
<listitem><para><command>nix-env</command> now uses the
|
||||
<varname>meta.priority</varname> attribute of derivations to resolve
|
||||
filename collisions between packages. Lower priority values denote
|
||||
a higher priority. For instance, the GCC wrapper package and the
|
||||
Binutils package in Nixpkgs both have a file
|
||||
<filename>bin/ld</filename>, so previously if you tried to install
|
||||
both you would get a collision. Now, on the other hand, the GCC
|
||||
wrapper declares a higher priority than Binutils, so the former’s
|
||||
<filename>bin/ld</filename> is symlinked in the user
|
||||
environment.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-env -i / -u</command>: instead of
|
||||
breaking package ties by version, break them by priority and version
|
||||
number. That is, if there are multiple packages with the same name,
|
||||
then pick the package with the highest priority, and only use the
|
||||
version if there are multiple packages with the same
|
||||
priority.</para>
|
||||
|
||||
<para>This makes it possible to mark specific versions/variant in
|
||||
Nixpkgs more or less desirable than others. A typical example would
|
||||
be a beta version of some package (e.g.,
|
||||
<literal>gcc-4.2.0rc1</literal>) which should not be installed even
|
||||
though it is the highest version, except when it is explicitly
|
||||
selected (e.g., <literal>nix-env -i
|
||||
gcc-4.2.0rc1</literal>).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-env --set-flag</command> allows meta
|
||||
attributes of installed packages to be modified. There are several
|
||||
attributes that can be usefully modified, because they affect the
|
||||
behaviour of <command>nix-env</command> or the user environment
|
||||
build script:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><varname>meta.priority</varname> can be changed
|
||||
to resolve filename clashes (see above).</para></listitem>
|
||||
|
||||
<listitem><para><varname>meta.keep</varname> can be set to
|
||||
<literal>true</literal> to prevent the package from being
|
||||
upgraded or replaced. Useful if you want to hang on to an older
|
||||
version of a package.</para></listitem>
|
||||
|
||||
<listitem><para><varname>meta.active</varname> can be set to
|
||||
<literal>false</literal> to “disable” the package. That is, no
|
||||
symlinks will be generated to the files of the package, but it
|
||||
remains part of the profile (so it won’t be garbage-collected).
|
||||
Set it back to <literal>true</literal> to re-enable the
|
||||
package.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command> <option>-i</option>
|
||||
/ <option>-u</option> take package priorities into
|
||||
account.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-env -q</command> now has a flag
|
||||
<option>--prebuilt-only</option> (<option>-b</option>) that causes
|
||||
<command>nix-env</command> to show only those derivations whose
|
||||
output is already in the Nix store or that can be substituted (i.e.,
|
||||
downloaded from somewhere). In other words, it shows the packages
|
||||
that can be installed “quickly”, i.e., don’t need to be built from
|
||||
source.</para></listitem>
|
||||
source. The <option>-b</option> flag is also available in
|
||||
<command>nix-env -i</command> and <command>nix-env -u</command> to
|
||||
filter out derivations for which no pre-built binary is
|
||||
available.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: new built-ins
|
||||
<function>builtins.attrNames</function>,
|
||||
<function>builtins.filterSource</function>,
|
||||
<function>builtins.sub</function>,
|
||||
<function>builtins.stringLength</function>,
|
||||
<function>builtins.substring</function>.</para></listitem>
|
||||
<listitem><para>The new option <option>--argstr</option> (in
|
||||
<command>nix-env</command>, <command>nix-instantiate</command> and
|
||||
<command>nix-build</command>) is like <option>--arg</option>, except
|
||||
that the value is a string. For example, <literal>--argstr system
|
||||
i686-linux</literal> is equivalent to <literal>--arg system
|
||||
\"i686-linux\"</literal> (note that <option>--argstr</option>
|
||||
prevents annoying quoting around shell arguments).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: each subscribed channel is its own attribute
|
||||
in the top-level expression generated for the channel, this allows
|
||||
disambiguation (<command>nix-env -qaA</command>).</para></listitem>
|
||||
<listitem><para><command>nix-store</command> has a new operation
|
||||
<option>--read-log</option> (<option>-l</option>)
|
||||
<parameter>paths</parameter> that shows the build log of the given
|
||||
paths.</para></listitem>
|
||||
|
||||
|
||||
<!--
|
||||
<listitem><para>TODO: semantic cleanups of string concatenation
|
||||
etc. (mostly in r6740).</para></listitem>
|
||||
-->
|
||||
|
||||
|
||||
<listitem><para>TODO: substitutes table is gone, registering
|
||||
substitutes is now much faster.</para></listitem>
|
||||
<listitem><para>Nix now uses Berkeley DB 4.5. The database is
|
||||
upgraded automatically, but you should be careful not to use old
|
||||
versions of Nix that still use Berkeley DB 4.4.</para></listitem>
|
||||
|
||||
|
||||
<!-- foo
|
||||
<listitem><para>TODO: option <option>- -reregister</option> in
|
||||
<command>nix-store - -register-validity</command>.</para></listitem>
|
||||
-->
|
||||
|
||||
|
||||
<listitem><para>The option <option>--max-silent-time</option>
|
||||
(corresponding to the configuration setting
|
||||
<literal>build-max-silent-time</literal>) allows you to set a
|
||||
timeout on builds — if a build produces no output on
|
||||
<literal>stdout</literal> or <literal>stderr</literal> for the given
|
||||
number of seconds, it is terminated. This is useful for recovering
|
||||
automatically from builds that are stuck in an infinite
|
||||
loop.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-channel</command>: each subscribed
|
||||
channel is its own attribute in the top-level expression generated
|
||||
for the channel. This allows disambiguation (e.g. <literal>nix-env
|
||||
-i -A nixpkgs_unstable.firefox</literal>).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The substitutes table has been removed from the
|
||||
database. This makes operations such as <command>nix-pull</command>
|
||||
and <command>nix-channel --update</command> much, much
|
||||
faster.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-pull</command> now supports
|
||||
bzip2-compressed manifests. This speeds up
|
||||
channels.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now has a
|
||||
limited form of caching. This is used by
|
||||
<command>nix-channel</command> to prevent unnecessary downloads when
|
||||
the channel hasn’t changed.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now by default
|
||||
computes the SHA-256 hash of the file instead of the MD5 hash. In
|
||||
calls to <function>fetchurl</function> you should pass the
|
||||
<literal>sha256</literal> attribute instead of
|
||||
<literal>md5</literal>. You can pass either a hexadecimal or a
|
||||
base-32 encoding of the hash.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Nix can now perform builds in an automatically
|
||||
generated “chroot”. This prevents a builder from accessing stuff
|
||||
outside of the Nix store, and thus helps ensure purity. This is an
|
||||
experimental feature.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The new command <command>nix-store
|
||||
--optimise</command> reduces Nix store disk space usage by finding
|
||||
identical files in the store and hard-linking them to each other.
|
||||
It typically reduces the size of the store by something like
|
||||
25-35%.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><filename>~/.nix-defexpr</filename> can now be a
|
||||
directory, in which case the Nix expressions in that directory are
|
||||
combined into an attribute set, with the file names used as the
|
||||
names of the attributes. The command <command>nix-env
|
||||
--import</command> (which set the
|
||||
<filename>~/.nix-defexpr</filename> symlink) is
|
||||
removed.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Derivations can specify the new special attribute
|
||||
<varname>allowedReferences</varname> to enforce that the references
|
||||
in the output of a derivation are a subset of a declared set of
|
||||
paths. For example, if <varname>allowedReferences</varname> is an
|
||||
empty list, then the output must not have any references. This is
|
||||
used in NixOS to check that generated files such as initial ramdisks
|
||||
for booting Linux don’t have any dependencies.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The new attribute
|
||||
<varname>exportReferencesGraph</varname> allows builders access to
|
||||
the references graph of their inputs. This is used in NixOS for
|
||||
tasks such as generating ISO-9660 images that contain a Nix store
|
||||
populated with the closure of certain paths.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Fixed-output derivations (like
|
||||
<function>fetchurl</function>) can define the attribute
|
||||
<varname>impureEnvVars</varname> to allow external environment
|
||||
variables to be passed to builders. This is used in Nixpkgs to
|
||||
support proxy configuration, among other things.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Several new built-in functions:
|
||||
<function>builtins.attrNames</function>,
|
||||
<function>builtins.filterSource</function>,
|
||||
<function>builtins.isAttrs</function>,
|
||||
<function>builtins.isFunction</function>,
|
||||
<function>builtins.listToAttrs</function>,
|
||||
<function>builtins.stringLength</function>,
|
||||
<function>builtins.sub</function>,
|
||||
<function>builtins.substring</function>,
|
||||
<function>throw</function>,
|
||||
<function>builtins.trace</function>,
|
||||
<function>builtins.readFile</function>.</para></listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
@@ -46,6 +46,11 @@ h3 /* subsections */
|
||||
font-size: 125%;
|
||||
}
|
||||
|
||||
div.simplesect h2
|
||||
{
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
div.appendix h3
|
||||
{
|
||||
font-size: 150%;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<title>Troubleshooting</title>
|
||||
|
||||
|
||||
<para>This section provides solutions for some common problems.</para>
|
||||
<para>This section provides solutions for some common problems. See
|
||||
the <link xlink:href="https://bugs.cs.uu.nl/browse/NIX">Nix
|
||||
bug tracker</link> for a list of currently known issues.</para>
|
||||
|
||||
|
||||
<section><title>Berkeley DB: <quote>Cannot allocate memory</quote></title>
|
||||
@@ -77,6 +79,46 @@ $ nix-store --verify</screen>
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Berkeley DB out of locks</title>
|
||||
|
||||
<para>It is possible, especially in <command>nix-store
|
||||
--verify</command> or when running the garbage collector, to run out
|
||||
of Berkeley DB locks, like this:
|
||||
|
||||
<screen>
|
||||
$ nix-store --verify
|
||||
checking path existence
|
||||
checking path realisability
|
||||
checking the derivers table
|
||||
checking the references table
|
||||
Berkeley DB error: Lock table is out of available object entries
|
||||
error: Db::get: Cannot allocate memory</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>A workaround is to increase the number of locks that Berkeley DB
|
||||
allocates. (The real solution would be for Nix to not use so many
|
||||
locks.) This can be done by putting the following in the file
|
||||
<filename>/nix/var/nix/db/<link
|
||||
xlink:href="http://www.oracle.com/technology/documentation/berkeley-db/db/ref/env/db_config.html">DB_CONFIG</link></filename>:
|
||||
|
||||
<programlisting>
|
||||
set_lk_max_locks 100000
|
||||
set_lk_max_lockers 100000
|
||||
set_lk_max_objects 100000
|
||||
</programlisting>
|
||||
|
||||
(Increase these numbers if necessary.) Then make sure that there are
|
||||
no running Nix processes and delete the Berkeley DB environment:
|
||||
|
||||
<screen>
|
||||
$ rm /nix/var/nix/db/__db.*</screen>
|
||||
|
||||
The Berkeley DB environment is automatically recreated with the new
|
||||
limits when you run any Nix command.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Collisions in <command>nix-env</command></title>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
8
externals/Makefile.am
vendored
8
externals/Makefile.am
vendored
@@ -2,6 +2,8 @@
|
||||
|
||||
DB = db-4.5.20
|
||||
|
||||
if OLD_DB_COMPAT
|
||||
|
||||
$(DB).tar.gz:
|
||||
@echo "Nix requires Berkeley DB to build."
|
||||
@echo "Please download version 4.5.20 from"
|
||||
@@ -32,6 +34,12 @@ build-db: have-db
|
||||
touch build-db
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
build-db:
|
||||
|
||||
endif
|
||||
|
||||
|
||||
# CWI ATerm
|
||||
|
||||
|
||||
35
misc/vim/syntax/nix.vim
Normal file
35
misc/vim/syntax/nix.vim
Normal file
@@ -0,0 +1,35 @@
|
||||
" Vim syntax file
|
||||
" Language: nix
|
||||
" Maintainer: Marc Weber <marco-oweber@gmx.de>
|
||||
" Modify and commit if you feel that way
|
||||
" Last Change: 2007 Dec
|
||||
|
||||
" Quit when a (custom) syntax file was already loaded
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn keyword nixKeyword let throw inherit import true false null with
|
||||
syn keyword nixConditional if else then
|
||||
syn keyword nixBrace ( ) { } =
|
||||
syn keyword nixBuiltin __currentSystem __currentTime __isFunction __getEnv __trace __toPath __pathExists
|
||||
\ __readFile __toXML __toFile __filterSource __attrNames __getAttr __hasAttr __isAttrs __listToAttrs __isList
|
||||
\ __head __tail __add __sub __lessThan __substring __stringLength
|
||||
|
||||
syn match nixAttr "\w\+\ze\s*="
|
||||
syn match nixFuncArg "\zs\w\+\ze\s*:"
|
||||
syn region nixStringParam start=+\${+ end=+}+
|
||||
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
|
||||
syn match nixEndOfLineComment "#.*$"
|
||||
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
|
||||
|
||||
hi def link nixKeyword Keyword
|
||||
hi def link nixConditional Conditional
|
||||
hi def link nixBrace Special
|
||||
hi def link nixString String
|
||||
hi def link nixBuiltin Special
|
||||
hi def link nixStringParam Macro
|
||||
hi def link nixMultiLineComment Comment
|
||||
hi def link nixEndOfLineComment Comment
|
||||
hi def link nixAttr Identifier
|
||||
hi def link nixFuncArg Identifier
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
### 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.
|
||||
# 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
|
||||
@@ -135,6 +135,44 @@
|
||||
#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'
|
||||
#
|
||||
# This option specifies the canonical Nix system name of the current
|
||||
|
||||
@@ -2,9 +2,10 @@ bin_SCRIPTS = nix-collect-garbage \
|
||||
nix-pull nix-push nix-prefetch-url \
|
||||
nix-install-package nix-channel nix-build \
|
||||
nix-pack-closure nix-unpack-closure \
|
||||
nix-copy-closure
|
||||
nix-copy-closure
|
||||
|
||||
noinst_SCRIPTS = nix-profile.sh generate-patches.pl find-runtime-roots.pl
|
||||
noinst_SCRIPTS = nix-profile.sh generate-patches.pl \
|
||||
find-runtime-roots.pl build-remote.pl nix-reduce-build
|
||||
|
||||
nix-pull nix-push: readmanifest.pm readconfig.pm download-using-manifests.pl
|
||||
|
||||
@@ -17,6 +18,7 @@ install-exec-local: readmanifest.pm download-using-manifests.pl find-runtime-roo
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) generate-patches.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
|
||||
include ../substitute.mk
|
||||
@@ -32,4 +34,6 @@ EXTRA_DIST = nix-collect-garbage.in \
|
||||
generate-patches.pl.in \
|
||||
nix-pack-closure.in nix-unpack-closure.in \
|
||||
nix-copy-closure.in \
|
||||
find-runtime-roots.pl.in
|
||||
find-runtime-roots.pl.in \
|
||||
build-remote.pl.in \
|
||||
nix-reduce-build.in
|
||||
|
||||
208
scripts/build-remote.pl.in
Executable file
208
scripts/build-remote.pl.in
Executable file
@@ -0,0 +1,208 @@
|
||||
#! @perl@ -w
|
||||
|
||||
use strict;
|
||||
use Fcntl ':flock';
|
||||
use English '-no_match_vars';
|
||||
|
||||
# General operation:
|
||||
#
|
||||
# Try to find a free machine of type $neededSystem. We do this as
|
||||
# follows:
|
||||
# - We acquire an exclusive lock on $currentLoad/main-lock.
|
||||
# - For each machine $machine of type $neededSystem and for each $slot
|
||||
# less than the maximum load for that machine, we try to get an
|
||||
# exclusive lock on $currentLoad/$machine-$slot (without blocking).
|
||||
# If we get such a lock, we send "accept" to the caller. Otherwise,
|
||||
# we send "postpone" and exit.
|
||||
# - We release the exclusive lock on $currentLoad/main-lock.
|
||||
# - We perform the build on $neededSystem.
|
||||
# - We release the exclusive lock on $currentLoad/$machine-$slot.
|
||||
#
|
||||
# The nice thing about this scheme is that if we die prematurely, the
|
||||
# locks are released automatically.
|
||||
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my $amWilling = shift @ARGV;
|
||||
my $localSystem = shift @ARGV;
|
||||
my $neededSystem = shift @ARGV;
|
||||
my $drvPath = shift @ARGV;
|
||||
|
||||
sub sendReply {
|
||||
my $reply = shift;
|
||||
open OUT, ">&3" or die;
|
||||
print OUT "$reply\n";
|
||||
close OUT;
|
||||
}
|
||||
|
||||
sub decline {
|
||||
sendReply "decline";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
decline unless defined $currentLoad;
|
||||
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
decline if !defined $conf || ! -e $conf;
|
||||
|
||||
# Decline if the local system can do the build.
|
||||
decline if $amWilling && ($localSystem eq $neededSystem);
|
||||
|
||||
|
||||
# Otherwise find a willing remote machine.
|
||||
my %machines;
|
||||
my %systemTypes;
|
||||
my %sshKeys;
|
||||
my %maxJobs;
|
||||
my %curJobs;
|
||||
|
||||
|
||||
# Read the list of machines.
|
||||
open CONF, "< $conf" or die;
|
||||
|
||||
while (<CONF>) {
|
||||
chomp;
|
||||
s/\#.*$//g;
|
||||
next if /^\s*$/;
|
||||
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s*$/ or die;
|
||||
$machines{$1} = "";
|
||||
$systemTypes{$1} = $2;
|
||||
$sshKeys{$1} = $3;
|
||||
$maxJobs{$1} = $4;
|
||||
}
|
||||
|
||||
close CONF;
|
||||
|
||||
|
||||
# Acquire the exclusive lock on $currentLoad/main-lock.
|
||||
my $mainLock = "$currentLoad/main-lock";
|
||||
open MAINLOCK, ">>$mainLock" or die;
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
|
||||
# Find a suitable system.
|
||||
my $rightType = 0;
|
||||
my $machine;
|
||||
LOOP: foreach my $cur (keys %machines) {
|
||||
if ($neededSystem eq $systemTypes{$cur}) {
|
||||
$rightType = 1;
|
||||
|
||||
# We have a machine of the right type. Try to get a lock on
|
||||
# one of the machine's lock files.
|
||||
my $slot = 0;
|
||||
while ($slot < $maxJobs{$cur}) {
|
||||
my $slotLock = "$currentLoad/$cur-$slot";
|
||||
open SLOTLOCK, ">>$slotLock" or die;
|
||||
if (flock(SLOTLOCK, LOCK_EX | LOCK_NB)) {
|
||||
$machine = $cur;
|
||||
last LOOP;
|
||||
}
|
||||
close SLOTLOCK;
|
||||
$slot++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close MAINLOCK;
|
||||
|
||||
|
||||
# Didn't find one?
|
||||
if (!defined $machine) {
|
||||
if ($rightType) {
|
||||
sendReply "postpone";
|
||||
exit 0;
|
||||
} else {
|
||||
decline;
|
||||
}
|
||||
}
|
||||
|
||||
# Yes we did, accept.
|
||||
sendReply "accept";
|
||||
open IN, "<&4" or die;
|
||||
my $x = <IN>;
|
||||
chomp $x;
|
||||
#print "got $x\n";
|
||||
close IN;
|
||||
|
||||
if ($x ne "okay") {
|
||||
exit 0;
|
||||
}
|
||||
|
||||
|
||||
# Do the actual job.
|
||||
print "BUILDING REMOTE: $drvPath on $machine\n";
|
||||
|
||||
# Make sure that we don't get any SSH passphrase or host key popups -
|
||||
# if there is any problem it should fail, not do something
|
||||
# interactive.
|
||||
$ENV{"DISPLAY"} = "";
|
||||
$ENV{"SSH_PASSWORD_FILE="} = "";
|
||||
$ENV{"SSH_ASKPASS="} = "";
|
||||
|
||||
my $sshOpts = "-i $sshKeys{$machine} -x";
|
||||
|
||||
# Hack to support Cygwin: if we login without a password, we don't
|
||||
# have exactly the same right as when we do. This causes the
|
||||
# Microsoft C compiler to fail with certain flags:
|
||||
#
|
||||
# http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99676
|
||||
#
|
||||
# So as a workaround, we pass a verbatim password. ssh tries to makes
|
||||
# this very hard; the trick is to make it call SSH_ASKPASS to get the
|
||||
# password. (It only calls this command when there is no controlling
|
||||
# terminal, but Nix ensures that is is the case. When doing this
|
||||
# manually, use setsid(1).)
|
||||
if ($sshKeys{$machine} =~ /^password:/) {
|
||||
my $passwordFile = $sshKeys{$machine};
|
||||
$passwordFile =~ s/^password://;
|
||||
$sshOpts = "ssh -x";
|
||||
$ENV{"SSH_PASSWORD_FILE"} = $passwordFile;
|
||||
$ENV{"SSH_ASKPASS"} = "/tmp/writepass";
|
||||
|
||||
open WRITEPASS, ">/tmp/writepass" or die;
|
||||
print WRITEPASS "#! /bin/sh\ncat \"\$SSH_PASSWORD_FILE\"";
|
||||
close WRITEPASS;
|
||||
chmod 0755, "/tmp/writepass" or die;
|
||||
}
|
||||
|
||||
my $inputs = `cat inputs`; die if ($? != 0);
|
||||
$inputs =~ s/\n/ /g;
|
||||
|
||||
my $outputs = `cat outputs`; die if ($? != 0);
|
||||
$outputs =~ s/\n/ /g;
|
||||
|
||||
print "COPYING INPUTS...\n";
|
||||
|
||||
my $maybeSign = "";
|
||||
$maybeSign = "--sign" if -e "/nix/etc/nix/signing-key.sec";
|
||||
|
||||
system("NIX_SSHOPTS=\"$sshOpts\" nix-copy-closure $machine $maybeSign $drvPath $inputs") == 0
|
||||
or die "cannot copy inputs to $machine: $?";
|
||||
|
||||
print "BUILDING...\n";
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store -rvvK $drvPath'") == 0
|
||||
or die "remote build on $machine failed: $?";
|
||||
|
||||
print "REMOTE BUILD DONE: $drvPath on $machine\n";
|
||||
|
||||
foreach my $output (split '\n', $outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store --export $maybeSignRemote $output' > dump") == 0
|
||||
or die "cannot copy $output from $machine: $?";
|
||||
|
||||
# This doesn't work yet, since the caller has a lock on the output
|
||||
# path. We should move towards lock-free invocation of build
|
||||
# hooks and substitutes.
|
||||
#system("nix-store --import < dump") == 0
|
||||
# or die "cannot import $output: $?";
|
||||
|
||||
# Hack: skip the first 8 bytes (the nix-store --export next
|
||||
# archive marker). The archive follows.
|
||||
system("(dd bs=1 count=8 of=/dev/null && cat) < dump | nix-store --restore $output") == 0
|
||||
or die "cannot restore $output: $?";
|
||||
}
|
||||
@@ -19,8 +19,6 @@ chdir $tmpDir or die "cannot change to `$tmpDir': $!";
|
||||
my $tmpNar = "$tmpDir/nar";
|
||||
my $tmpNar2 = "$tmpDir/nar2";
|
||||
|
||||
END { unlink $tmpNar; unlink $tmpNar2; rmdir $tmpDir; }
|
||||
|
||||
|
||||
# Load all manifests.
|
||||
my %narFiles;
|
||||
|
||||
@@ -77,9 +77,9 @@ EOF
|
||||
push @instArgs, ("--attr", $ARGV[$n]);
|
||||
}
|
||||
|
||||
elsif ($arg eq "--arg") {
|
||||
die "$0: `--arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
||||
push @instArgs, ("--arg", $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
elsif ($arg eq "--arg" || $arg eq "--argstr") {
|
||||
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
||||
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ $binDir = "@bindir@" unless defined $binDir;
|
||||
|
||||
if (scalar @ARGV < 1) {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] PATHS...
|
||||
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] [--gzip] PATHS...
|
||||
EOF
|
||||
;
|
||||
exit 1;
|
||||
|
||||
@@ -36,6 +36,28 @@ if test -n "$expHash"; then
|
||||
fi
|
||||
|
||||
|
||||
mkTempDir() {
|
||||
local i=0
|
||||
while true; do
|
||||
if test -z "$TMPDIR"; then TMPDIR=/tmp; fi
|
||||
tmpPath=$TMPDIR/nix-prefetch-url-$$-$i
|
||||
if mkdir "$tmpPath"; then break; fi
|
||||
# !!! to bad we can't check for ENOENT in mkdir, so this check
|
||||
# is slightly racy (it bombs out if somebody just removed
|
||||
# $tmpPath...).
|
||||
if ! test -e "$tmpPath"; then exit 1; fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
trap removeTempDir EXIT SIGINT SIGQUIT
|
||||
}
|
||||
|
||||
removeTempDir() {
|
||||
if test -n "$tmpPath"; then
|
||||
rm -rf "$tmpPath" || true
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
doDownload() {
|
||||
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
|
||||
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
|
||||
@@ -46,9 +68,8 @@ doDownload() {
|
||||
# download the file and add it to the store.
|
||||
if test -z "$finalPath"; then
|
||||
|
||||
tmpPath=/tmp/nix-prefetch-url-$$ # !!! security?
|
||||
mkTempDir
|
||||
tmpFile=$tmpPath/$name
|
||||
mkdir $tmpPath # !!! retry if tmpPath already exists
|
||||
|
||||
# Optionally do timestamp-based caching of the download.
|
||||
# Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
|
||||
@@ -98,8 +119,6 @@ if test -z "$finalPath"; then
|
||||
# Add the downloaded file to the Nix store.
|
||||
finalPath=$(@bindir@/nix-store --add-fixed "$hashType" $tmpFile)
|
||||
|
||||
if test -n "$tmpPath"; then rm -rf $tmpPath || true; fi
|
||||
|
||||
if test -n "$expHash" -a "$expHash" != "$hash"; then
|
||||
echo "hash mismatch for URL \`$url'" >&2
|
||||
exit 1
|
||||
|
||||
@@ -116,9 +116,9 @@ print NIX "]";
|
||||
close NIX;
|
||||
|
||||
|
||||
# Instantiate store expressions from the Nix expression.
|
||||
# Instantiate store derivations from the Nix expression.
|
||||
my @storeExprs;
|
||||
print STDERR "instantiating store expressions...\n";
|
||||
print STDERR "instantiating store derivations...\n";
|
||||
my $pid = open(READ, "$binDir/nix-instantiate $nixExpr|")
|
||||
or die "cannot run nix-instantiate";
|
||||
while (<READ>) {
|
||||
@@ -146,7 +146,7 @@ while (scalar @tmp > 0) {
|
||||
# probably wouldn't make that much sense; pumping lots of data
|
||||
# around just to compress them won't gain that much.
|
||||
$ENV{"NIX_BUILD_HOOK"} = "";
|
||||
my $pid = open(READ, "$binDir/nix-store --realise @tmp2|")
|
||||
my $pid = open(READ, "$binDir/nix-store --no-build-hook --realise @tmp2|")
|
||||
or die "cannot run nix-store";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
|
||||
68
scripts/nix-reduce-build.in
Normal file
68
scripts/nix-reduce-build.in
Normal file
@@ -0,0 +1,68 @@
|
||||
#! @shell@
|
||||
|
||||
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
|
||||
cd "$WORKING_DIRECTORY";
|
||||
|
||||
if test -z "$1" ; then
|
||||
echo 'nix-reduce-build (paths or Nix expressions) -- (logins at remote computers)' >&2
|
||||
echo As in: >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- user@somewhere.nowhere.example.org >&2
|
||||
exit;
|
||||
fi;
|
||||
|
||||
while ! test "$1" = "--" || test "$1" = "" ; do
|
||||
echo "$1" >> initial; >&2
|
||||
shift;
|
||||
done
|
||||
shift;
|
||||
echo Will work on $(cat initial | wc -l) targets. >&2
|
||||
|
||||
while read ; do
|
||||
case "$REPLY" in
|
||||
${NIX_STORE_PATH:-/nix/store}/*)
|
||||
echo "$REPLY" >> paths; >&2
|
||||
;;
|
||||
*)
|
||||
nix-instantiate "$REPLY" >> paths;
|
||||
;;
|
||||
esac;
|
||||
done < initial;
|
||||
echo Proceeding $(cat paths | wc -l) paths. >&2
|
||||
|
||||
while read; do
|
||||
case "$REPLY" in
|
||||
*.drv)
|
||||
echo "$REPLY" >> derivers; >&2
|
||||
;;
|
||||
*)
|
||||
nix-store --query --deriver "$REPLY" >>derivers;
|
||||
;;
|
||||
esac;
|
||||
done < paths;
|
||||
echo Found $(cat derivers | wc -l) derivers. >&2
|
||||
|
||||
cat derivers | xargs nix-store --query -R > derivers-closure;
|
||||
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
|
||||
|
||||
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
|
||||
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
|
||||
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
|
||||
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We need $(cat needed-paths | wc -l) paths. >&2
|
||||
|
||||
if test -z "$1" ; then
|
||||
cat needed-paths;
|
||||
fi;
|
||||
|
||||
for i in "$@"; do
|
||||
cat needed-paths | while read; do
|
||||
nix-copy-closure --from "$i" --gzip "$REPLY" </dev/null || true;
|
||||
done;
|
||||
mv needed-paths wanted-paths;
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We still need $(cat needed-paths | wc -l) paths. >&2
|
||||
done;
|
||||
|
||||
cd /
|
||||
rm -r "$WORKING_DIRECTORY"
|
||||
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
static off_t offtin(u_char *buf)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "eval.hh"
|
||||
#include "parser.hh"
|
||||
#include "hash.hh"
|
||||
@@ -15,19 +13,6 @@
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
int cacheTerms;
|
||||
|
||||
bool shortCircuit;
|
||||
bool closedTerms; // don't substitute under terms known to be closed
|
||||
bool substCache; // memoization of the term substitution function
|
||||
bool posInfo; // attach position info to functions, assertions, attributes
|
||||
|
||||
#define maxActiveCalls 4096
|
||||
|
||||
ATerm activeCalls[maxActiveCalls];
|
||||
unsigned int activeCallsCount = 0;
|
||||
|
||||
|
||||
EvalState::EvalState()
|
||||
@@ -38,15 +23,6 @@ EvalState::EvalState()
|
||||
initNixExprHelpers();
|
||||
|
||||
addPrimOps();
|
||||
|
||||
if (!string2Int(getEnv("NIX_TERM_CACHE"), cacheTerms)) cacheTerms = 1;
|
||||
shortCircuit = getEnv("NIX_SHORT_CIRCUIT", "0") == "1";
|
||||
strictMode = getEnv("NIX_STRICT", "0") == "1";
|
||||
closedTerms = getEnv("NIX_CLOSED_TERMS", "1") == "1";
|
||||
substCache = getEnv("NIX_SUBST_CACHE", "1") == "1";
|
||||
posInfo = getEnv("NIX_POS_INFO", "1") == "1";
|
||||
|
||||
ATprotectMemory(activeCalls, maxActiveCalls);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,19 +69,6 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
|
||||
}
|
||||
|
||||
|
||||
Expr speculativeEval(EvalState & state, Expr e)
|
||||
{
|
||||
if (!state.strictMode) return e;
|
||||
try {
|
||||
return evalExpr(state, e);
|
||||
} catch (EvalError & err) {
|
||||
/* ignore, pass the original arg and depend on
|
||||
laziness */
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Substitute an argument set into the body of a function. */
|
||||
static Expr substArgs(EvalState & state,
|
||||
Expr body, ATermList formals, Expr arg)
|
||||
@@ -117,7 +80,7 @@ static Expr substArgs(EvalState & state,
|
||||
ATermMap args;
|
||||
queryAllAttrs(arg, args);
|
||||
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
|
||||
subs.set(i->key, speculativeEval(state, i->value));
|
||||
subs.set(i->key, i->value);
|
||||
|
||||
/* Get the formal arguments. */
|
||||
ATermVector defsUsed;
|
||||
@@ -426,7 +389,7 @@ Expr autoCallFunction(Expr e, const ATermMap & args)
|
||||
Expr name, def, value; ATerm values, def2;
|
||||
if (!matchFormal(*i, name, values, def2)) abort();
|
||||
if ((value = args.get(name)))
|
||||
actualArgs.set(name, makeAttrRHS(allocCell(value), makeNoPos()));
|
||||
actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
|
||||
else if (!matchDefaultValue(def2, def))
|
||||
throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
|
||||
% aterm2String(name));
|
||||
@@ -494,7 +457,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
||||
else if (matchFunction(fun, formals, body, pos)) {
|
||||
arg = evalExpr(state, arg);
|
||||
try {
|
||||
return evalExpr(state, substArgs(state, allocCells(body), formals, arg));
|
||||
return evalExpr(state, substArgs(state, body, formals, arg));
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||
showPos(pos));
|
||||
@@ -504,10 +467,9 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
||||
|
||||
else if (matchFunction1(fun, name, body, pos)) {
|
||||
try {
|
||||
arg = speculativeEval(state, arg);
|
||||
ATermMap subs(1);
|
||||
subs.set(name, allocCell(arg));
|
||||
return evalExpr(state, substitute(Substitution(0, &subs), allocCells(body)));
|
||||
subs.set(name, arg);
|
||||
return evalExpr(state, substitute(Substitution(0, &subs), body));
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||
showPos(pos));
|
||||
@@ -662,10 +624,6 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||
|
||||
Expr e1, e2, e3;
|
||||
ATerm name, pos;
|
||||
|
||||
int bla;
|
||||
if (matchCell(e, bla, e1)) e = e1;
|
||||
|
||||
AFun sym = ATgetAFun(e);
|
||||
|
||||
/* Normal forms. */
|
||||
@@ -757,141 +715,41 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
|
||||
|
||||
/* Barf. */
|
||||
//printMsg(lvlError, format("%1%") % e);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
class ShortCircuit
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
unsigned int fnord;
|
||||
|
||||
|
||||
void maybeShortCircuit(EvalState & state, Expr e, Expr nf)
|
||||
{
|
||||
for (unsigned int i = 0; i < activeCallsCount; ++i) {
|
||||
Expr fun, arg;
|
||||
if (!matchCall(activeCalls[i], fun, arg)) abort();
|
||||
if (arg == e) {
|
||||
//printMsg(lvlError, format("blaat"));
|
||||
//printMsg(lvlError, format("blaat %1% %2% %3%") % fun % arg % e);
|
||||
Expr res = state.normalForms.get(makeCall(fun, nf));
|
||||
if (res) {
|
||||
fnord++;
|
||||
//printMsg(lvlError, format("blaat"));
|
||||
throw ShortCircuit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Expr evalExpr(EvalState & state, Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
startNest(nest, lvlVomit,
|
||||
format("evaluating expression: %1%") % e);
|
||||
#endif
|
||||
|
||||
state.nrEvaluated++;
|
||||
|
||||
if (cacheTerms == 0) return evalExpr2(state, e);
|
||||
|
||||
if (cacheTerms == 2) {
|
||||
int pseudoAddr;
|
||||
Expr e2;
|
||||
if (!matchCell(e, pseudoAddr, e2)) return evalExpr2(state, e);
|
||||
}
|
||||
|
||||
/* Consult the memo table to quickly get the normal form of
|
||||
previously evaluated expressions. */
|
||||
Expr nf = state.normalForms.get(e);
|
||||
if (nf) {
|
||||
//if (nf == makeBlackHole())
|
||||
// throwEvalError("infinite recursion encountered");
|
||||
if (nf == makeBlackHole())
|
||||
throwEvalError("infinite recursion encountered");
|
||||
state.nrCached++;
|
||||
return nf;
|
||||
}
|
||||
|
||||
Expr fun, arg;
|
||||
if (shortCircuit && matchCall(e, fun, arg)) {
|
||||
|
||||
#if 0
|
||||
Expr arg2 = state.normalForms.get(arg);
|
||||
if (arg2) { /* the evaluated argument is now known */
|
||||
//printMsg(lvlError, "foo");
|
||||
/* do we know the result of the same function called
|
||||
with the evaluated argument? */
|
||||
Expr res = state.normalForms.get(makeCall(fun, arg2));
|
||||
if (res) { /* woohoo! */
|
||||
printMsg(lvlError, "dingdong");
|
||||
state.normalForms.set(e, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(activeCallsCount < maxActiveCalls);
|
||||
activeCalls[activeCallsCount++] = e;
|
||||
|
||||
//state.normalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
}
|
||||
catch (ShortCircuit & exception) {
|
||||
//printMsg(lvlError, "catch!");
|
||||
Expr arg2 = state.normalForms.get(arg);
|
||||
if (arg2) { /* the evaluated argument is now known */
|
||||
/* do we know the result of the same function called
|
||||
with the evaluated argument? */
|
||||
Expr res = state.normalForms.get(makeCall(fun, arg2));
|
||||
if (res) { /* woohoo! */
|
||||
//printMsg(lvlError, "woohoo!");
|
||||
//printMsg(lvlError, format("woohoo! %1% %2% %3% %4%") % fun % arg % arg2 % res);
|
||||
activeCallsCount--;
|
||||
state.normalForms.set(e, res);
|
||||
maybeShortCircuit(state, e, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
activeCallsCount--;
|
||||
state.normalForms.remove(e);
|
||||
throw; /* not for us */
|
||||
}
|
||||
catch (...) {
|
||||
activeCallsCount--;
|
||||
state.normalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
activeCallsCount--;
|
||||
state.normalForms.set(e, nf);
|
||||
Expr arg2 = state.normalForms.get(arg);
|
||||
if (arg2) state.normalForms.set(makeCall(fun, arg2), nf);
|
||||
maybeShortCircuit(state, e, nf);
|
||||
return nf;
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
//state.normalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
} catch (...) {
|
||||
state.normalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
state.normalForms.set(e, nf);
|
||||
if (shortCircuit) maybeShortCircuit(state, e, nf);
|
||||
return nf;
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
state.normalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
} catch (Error & err) {
|
||||
state.normalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
state.normalForms.set(e, nf);
|
||||
return nf;
|
||||
}
|
||||
|
||||
|
||||
@@ -987,24 +845,16 @@ extern "C" {
|
||||
unsigned long AT_calcAllocatedSize();
|
||||
}
|
||||
|
||||
|
||||
unsigned int substs = 0;
|
||||
unsigned int substsCached = 0;
|
||||
|
||||
|
||||
void printEvalStats(EvalState & state)
|
||||
{
|
||||
char x;
|
||||
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
||||
printMsg(lvlError, format("FNORD %1%") % fnord);
|
||||
printMsg(showStats ? lvlInfo : lvlDebug,
|
||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space, %6% substitutions (%7% cached)")
|
||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
|
||||
% state.nrEvaluated % state.nrCached
|
||||
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
|
||||
% AT_calcAllocatedSize()
|
||||
% (&x - deepestStack)
|
||||
% substs
|
||||
% substsCached);
|
||||
% (&x - deepestStack));
|
||||
if (showStats)
|
||||
printATermMapStats();
|
||||
}
|
||||
|
||||
@@ -27,9 +27,6 @@ struct EvalState;
|
||||
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
||||
|
||||
|
||||
extern int cacheTerms; // 0 = don't, 1 = do, 2 = "cell" terms only
|
||||
|
||||
|
||||
struct EvalState
|
||||
{
|
||||
ATermMap normalForms;
|
||||
@@ -41,10 +38,6 @@ struct EvalState
|
||||
unsigned int nrEvaluated;
|
||||
unsigned int nrCached;
|
||||
|
||||
bool strictMode;
|
||||
|
||||
ATermMap parsings; /* path -> expr mapping */
|
||||
|
||||
EvalState();
|
||||
|
||||
void addPrimOps();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
|
||||
%x STRING
|
||||
%x IND_STRING
|
||||
|
||||
|
||||
%{
|
||||
@@ -122,6 +123,30 @@ inherit { return INHERIT; }
|
||||
<STRING>\" { BEGIN(INITIAL); return '"'; }
|
||||
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
|
||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||
yylval->t = makeIndStr(toATerm(yytext));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\$ {
|
||||
yylval->t = makeIndStr(toATerm("$"));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\' {
|
||||
yylval->t = makeIndStr(toATerm("''"));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\\. {
|
||||
yylval->t = unescapeStr(yytext + 2);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
||||
<IND_STRING>\' {
|
||||
yylval->t = makeIndStr(toATerm("'"));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
|
||||
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
|
||||
@@ -148,4 +173,10 @@ void backToString(yyscan_t scanner)
|
||||
BEGIN(STRING);
|
||||
}
|
||||
|
||||
void backToIndString(yyscan_t scanner)
|
||||
{
|
||||
struct yyguts_t * yyg = (struct yyguts_t *) scanner;
|
||||
BEGIN(IND_STRING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ Int | int | Expr |
|
||||
Str | string ATermList | Expr |
|
||||
Str | string | Expr | ObsoleteStr
|
||||
|
||||
# Internal to the parser, doesn't occur in ASTs.
|
||||
IndStr | string | Expr |
|
||||
|
||||
# A path is a reference to a file system object that is to be copied
|
||||
# to the Nix store when used as a derivation attribute. When it is
|
||||
# concatenated to a string (i.e., `str + path'), it is also copied and
|
||||
@@ -73,8 +76,6 @@ Inherit | Expr ATermList Pos | ATerm |
|
||||
|
||||
Scope | | Expr |
|
||||
|
||||
Cell | int Expr | Expr |
|
||||
|
||||
Formal | string ValidValues DefaultValue | ATerm |
|
||||
|
||||
ValidValues | ATermList | ValidValues |
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "derivations.hh"
|
||||
#include "util.hh"
|
||||
#include "aterm.hh"
|
||||
#include "eval.hh" // !!! urgh
|
||||
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "nixexpr-ast.cc"
|
||||
@@ -109,16 +108,7 @@ Expr makeAttrs(const ATermMap & attrs)
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int substs;
|
||||
extern unsigned int substsCached;
|
||||
extern bool closedTerms;
|
||||
extern bool substCache;
|
||||
|
||||
|
||||
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e);
|
||||
|
||||
|
||||
static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
Expr substitute(const Substitution & subs, Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
@@ -126,20 +116,19 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
|
||||
ATerm name, pos, e2;
|
||||
|
||||
substs++;
|
||||
|
||||
/* As an optimisation, don't substitute in subterms known to be
|
||||
closed. */
|
||||
if (closedTerms && matchClosed(e, e2)) return e;
|
||||
if (matchClosed(e, e2)) return e;
|
||||
|
||||
if (matchVar(e, name)) {
|
||||
Expr sub = subs.lookup(name);
|
||||
if (sub == makeRemoved()) sub = 0;
|
||||
Expr wrapped;
|
||||
/* Add a "closed" wrapper around terms that aren't already
|
||||
closed. The check is necessary to prevent repeated
|
||||
wrapping, e.g., closed(closed(closed(...))), which kills
|
||||
caching. */
|
||||
return sub ? ((!closedTerms || matchClosed(sub, wrapped)) ? sub : makeClosed(sub)) : e;
|
||||
return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
|
||||
}
|
||||
|
||||
/* In case of a function, filter out all variables bound by this
|
||||
@@ -151,30 +140,18 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm d1, d2;
|
||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||
if (subs.lookup(name))
|
||||
map.set(name, constRemoved);
|
||||
}
|
||||
if (map.size() == 0)
|
||||
return makeFunction(
|
||||
(ATermList) substitute(done, subs, (ATerm) formals),
|
||||
substitute(done, subs, body), pos);
|
||||
else {
|
||||
Substitution subs2(&subs, &map);
|
||||
ATermMap done2(128);
|
||||
return makeFunction(
|
||||
(ATermList) substitute(done2, subs2, (ATerm) formals),
|
||||
substitute(done2, subs2, body), pos);
|
||||
map.set(name, makeRemoved());
|
||||
}
|
||||
Substitution subs2(&subs, &map);
|
||||
return makeFunction(
|
||||
(ATermList) substitute(subs2, (ATerm) formals),
|
||||
substitute(subs2, body), pos);
|
||||
}
|
||||
|
||||
if (matchFunction1(e, name, body, pos)) {
|
||||
if (subs.lookup(name)) {
|
||||
ATermMap map(1);
|
||||
map.set(name, constRemoved);
|
||||
ATermMap done2(128);
|
||||
return makeFunction1(name, substitute(done2, Substitution(&subs, &map), body), pos);
|
||||
} else
|
||||
return makeFunction1(name, substitute(done, subs, body), pos);
|
||||
ATermMap map(1);
|
||||
map.set(name, makeRemoved());
|
||||
return makeFunction1(name, substitute(Substitution(&subs, &map), body), pos);
|
||||
}
|
||||
|
||||
/* Idem for a mutually recursive attribute set. */
|
||||
@@ -182,21 +159,14 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
if (matchRec(e, rbnds, nrbnds)) {
|
||||
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
||||
for (ATermIterator i(rbnds); i; ++i)
|
||||
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
|
||||
map.set(name, constRemoved);
|
||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
||||
else abort(); /* can't happen */
|
||||
for (ATermIterator i(nrbnds); i; ++i)
|
||||
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
|
||||
map.set(name, constRemoved);
|
||||
if (map.size() == 0)
|
||||
return makeRec(
|
||||
(ATermList) substitute(done, subs, (ATerm) rbnds),
|
||||
(ATermList) substitute(done, subs, (ATerm) nrbnds));
|
||||
else {
|
||||
ATermMap done2(128);
|
||||
return makeRec(
|
||||
(ATermList) substitute(done2, Substitution(&subs, &map), (ATerm) rbnds),
|
||||
(ATermList) substitute(done, subs, (ATerm) nrbnds));
|
||||
}
|
||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
||||
else abort(); /* can't happen */
|
||||
return makeRec(
|
||||
(ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
|
||||
(ATermList) substitute(subs, (ATerm) nrbnds));
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
@@ -207,73 +177,7 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
|
||||
for (int i = 0; i < arity; ++i) {
|
||||
ATerm arg = ATgetArgument(e, i);
|
||||
args[i] = substitute(done, subs, arg);
|
||||
if (args[i] != arg) changed = true;
|
||||
}
|
||||
|
||||
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_LIST) {
|
||||
unsigned int len = ATgetLength((ATermList) e);
|
||||
ATerm es[len];
|
||||
ATermIterator i((ATermList) e);
|
||||
bool changed = false;
|
||||
for (unsigned int j = 0; i; ++i, ++j) {
|
||||
es[j] = substitute(done, subs, *i);
|
||||
if (es[j] != *i) changed = true;
|
||||
}
|
||||
if (!changed) return e;
|
||||
ATermList out = ATempty;
|
||||
for (unsigned int j = len; j; --j)
|
||||
out = ATinsert(out, es[j - 1]);
|
||||
return (ATerm) out;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e)
|
||||
{
|
||||
Expr res = done[e];
|
||||
if (substCache && res) {
|
||||
substsCached++;
|
||||
return res;
|
||||
}
|
||||
res = substitute2(done, subs, e);
|
||||
done.set(e, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
Expr substitute(const Substitution & subs, Expr e)
|
||||
{
|
||||
ATermMap done(256);
|
||||
return substitute(done, subs, e);
|
||||
}
|
||||
|
||||
|
||||
Expr allocCells(Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
ATerm e2;
|
||||
if (matchClosed(e, e2)) return e;
|
||||
|
||||
int i;
|
||||
if (matchCell(e, i, e2))
|
||||
return allocCell(allocCells(e2));
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
AFun fun = ATgetAFun(e);
|
||||
int arity = ATgetArity(fun);
|
||||
ATerm args[arity];
|
||||
bool changed = false;
|
||||
|
||||
for (int i = 0; i < arity; ++i) {
|
||||
ATerm arg = ATgetArgument(e, i);
|
||||
args[i] = allocCells(arg);
|
||||
args[i] = substitute(subs, arg);
|
||||
if (args[i] != arg) changed = true;
|
||||
}
|
||||
|
||||
@@ -285,7 +189,7 @@ Expr allocCells(Expr e)
|
||||
ATerm es[len];
|
||||
ATermIterator i((ATermList) e);
|
||||
for (unsigned int j = 0; i; ++i, ++j)
|
||||
es[j] = allocCells(*i);
|
||||
es[j] = substitute(subs, *i);
|
||||
ATermList out = ATempty;
|
||||
for (unsigned int j = len; j; --j)
|
||||
out = ATinsert(out, es[j - 1]);
|
||||
@@ -311,7 +215,11 @@ static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
|
||||
ATerm with, body;
|
||||
ATermList rbnds, nrbnds;
|
||||
|
||||
if (matchVar(e, name)) {
|
||||
/* Closed terms don't have free variables, so we don't have to
|
||||
check by definition. */
|
||||
if (matchClosed(e, value)) return;
|
||||
|
||||
else if (matchVar(e, name)) {
|
||||
if (!defs.get(name))
|
||||
throw EvalError(format("undefined variable `%1%'")
|
||||
% aterm2String(name));
|
||||
@@ -495,18 +403,5 @@ string showValue(Expr e)
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
|
||||
static unsigned int cellCount = 0;
|
||||
|
||||
|
||||
Expr allocCell(Expr e)
|
||||
{
|
||||
if (cacheTerms != 2) return e;
|
||||
int i;
|
||||
Expr e2;
|
||||
if (matchCell(e, i, e2)) return e;
|
||||
return makeCell(cellCount++, e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,6 @@ typedef ATerm Pos;
|
||||
typedef vector<ATerm> ATermVector;
|
||||
|
||||
|
||||
extern Expr constRemoved;
|
||||
|
||||
|
||||
/* A substitution is a linked list of ATermMaps that map names to
|
||||
identifiers. We use a list of ATermMaps rather than a single to
|
||||
make it easy to grow or shrink a substitution when entering a
|
||||
@@ -56,8 +53,7 @@ struct Substitution
|
||||
{
|
||||
Expr x;
|
||||
for (const Substitution * s(this); s; s = s->prev)
|
||||
if ((x = s->map->get(name)))
|
||||
return x == constRemoved ? 0 : x;
|
||||
if ((x = s->map->get(name))) return x;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -120,11 +116,6 @@ string showType(Expr e);
|
||||
|
||||
string showValue(Expr e);
|
||||
|
||||
|
||||
Expr allocCell(Expr e); // make an updateable cell (for simulating conventional laziness)
|
||||
|
||||
Expr allocCells(Expr e); // re-allocate all cells in e
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ static Expr fixAttrs(int recursive, ATermList as)
|
||||
bool fromScope = matchScope(src);
|
||||
for (ATermIterator j(names); j; ++j) {
|
||||
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
||||
*is = ATinsert(*is, makeBind(*j, allocCell(rhs), pos));
|
||||
*is = ATinsert(*is, makeBind(*j, rhs, pos));
|
||||
}
|
||||
} else bs = ATinsert(bs, *i);
|
||||
}
|
||||
@@ -68,15 +68,104 @@ static Expr fixAttrs(int recursive, ATermList as)
|
||||
}
|
||||
|
||||
|
||||
static Expr stripIndentation(ATermList es)
|
||||
{
|
||||
if (es == ATempty) return makeStr("");
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
|
||||
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||
unsigned int minIndent = 1000000;
|
||||
unsigned int curIndent = 0;
|
||||
ATerm e;
|
||||
for (ATermIterator i(es); i; ++i) {
|
||||
if (!matchIndStr(*i, e)) {
|
||||
/* Anti-quotations end the current start-of-line whitespace. */
|
||||
if (atStartOfLine) {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
string s = aterm2String(e);
|
||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (s[j] == ' ')
|
||||
curIndent++;
|
||||
else if (s[j] == '\n') {
|
||||
/* Empty line, doesn't influence minimum
|
||||
indentation. */
|
||||
curIndent = 0;
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
}
|
||||
} else if (s[j] == '\n') {
|
||||
atStartOfLine = true;
|
||||
curIndent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip spaces from each line. */
|
||||
ATermList es2 = ATempty;
|
||||
atStartOfLine = true;
|
||||
unsigned int curDropped = 0;
|
||||
unsigned int n = ATgetLength(es);
|
||||
for (ATermIterator i(es); i; ++i, --n) {
|
||||
if (!matchIndStr(*i, e)) {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
es2 = ATinsert(es2, *i);
|
||||
continue;
|
||||
}
|
||||
|
||||
string s = aterm2String(e);
|
||||
string s2;
|
||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (s[j] == ' ') {
|
||||
if (curDropped++ >= minIndent)
|
||||
s2 += s[j];
|
||||
}
|
||||
else if (s[j] == '\n') {
|
||||
curDropped = 0;
|
||||
s2 += s[j];
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
s2 += s[j];
|
||||
}
|
||||
} else {
|
||||
s2 += s[j];
|
||||
if (s[j] == '\n') atStartOfLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the last line if it is empty and consists only of
|
||||
spaces. */
|
||||
if (n == 1) {
|
||||
unsigned int p = s2.find_last_of('\n');
|
||||
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
|
||||
s2 = string(s2, 0, p + 1);
|
||||
}
|
||||
|
||||
es2 = ATinsert(es2, makeStr(s2));
|
||||
}
|
||||
|
||||
return makeConcatStrings(ATreverse(es2));
|
||||
}
|
||||
|
||||
|
||||
void backToString(yyscan_t scanner);
|
||||
void backToIndString(yyscan_t scanner);
|
||||
|
||||
|
||||
extern bool posInfo;
|
||||
|
||||
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
||||
{
|
||||
return posInfo ? makePos(toATerm(data->path),
|
||||
loc->first_line, loc->first_column) : makeNoPos();
|
||||
return makePos(toATerm(data->path),
|
||||
loc->first_line, loc->first_column);
|
||||
}
|
||||
|
||||
#define CUR_POS makeCurPos(yylocp, data)
|
||||
@@ -123,10 +212,11 @@ static void freeAndUnprotect(void * p)
|
||||
|
||||
%type <t> start expr expr_function expr_if expr_op
|
||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
|
||||
%type <ts> binds ids expr_list formals string_parts
|
||||
%token <t> ID INT STR PATH URI
|
||||
%type <ts> binds ids expr_list formals string_parts ind_string_parts
|
||||
%token <t> ID INT STR IND_STR PATH URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||
|
||||
%nonassoc IMPL
|
||||
%left OR
|
||||
@@ -201,6 +291,9 @@ expr_simple
|
||||
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
|
||||
else $$ = makeConcatStrings(ATreverse($2));
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(ATreverse($2));
|
||||
}
|
||||
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
|
||||
| URI { $$ = makeStr($1, ATempty); }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
@@ -221,6 +314,12 @@ string_parts
|
||||
| { $$ = ATempty; }
|
||||
;
|
||||
|
||||
ind_string_parts
|
||||
: ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
|
||||
| { $$ = ATempty; }
|
||||
;
|
||||
|
||||
binds
|
||||
: binds bind { $$ = ATinsert($1, $2); }
|
||||
| { $$ = ATempty; }
|
||||
@@ -228,7 +327,7 @@ binds
|
||||
|
||||
bind
|
||||
: ID '=' expr ';'
|
||||
{ $$ = makeBind($1, allocCell($3), CUR_POS); }
|
||||
{ $$ = makeBind($1, $3, CUR_POS); }
|
||||
| INHERIT inheritsrc ids ';'
|
||||
{ $$ = makeInherit($2, $3, CUR_POS); }
|
||||
;
|
||||
@@ -385,13 +484,8 @@ Expr parseExprFromFile(EvalState & state, Path path)
|
||||
if (S_ISDIR(st.st_mode))
|
||||
path = canonPath(path + "/default.nix");
|
||||
|
||||
Expr cached = state.parsings.get(toATerm(path));
|
||||
if (cached) return cached;
|
||||
|
||||
/* Read and parse the input file. */
|
||||
cached = parse(state, readFile(path).c_str(), path, dirOf(path));
|
||||
state.parsings.set(toATerm(path), cached);
|
||||
return cached;
|
||||
return parse(state, readFile(path).c_str(), path, dirOf(path));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "archive.hh"
|
||||
#include "expr-to-xml.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "parser.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -296,18 +297,19 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args)
|
||||
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)
|
||||
{
|
||||
//string str = evalStringNoCtx(state, args[0]);
|
||||
|
||||
Expr a = evalExpr(state, args[0]);
|
||||
printf("traced value: %s\n", atPrint(a).c_str());
|
||||
Expr e = evalExpr(state, args[0]);
|
||||
printMsg(lvlError, format("trace: %1%") % e);
|
||||
return evalExpr(state, args[1]);
|
||||
}
|
||||
|
||||
|
||||
static Expr prim_relativise(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context; /* !!! what to do? */
|
||||
@@ -564,7 +566,7 @@ static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
|
||||
attrs.set(toATerm("type"),
|
||||
makeAttrRHS(makeStr("derivation"), makeNoPos()));
|
||||
|
||||
Expr drvStrict = allocCell(makeCall(makeVar(toATerm("derivation!")), eAttrs));
|
||||
Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
|
||||
|
||||
attrs.set(toATerm("outPath"),
|
||||
makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
|
||||
@@ -621,6 +623,17 @@ static Expr prim_dirOf(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
|
||||
|
||||
/* Return the contents of a file as a string. */
|
||||
static Expr prim_readFile(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context;
|
||||
Path path = coerceToPath(state, args[0], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
|
||||
return makeStr(readFile(path));
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Creating files
|
||||
*************************************************************/
|
||||
@@ -773,7 +786,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
|
||||
Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
|
||||
string attr = evalStringNoCtx(state,e);
|
||||
Expr r = makeSelect(evaledExpr, toATerm("value"));
|
||||
res.set(toATerm(attr), makeAttrRHS(allocCell(r), makeNoPos()));
|
||||
res.set(toATerm(attr), makeAttrRHS(r, makeNoPos()));
|
||||
}
|
||||
else
|
||||
throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
|
||||
@@ -788,6 +801,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
ATermMap attrs;
|
||||
@@ -802,6 +816,7 @@ static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
|
||||
return makeAttrs(attrs);
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is a list. */
|
||||
static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
@@ -809,6 +824,7 @@ static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
|
||||
return makeBool(matchAttrs(evalExpr(state, args[0]), list));
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Lists
|
||||
*************************************************************/
|
||||
@@ -902,8 +918,8 @@ static Expr prim_toString(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
|
||||
|
||||
/* `substr start len str' returns the substring of `str' starting at
|
||||
character position `min(start, stringLength str)' inclusive and
|
||||
/* `substring start len str' returns the substring of `str' starting
|
||||
at character position `min(start, stringLength str)' inclusive and
|
||||
ending at `min(start + len, stringLength str)'. `start' must be
|
||||
non-negative. */
|
||||
static Expr prim_substring(EvalState & state, const ATermVector & args)
|
||||
@@ -927,16 +943,29 @@ static Expr prim_stringLength(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Strictness
|
||||
*************************************************************/
|
||||
|
||||
|
||||
static Expr prim_strict(EvalState & state, const ATermVector & args)
|
||||
static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
return evalExpr(state, makeCall(args[0], evalExpr(state, args[1])));
|
||||
PathSet context;
|
||||
string s = coerceToString(state, args[0], context);
|
||||
return makeStr(s, PathSet());
|
||||
}
|
||||
|
||||
/* Expression serialization/deserialization */
|
||||
|
||||
static Expr prim_ExprToString ( EvalState & state, const ATermVector & args)
|
||||
{
|
||||
return makeStr ( atPrint ( evalExpr ( state, args [ 0 ] ) ) );
|
||||
}
|
||||
|
||||
static Expr prim_StringToExpr ( EvalState & state, const ATermVector & args)
|
||||
{
|
||||
string s;
|
||||
PathSet l;
|
||||
if (! matchStr ( evalExpr ( state, args[0] ), s, l )) {
|
||||
throw EvalError("__stringToExpr needs string argument!");
|
||||
}
|
||||
return ATreadFromString(s.c_str());
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Primop registration
|
||||
@@ -963,6 +992,10 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("throw", 1, prim_throw);
|
||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||
addPrimOp("__trace", 2, prim_trace);
|
||||
|
||||
// Expr <-> String
|
||||
addPrimOp("__exprToString", 1, prim_ExprToString);
|
||||
addPrimOp("__stringToExpr", 1, prim_StringToExpr);
|
||||
|
||||
addPrimOp("relativise", 2, prim_relativise);
|
||||
|
||||
@@ -975,6 +1008,7 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("__pathExists", 1, prim_pathExists);
|
||||
addPrimOp("baseNameOf", 1, prim_baseNameOf);
|
||||
addPrimOp("dirOf", 1, prim_dirOf);
|
||||
addPrimOp("__readFile", 1, prim_readFile);
|
||||
|
||||
// Creating files
|
||||
addPrimOp("__toXML", 1, prim_toXML);
|
||||
@@ -1004,9 +1038,8 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("toString", 1, prim_toString);
|
||||
addPrimOp("__substring", 3, prim_substring);
|
||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||
|
||||
// Strictness
|
||||
addPrimOp("strict", 2, prim_strict);
|
||||
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -212,6 +212,8 @@ static void initAndRun(int argc, char * * argv)
|
||||
readOnlyMode = true;
|
||||
else if (arg == "--max-silent-time")
|
||||
maxSilentTime = getIntArg(arg, i, args.end());
|
||||
else if (arg == "--no-build-hook")
|
||||
useBuildHook = false;
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
pkglib_LTLIBRARIES = libstore.la
|
||||
|
||||
libstore_la_SOURCES = \
|
||||
store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \
|
||||
globals.cc db.cc references.cc pathlocks.cc gc.cc
|
||||
store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \
|
||||
globals.cc db.cc references.cc pathlocks.cc gc.cc upgrade-schema.cc \
|
||||
optimise-store.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \
|
||||
globals.hh db.hh references.hh pathlocks.hh \
|
||||
worker-protocol.hh
|
||||
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \
|
||||
globals.hh db.hh references.hh pathlocks.hh \
|
||||
worker-protocol.hh
|
||||
|
||||
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "misc.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <map>
|
||||
@@ -25,6 +24,19 @@
|
||||
#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 {
|
||||
|
||||
using std::map;
|
||||
@@ -87,6 +99,7 @@ protected:
|
||||
{
|
||||
nrFailed = 0;
|
||||
exitCode = ecBusy;
|
||||
forceInputs = false;
|
||||
}
|
||||
|
||||
virtual ~Goal()
|
||||
@@ -94,6 +107,8 @@ protected:
|
||||
trace("goal destroyed");
|
||||
}
|
||||
|
||||
bool forceInputs;
|
||||
|
||||
public:
|
||||
virtual void work() = 0;
|
||||
|
||||
@@ -128,6 +143,11 @@ public:
|
||||
(important!), etc. */
|
||||
virtual void cancel() = 0;
|
||||
|
||||
void setForceInputs(bool x)
|
||||
{
|
||||
forceInputs = x;
|
||||
}
|
||||
|
||||
protected:
|
||||
void amDone(ExitCode result);
|
||||
};
|
||||
@@ -186,7 +206,9 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
Worker();
|
||||
LocalStore & store;
|
||||
|
||||
Worker(LocalStore & store);
|
||||
~Worker();
|
||||
|
||||
/* Make a goal (with caching). */
|
||||
@@ -583,6 +605,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
|
||||
{
|
||||
private:
|
||||
@@ -623,6 +728,17 @@ private:
|
||||
Pipe toHook;
|
||||
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)();
|
||||
GoalState state;
|
||||
|
||||
@@ -638,7 +754,7 @@ public:
|
||||
{
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/* The states. */
|
||||
void init();
|
||||
@@ -678,7 +794,7 @@ private:
|
||||
void openLogFile();
|
||||
|
||||
/* 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();
|
||||
|
||||
/* Delete the temporary directory, if we have one. */
|
||||
@@ -705,7 +821,6 @@ DerivationGoal::DerivationGoal(const Path & drvPath, Worker & worker)
|
||||
trace("created");
|
||||
}
|
||||
|
||||
|
||||
DerivationGoal::~DerivationGoal()
|
||||
{
|
||||
/* Careful: we should never ever throw an exception from a
|
||||
@@ -718,7 +833,6 @@ DerivationGoal::~DerivationGoal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DerivationGoal::killChild()
|
||||
{
|
||||
if (pid != -1) {
|
||||
@@ -784,22 +898,24 @@ void DerivationGoal::haveDerivation()
|
||||
return;
|
||||
}
|
||||
|
||||
assert(store->isValidPath(drvPath));
|
||||
assert(worker.store.isValidPath(drvPath));
|
||||
|
||||
/* Get the derivation. */
|
||||
drv = derivationFromPath(drvPath);
|
||||
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
store->addTempRoot(i->second.path);
|
||||
worker.store.addTempRoot(i->second.path);
|
||||
|
||||
/* Check what outputs paths are not already valid. */
|
||||
PathSet invalidOutputs = checkPathValidity(false);
|
||||
|
||||
/* If they are all valid, then we're done. */
|
||||
if (invalidOutputs.size() == 0) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
if(! forceInputs) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a fixed-output derivation, it is possible that some
|
||||
@@ -823,7 +939,7 @@ void DerivationGoal::haveDerivation()
|
||||
i != invalidOutputs.end(); ++i)
|
||||
/* Don't bother creating a substitution goal if there are no
|
||||
substitutes. */
|
||||
if (store->hasSubstitutes(*i))
|
||||
if (worker.store.hasSubstitutes(*i))
|
||||
addWaitee(worker.makeSubstitutionGoal(*i));
|
||||
|
||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||
@@ -843,8 +959,10 @@ void DerivationGoal::outputsSubstituted()
|
||||
nrFailed = 0;
|
||||
|
||||
if (checkPathValidity(false).size() == 0) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
if (! forceInputs){
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, at least one of the output paths could not be
|
||||
@@ -853,13 +971,43 @@ void DerivationGoal::outputsSubstituted()
|
||||
/* The inputs must be built before we can build this goal. */
|
||||
/* !!! but if possible, only install the paths that we need */
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
addWaitee(worker.makeDerivationGoal(i->first));
|
||||
i != drv.inputDrvs.end(); ++i){
|
||||
GoalPtr newGoal = worker.makeDerivationGoal(i->first);
|
||||
newGoal->setForceInputs(forceInputs);
|
||||
addWaitee(newGoal);
|
||||
}
|
||||
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin();
|
||||
i != drv.inputSrcs.end(); ++i)
|
||||
addWaitee(worker.makeSubstitutionGoal(*i));
|
||||
|
||||
/* Actually, I do some work twice just to be on the safe side */
|
||||
string s = drv.env["exportBuildReferencesGraph"];
|
||||
Strings ss = tokenizeString(s);
|
||||
if (ss.size() % 2 !=0)
|
||||
throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
|
||||
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
|
||||
string fileName = *i++;
|
||||
Path storePath=*i++;
|
||||
|
||||
if (!isInStore(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
|
||||
% storePath);
|
||||
storePath = toStorePath(storePath);
|
||||
if (!worker.store.isValidPath(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
|
||||
% storePath);
|
||||
|
||||
/* Build-time closure should be in dependencies
|
||||
* We really want just derivation, its closure
|
||||
* and outputs. Looks like we should build it.
|
||||
* */
|
||||
|
||||
GoalPtr newGoal = worker.makeDerivationGoal(storePath);
|
||||
newGoal->setForceInputs(true);
|
||||
addWaitee(newGoal);
|
||||
}
|
||||
|
||||
state = &DerivationGoal::inputsRealised;
|
||||
}
|
||||
|
||||
@@ -877,6 +1025,12 @@ void DerivationGoal::inputsRealised()
|
||||
return;
|
||||
}
|
||||
|
||||
/* Maybe we just wanted to force build of inputs */
|
||||
if (checkPathValidity(false).size() == 0) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Okay, try to build. Note that here we don't wait for a build
|
||||
slot to become available, since we don't need one if there is a
|
||||
build hook. */
|
||||
@@ -1024,6 +1178,9 @@ void DerivationGoal::buildDone()
|
||||
|
||||
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
|
||||
being valid. */
|
||||
computeClosure();
|
||||
@@ -1094,49 +1251,9 @@ PathSet outputPaths(const DerivationOutputs & outputs)
|
||||
}
|
||||
|
||||
|
||||
string showPaths(const PathSet & paths)
|
||||
{
|
||||
string s;
|
||||
for (PathSet::const_iterator i = paths.begin();
|
||||
i != paths.end(); ++i)
|
||||
{
|
||||
if (s.size() != 0) s += ", ";
|
||||
s += "`" + *i + "'";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/* Return a string accepted by `nix-store --register-validity' that
|
||||
registers the specified paths as valid. Note: it's the
|
||||
responsibility of the caller to provide a closure. */
|
||||
static string makeValidityRegistration(const PathSet & paths,
|
||||
bool showDerivers)
|
||||
{
|
||||
string s = "";
|
||||
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
s += *i + "\n";
|
||||
|
||||
Path deriver = showDerivers ? store->queryDeriver(*i) : "";
|
||||
s += deriver + "\n";
|
||||
|
||||
PathSet references;
|
||||
store->queryReferences(*i, references);
|
||||
|
||||
s += (format("%1%\n") % references.size()).str();
|
||||
|
||||
for (PathSet::iterator j = references.begin();
|
||||
j != references.end(); ++j)
|
||||
s += *j + "\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
{
|
||||
if (!useBuildHook) return rpDecline;
|
||||
Path buildHook = getEnv("NIX_BUILD_HOOK");
|
||||
if (buildHook == "") return rpDecline;
|
||||
buildHook = absPath(buildHook);
|
||||
@@ -1173,7 +1290,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
throw SysError(format("executing `%1%'") % buildHook);
|
||||
|
||||
} 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);
|
||||
}
|
||||
@@ -1260,7 +1377,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
/* The `references' file has exactly the format accepted by
|
||||
`nix-store --register-validity'. */
|
||||
writeStringToFile(referencesFN,
|
||||
makeValidityRegistration(allInputs, true));
|
||||
makeValidityRegistration(allInputs, true, false));
|
||||
|
||||
/* Tell the hook to proceed. */
|
||||
writeLine(toHook.writeSide, "okay");
|
||||
@@ -1345,7 +1462,7 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild()
|
||||
i != drv.outputs.end(); ++i)
|
||||
{
|
||||
Path path = i->second.path;
|
||||
if (store->isValidPath(path))
|
||||
if (worker.store.isValidPath(path))
|
||||
throw BuildError(format("obstructed build: path `%1%' exists") % path);
|
||||
if (pathExists(path)) {
|
||||
debug(format("removing unregistered path `%1%'") % path);
|
||||
@@ -1373,7 +1490,7 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild()
|
||||
/* Add the relevant output closures of the input derivation
|
||||
`*i' as input paths. Only add the closures of output paths
|
||||
that are specified as inputs. */
|
||||
assert(store->isValidPath(i->first));
|
||||
assert(worker.store.isValidPath(i->first));
|
||||
Derivation inDrv = derivationFromPath(i->first);
|
||||
for (StringSet::iterator j = i->second.begin();
|
||||
j != i->second.end(); ++j)
|
||||
@@ -1495,7 +1612,7 @@ void DerivationGoal::startBuilder()
|
||||
throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
|
||||
% storePath);
|
||||
storePath = toStorePath(storePath);
|
||||
if (!store->isValidPath(storePath))
|
||||
if (!worker.store.isValidPath(storePath))
|
||||
throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
|
||||
% storePath);
|
||||
|
||||
@@ -1505,9 +1622,48 @@ void DerivationGoal::startBuilder()
|
||||
/* !!! in secure Nix, the writing should be done on the
|
||||
build uid for security (maybe). */
|
||||
writeStringToFile(tmpDir + "/" + fileName,
|
||||
makeValidityRegistration(refs, false));
|
||||
makeValidityRegistration(refs, false, false));
|
||||
}
|
||||
|
||||
// The same for derivations
|
||||
s = drv.env["exportBuildReferencesGraph"];
|
||||
ss = tokenizeString(s);
|
||||
if (ss.size() % 2 != 0)
|
||||
throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
|
||||
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
|
||||
string fileName = *i++;
|
||||
checkStoreName(fileName); /* !!! abuse of this function */
|
||||
|
||||
/* Check that the store path is valid. */
|
||||
Path storePath = *i++;
|
||||
if (!isInStore(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
|
||||
% storePath);
|
||||
storePath = toStorePath(storePath);
|
||||
if (!worker.store.isValidPath(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
|
||||
% storePath);
|
||||
|
||||
/* Write closure info to `fileName'. */
|
||||
PathSet refs1,refs;
|
||||
computeFSClosure(storePath, refs1);
|
||||
for (PathSet::iterator j = refs1.begin(); j != refs1.end() ; j++) {
|
||||
refs.insert (*j);
|
||||
if (isDerivation (*j)) {
|
||||
Derivation deriv = derivationFromPath (*j);
|
||||
for (DerivationOutputs::iterator k=deriv.outputs.begin();
|
||||
k != deriv.outputs.end(); k++) {
|
||||
refs.insert(k->second.path);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
/* !!! in secure Nix, the writing should be done on the
|
||||
build uid for security (maybe). */
|
||||
writeStringToFile(tmpDir + "/" + fileName,
|
||||
makeValidityRegistration(refs, false, false));
|
||||
}
|
||||
|
||||
|
||||
/* If `build-users-group' is not empty, then we have to build as
|
||||
one of the members of that group. */
|
||||
@@ -1544,6 +1700,56 @@ void DerivationGoal::startBuilder()
|
||||
% 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. */
|
||||
printMsg(lvlChatty, format("executing builder `%1%'") %
|
||||
@@ -1569,6 +1775,17 @@ void DerivationGoal::startBuilder()
|
||||
|
||||
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();
|
||||
|
||||
/* Fill in the environment. */
|
||||
@@ -1632,7 +1849,7 @@ void DerivationGoal::startBuilder()
|
||||
% drv.builder);
|
||||
|
||||
} 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);
|
||||
}
|
||||
@@ -1765,27 +1982,17 @@ void DerivationGoal::computeClosure()
|
||||
}
|
||||
|
||||
/* Register each output path as valid, and register the sets of
|
||||
paths referenced by each of them. This is wrapped in one
|
||||
database transaction to ensure that if we crash, either
|
||||
everything is registered or nothing is. This is for
|
||||
recoverability: unregistered paths in the store can be deleted
|
||||
arbitrarily, while registered paths can only be deleted by
|
||||
running the garbage collector.
|
||||
|
||||
The reason that we do the transaction here and not on the fly
|
||||
while we are scanning (above) is so that we don't hold database
|
||||
locks for too long. */
|
||||
Transaction txn;
|
||||
createStoreTransaction(txn);
|
||||
paths referenced by each of them. !!! this should be
|
||||
atomic so that either all paths are registered as valid, or
|
||||
none are. */
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
{
|
||||
registerValidPath(txn, i->second.path,
|
||||
worker.store.registerValidPath(i->second.path,
|
||||
contentHashes[i->second.path],
|
||||
allReferences[i->second.path],
|
||||
drvPath);
|
||||
}
|
||||
txn.commit();
|
||||
|
||||
/* It is now safe to delete the lock files, since all future
|
||||
lockers will see that the output paths are valid; they will not
|
||||
@@ -1884,7 +2091,7 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid)
|
||||
PathSet result;
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
if (store->isValidPath(i->second.path)) {
|
||||
if (worker.store.isValidPath(i->second.path)) {
|
||||
if (returnValid) result.insert(i->second.path);
|
||||
} else {
|
||||
if (!returnValid) result.insert(i->second.path);
|
||||
@@ -1990,10 +2197,10 @@ void SubstitutionGoal::init()
|
||||
{
|
||||
trace("init");
|
||||
|
||||
store->addTempRoot(storePath);
|
||||
worker.store.addTempRoot(storePath);
|
||||
|
||||
/* If the path already exists we're done. */
|
||||
if (store->isValidPath(storePath)) {
|
||||
if (worker.store.isValidPath(storePath)) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
@@ -2064,7 +2271,7 @@ void SubstitutionGoal::referencesValid()
|
||||
for (PathSet::iterator i = references.begin();
|
||||
i != references.end(); ++i)
|
||||
if (*i != storePath) /* ignore self-references */
|
||||
assert(store->isValidPath(*i));
|
||||
assert(worker.store.isValidPath(*i));
|
||||
|
||||
state = &SubstitutionGoal::tryToRun;
|
||||
worker.waitForBuildSlot(shared_from_this());
|
||||
@@ -2098,7 +2305,7 @@ void SubstitutionGoal::tryToRun()
|
||||
(format("waiting for lock on `%1%'") % storePath).str());
|
||||
|
||||
/* Check again whether the path is invalid. */
|
||||
if (store->isValidPath(storePath)) {
|
||||
if (worker.store.isValidPath(storePath)) {
|
||||
debug(format("store path `%1%' has become valid") % storePath);
|
||||
outputLock->setDeletion(true);
|
||||
amDone(ecSuccess);
|
||||
@@ -2143,7 +2350,7 @@ void SubstitutionGoal::tryToRun()
|
||||
throw SysError(format("executing `%1%'") % sub);
|
||||
|
||||
} 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);
|
||||
}
|
||||
@@ -2205,11 +2412,8 @@ void SubstitutionGoal::finished()
|
||||
|
||||
Hash contentHash = hashPath(htSHA256, storePath);
|
||||
|
||||
Transaction txn;
|
||||
createStoreTransaction(txn);
|
||||
registerValidPath(txn, storePath, contentHash,
|
||||
worker.store.registerValidPath(storePath, contentHash,
|
||||
references, deriver);
|
||||
txn.commit();
|
||||
|
||||
outputLock->setDeletion(true);
|
||||
|
||||
@@ -2243,7 +2447,8 @@ void SubstitutionGoal::handleEOF(int fd)
|
||||
static bool working = false;
|
||||
|
||||
|
||||
Worker::Worker()
|
||||
Worker::Worker(LocalStore & store)
|
||||
: store(store)
|
||||
{
|
||||
/* Debugging: prevent recursive workers. */
|
||||
if (working) abort();
|
||||
@@ -2641,7 +2846,7 @@ void LocalStore::buildDerivations(const PathSet & drvPaths)
|
||||
startNest(nest, lvlDebug,
|
||||
format("building %1%") % showPaths(drvPaths));
|
||||
|
||||
Worker worker;
|
||||
Worker worker(*this);
|
||||
|
||||
Goals goals;
|
||||
for (PathSet::const_iterator i = drvPaths.begin();
|
||||
@@ -2666,9 +2871,9 @@ void LocalStore::buildDerivations(const PathSet & drvPaths)
|
||||
void LocalStore::ensurePath(const Path & path)
|
||||
{
|
||||
/* If the path is already valid, we're done. */
|
||||
if (store->isValidPath(path)) return;
|
||||
if (isValidPath(path)) return;
|
||||
|
||||
Worker worker;
|
||||
Worker worker(*this);
|
||||
GoalPtr goal = worker.makeSubstitutionGoal(path);
|
||||
Goals goals = singleton<Goals>(goal);
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef OLD_DB_COMPAT
|
||||
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
#include "pathlocks.hh"
|
||||
@@ -466,3 +470,5 @@ void Database::clearTable(const Transaction & txn, TableId table)
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include "misc.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "local-store.hh"
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
@@ -26,6 +24,8 @@ static string gcLockName = "gc.lock";
|
||||
static string tempRootsDir = "temproots";
|
||||
static string gcRootsDir = "gcroots";
|
||||
|
||||
static const int defaultGcLevel = 1000;
|
||||
|
||||
|
||||
/* Acquire the global GC lock. This is used to prevent new Nix
|
||||
processes from starting after the temporary root files have been
|
||||
@@ -445,6 +445,8 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
queryBoolSetting("gc-keep-outputs", false);
|
||||
bool gcKeepDerivations =
|
||||
queryBoolSetting("gc-keep-derivations", true);
|
||||
int gcKeepOutputsThreshold =
|
||||
queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel);
|
||||
|
||||
/* Acquire the global GC root. This prevents
|
||||
a) New roots from being added.
|
||||
@@ -496,10 +498,17 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
i != livePaths.end(); ++i)
|
||||
if (isDerivation(*i)) {
|
||||
Derivation drv = derivationFromPath(*i);
|
||||
for (DerivationOutputs::iterator j = drv.outputs.begin();
|
||||
j != drv.outputs.end(); ++j)
|
||||
if (store->isValidPath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths);
|
||||
|
||||
string gcLevelStr = drv.env["__gcLevel"];
|
||||
int gcLevel;
|
||||
if (!string2Int(gcLevelStr, gcLevel))
|
||||
gcLevel = defaultGcLevel;
|
||||
|
||||
if (gcLevel >= gcKeepOutputsThreshold)
|
||||
for (DerivationOutputs::iterator j = drv.outputs.begin();
|
||||
j != drv.outputs.end(); ++j)
|
||||
if (store->isValidPath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ bool readOnlyMode = false;
|
||||
string thisSystem = "unset";
|
||||
unsigned int maxSilentTime = 0;
|
||||
Paths substituters;
|
||||
bool useBuildHook = true;
|
||||
|
||||
|
||||
static bool settingsRead = false;
|
||||
|
||||
@@ -72,6 +72,10 @@ extern unsigned int maxSilentTime;
|
||||
from a CD. */
|
||||
extern Paths substituters;
|
||||
|
||||
/* Whether to use build hooks (for distributed builds). Sometimes
|
||||
users want to disable this from the command-line. */
|
||||
extern bool useBuildHook;
|
||||
|
||||
|
||||
Strings querySetting(const string & name, const Strings & def);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,18 +4,16 @@
|
||||
#include <string>
|
||||
|
||||
#include "store-api.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
class Transaction;
|
||||
|
||||
|
||||
/* Nix store and database schema version. Version 1 (or 0) was Nix <=
|
||||
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
|
||||
Version 4 is Nix 0.11. */
|
||||
const int nixSchemaVersion = 4;
|
||||
Version 4 is Nix 0.11. Version 5 is Nix 0.12*/
|
||||
const int nixSchemaVersion = 5;
|
||||
|
||||
|
||||
extern string drvsLogDir;
|
||||
@@ -43,15 +41,9 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
/* Open the database environment. If `reserveSpace' is true, make
|
||||
sure that a big empty file exists in /nix/var/nix/db/reserved.
|
||||
If `reserveSpace' is false, delete this file if it exists. The
|
||||
idea is that on normal operation, the file exists; but when we
|
||||
run the garbage collector, it is deleted. This is to ensure
|
||||
that the garbage collector has a small amount of disk space
|
||||
available, which is required to open the Berkeley DB
|
||||
environment. */
|
||||
LocalStore(bool reserveSpace);
|
||||
/* Initialise the local store, upgrading the schema if
|
||||
necessary. */
|
||||
LocalStore();
|
||||
|
||||
~LocalStore();
|
||||
|
||||
@@ -59,6 +51,8 @@ public:
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
|
||||
void queryReferences(const Path & path, PathSet & references);
|
||||
@@ -98,33 +92,63 @@ public:
|
||||
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
|
||||
|
||||
/* Delete a path from the Nix store. */
|
||||
void deleteFromStore(const Path & path, unsigned long long & bytesFreed);
|
||||
|
||||
/* Optimise the disk space usage of the Nix store by hard-linking
|
||||
files with the same contents. */
|
||||
void optimiseStore(bool dryRun, OptimiseStats & stats);
|
||||
|
||||
/* Check the integrity of the Nix store. */
|
||||
void verifyStore(bool checkContents);
|
||||
|
||||
/* Register the validity of a path, i.e., that `path' exists, that
|
||||
the paths referenced by it exists, and in the case of an output
|
||||
path of a derivation, that it has been produced by a succesful
|
||||
execution of the derivation (or something equivalent). Also
|
||||
register the hash of the file system contents of the path. The
|
||||
hash must be a SHA-256 hash. */
|
||||
void registerValidPath(const Path & path,
|
||||
const Hash & hash, const PathSet & references, const Path & deriver);
|
||||
|
||||
void registerValidPaths(const ValidPathInfos & infos);
|
||||
|
||||
private:
|
||||
|
||||
Path schemaPath;
|
||||
|
||||
/* Lock file used for upgrading. */
|
||||
AutoCloseFD globalLock;
|
||||
|
||||
/* !!! The cache can grow very big. Maybe it should be pruned
|
||||
every once in a while. */
|
||||
std::map<Path, ValidPathInfo> pathInfoCache;
|
||||
|
||||
/* Store paths for which the referrers file must be purged. */
|
||||
PathSet delayedUpdates;
|
||||
|
||||
int getSchema();
|
||||
|
||||
void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false);
|
||||
|
||||
ValidPathInfo queryPathInfo(const Path & path);
|
||||
|
||||
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
|
||||
|
||||
void flushDelayedUpdates();
|
||||
|
||||
bool queryReferrersInternal(const Path & path, PathSet & referrers);
|
||||
|
||||
void invalidatePath(const Path & path);
|
||||
|
||||
void upgradeStore12();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* Get a transaction object. */
|
||||
void createStoreTransaction(Transaction & txn);
|
||||
|
||||
/* Copy a path recursively. */
|
||||
void copyPath(const Path & src, const Path & dst);
|
||||
|
||||
/* Register the validity of a path, i.e., that `path' exists, that the
|
||||
paths referenced by it exists, and in the case of an output path of
|
||||
a derivation, that it has been produced by a succesful execution of
|
||||
the derivation (or something equivalent). Also register the hash
|
||||
of the file system contents of the path. The hash must be a
|
||||
SHA-256 hash. */
|
||||
void registerValidPath(const Transaction & txn,
|
||||
const Path & path, const Hash & hash, const PathSet & references,
|
||||
const Path & deriver);
|
||||
|
||||
typedef list<ValidPathInfo> ValidPathInfos;
|
||||
|
||||
void registerValidPaths(const Transaction & txn,
|
||||
const ValidPathInfos & infos);
|
||||
|
||||
/* "Fix", or canonicalise, the meta-data of the files in a store path
|
||||
after it has been built. In particular:
|
||||
- the last modification date on each file is set to 0 (i.e.,
|
||||
@@ -135,25 +159,10 @@ void registerValidPaths(const Transaction & txn,
|
||||
in a setuid Nix installation. */
|
||||
void canonicalisePathMetaData(const Path & path);
|
||||
|
||||
/* Checks whether a path is valid. */
|
||||
bool isValidPathTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
/* Sets the set of outgoing FS references for a store path. Use with
|
||||
care! */
|
||||
void setReferences(const Transaction & txn, const Path & path,
|
||||
const PathSet & references);
|
||||
|
||||
/* Sets the deriver of a store path. Use with care! */
|
||||
void setDeriver(const Transaction & txn, const Path & path,
|
||||
const Path & deriver);
|
||||
|
||||
/* Delete a value from the nixStore directory. */
|
||||
void deleteFromStore(const Path & path, unsigned long long & bytesFreed);
|
||||
void canonicalisePathMetaData(const Path & path, bool recurse);
|
||||
|
||||
MakeError(PathInUse, Error);
|
||||
|
||||
void verifyStore(bool checkContents);
|
||||
|
||||
/* Whether we are in build users mode. */
|
||||
bool haveBuildUsers();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "misc.hh"
|
||||
#include "store-api.hh"
|
||||
#include "db.hh"
|
||||
|
||||
#include <aterm2.h>
|
||||
|
||||
|
||||
129
src/libstore/optimise-store.cc
Normal file
129
src/libstore/optimise-store.cc
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
typedef std::map<Hash, std::pair<Path, ino_t> > HashToPath;
|
||||
|
||||
|
||||
static void makeWritable(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("changing writability of `%1%'") % path);
|
||||
}
|
||||
|
||||
|
||||
static void hashAndLink(bool dryRun, HashToPath & hashToPath,
|
||||
OptimiseStats & stats, const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
/* Sometimes SNAFUs can cause files in the Nix store to be
|
||||
modified, in particular when running programs as root under
|
||||
NixOS (example: $fontconfig/var/cache being modified). Skip
|
||||
those files. */
|
||||
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
|
||||
printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We can hard link regular files and symlinks. */
|
||||
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
|
||||
|
||||
/* Hash the file. Note that hashPath() returns the hash over
|
||||
the NAR serialisation, which includes the execute bit on
|
||||
the file. Thus, executable and non-executable files with
|
||||
the same contents *won't* be linked (which is good because
|
||||
otherwise the permissions would be screwed up).
|
||||
|
||||
Also note that if `path' is a symlink, then we're hashing
|
||||
the contents of the symlink (i.e. the result of
|
||||
readlink()), not the contents of the target (which may not
|
||||
even exist). */
|
||||
Hash hash = hashPath(htSHA256, path);
|
||||
stats.totalFiles++;
|
||||
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
|
||||
|
||||
std::pair<Path, ino_t> prevPath = hashToPath[hash];
|
||||
|
||||
if (prevPath.first == "") {
|
||||
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Yes! We've seen a file with the same contents. Replace
|
||||
the current file with a hard link to that file. */
|
||||
stats.sameContents++;
|
||||
if (prevPath.second == st.st_ino) {
|
||||
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % prevPath.first);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
|
||||
printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % prevPath.first);
|
||||
|
||||
Path tempLink = (format("%1%.tmp-%2%-%3%")
|
||||
% path % getpid() % rand()).str();
|
||||
|
||||
/* Make the containing directory writable, but only if
|
||||
it's not the store itself (we don't want or need to
|
||||
mess with its permissions). */
|
||||
bool mustToggle = !isStorePath(path);
|
||||
if (mustToggle) makeWritable(dirOf(path));
|
||||
|
||||
if (link(prevPath.first.c_str(), tempLink.c_str()) == -1)
|
||||
throw SysError(format("cannot link `%1%' to `%2%'")
|
||||
% tempLink % prevPath.first);
|
||||
|
||||
/* Atomically replace the old file with the new hard link. */
|
||||
if (rename(tempLink.c_str(), path.c_str()) == -1)
|
||||
throw SysError(format("cannot rename `%1%' to `%2%'")
|
||||
% tempLink % path);
|
||||
|
||||
/* Make the directory read-only again and reset its
|
||||
timestamp back to 0. */
|
||||
if (mustToggle) canonicalisePathMetaData(dirOf(path), false);
|
||||
|
||||
} else
|
||||
printMsg(lvlTalkative, format("would link `%1%' to `%2%'") % path % prevPath.first);
|
||||
|
||||
stats.filesLinked++;
|
||||
stats.bytesFreed += st.st_size;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
hashAndLink(dryRun, hashToPath, stats, path + "/" + *i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats)
|
||||
{
|
||||
HashToPath hashToPath;
|
||||
|
||||
PathSet paths = queryValidPaths();
|
||||
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
addTempRoot(*i);
|
||||
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
|
||||
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
|
||||
hashAndLink(dryRun, hashToPath, stats, *i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -59,7 +59,7 @@ RemoteStore::RemoteStore()
|
||||
unsigned int magic = readInt(from);
|
||||
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
|
||||
|
||||
unsigned int daemonVersion = readInt(from);
|
||||
daemonVersion = readInt(from);
|
||||
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
||||
throw Error("Nix daemon protocol version not supported");
|
||||
writeInt(PROTOCOL_VERSION, to);
|
||||
@@ -169,6 +169,8 @@ void RemoteStore::setOptions()
|
||||
writeInt(verbosity, to);
|
||||
writeInt(maxBuildJobs, to);
|
||||
writeInt(maxSilentTime, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
|
||||
writeInt(useBuildHook, to);
|
||||
processStderr();
|
||||
}
|
||||
|
||||
@@ -183,6 +185,12 @@ bool RemoteStore::isValidPath(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryValidPaths()
|
||||
{
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
bool RemoteStore::hasSubstitutes(const Path & path)
|
||||
{
|
||||
writeInt(wopHasSubstitutes, to);
|
||||
@@ -230,7 +238,9 @@ Path RemoteStore::queryDeriver(const Path & path)
|
||||
writeInt(wopQueryDeriver, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
return readStorePath(from);
|
||||
Path drvPath = readString(from);
|
||||
if (drvPath != "") assertStorePath(drvPath);
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ public:
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
|
||||
void queryReferences(const Path & path, PathSet & references);
|
||||
@@ -71,6 +73,7 @@ private:
|
||||
FdSink to;
|
||||
FdSource from;
|
||||
Pid child;
|
||||
unsigned int daemonVersion;
|
||||
|
||||
void processStderr(Sink * sink = 0, Source * source = 0);
|
||||
|
||||
|
||||
@@ -48,6 +48,26 @@ Path toStorePath(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
Path followLinksToStore(const Path & _path)
|
||||
{
|
||||
Path path = absPath(_path);
|
||||
while (!isInStore(path)) {
|
||||
if (!isLink(path)) break;
|
||||
string target = readLink(path);
|
||||
path = absPath(target, dirOf(path));
|
||||
}
|
||||
if (!isInStore(path))
|
||||
throw Error(format("path `%1%' is not in the Nix store") % path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
Path followLinksToStorePath(const Path & path)
|
||||
{
|
||||
return toStorePath(followLinksToStore(path));
|
||||
}
|
||||
|
||||
|
||||
void checkStoreName(const string & name)
|
||||
{
|
||||
string validChars = "+-._?=";
|
||||
@@ -131,11 +151,47 @@ Path computeStorePathForText(const string & suffix, const string & s,
|
||||
}
|
||||
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str)
|
||||
/* Return a string accepted by decodeValidPathInfo() that
|
||||
registers the specified paths as valid. Note: it's the
|
||||
responsibility of the caller to provide a closure. */
|
||||
string makeValidityRegistration(const PathSet & paths,
|
||||
bool showDerivers, bool showHash)
|
||||
{
|
||||
string s = "";
|
||||
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
s += *i + "\n";
|
||||
|
||||
if (showHash)
|
||||
s += printHash(store->queryPathHash(*i)) + "\n";
|
||||
|
||||
Path deriver = showDerivers ? store->queryDeriver(*i) : "";
|
||||
s += deriver + "\n";
|
||||
|
||||
PathSet references;
|
||||
store->queryReferences(*i, references);
|
||||
|
||||
s += (format("%1%\n") % references.size()).str();
|
||||
|
||||
for (PathSet::iterator j = references.begin();
|
||||
j != references.end(); ++j)
|
||||
s += *j + "\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
|
||||
{
|
||||
ValidPathInfo info;
|
||||
getline(str, info.path);
|
||||
if (str.eof()) { info.path = ""; return info; }
|
||||
if (hashGiven) {
|
||||
string s;
|
||||
getline(str, s);
|
||||
info.hash = parseHash(htSHA256, s);
|
||||
}
|
||||
getline(str, info.deriver);
|
||||
string s; int n;
|
||||
getline(str, s);
|
||||
@@ -149,6 +205,19 @@ ValidPathInfo decodeValidPathInfo(std::istream & str)
|
||||
}
|
||||
|
||||
|
||||
string showPaths(const PathSet & paths)
|
||||
{
|
||||
string s;
|
||||
for (PathSet::const_iterator i = paths.begin();
|
||||
i != paths.end(); ++i)
|
||||
{
|
||||
if (s.size() != 0) s += ", ";
|
||||
s += "`" + *i + "'";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -163,10 +232,10 @@ namespace nix {
|
||||
boost::shared_ptr<StoreAPI> store;
|
||||
|
||||
|
||||
boost::shared_ptr<StoreAPI> openStore(bool reserveSpace)
|
||||
boost::shared_ptr<StoreAPI> openStore()
|
||||
{
|
||||
if (getEnv("NIX_REMOTE") == "")
|
||||
return boost::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
|
||||
return boost::shared_ptr<StoreAPI>(new LocalStore());
|
||||
else
|
||||
return boost::shared_ptr<StoreAPI>(new RemoteStore());
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public:
|
||||
/* Checks whether a path is valid. */
|
||||
virtual bool isValidPath(const Path & path) = 0;
|
||||
|
||||
/* Query the set of valid paths. */
|
||||
virtual PathSet queryValidPaths() = 0;
|
||||
|
||||
/* Queries the hash of a valid path. */
|
||||
virtual Hash queryPathHash(const Path & path) = 0;
|
||||
|
||||
@@ -174,11 +177,21 @@ bool isStorePath(const Path & path);
|
||||
|
||||
void checkStoreName(const string & name);
|
||||
|
||||
|
||||
/* Chop off the parts after the top-level store name, e.g.,
|
||||
/nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
|
||||
Path toStorePath(const Path & path);
|
||||
|
||||
|
||||
/* Follow symlinks until we end up with a path in the Nix store. */
|
||||
Path followLinksToStore(const Path & path);
|
||||
|
||||
|
||||
/* Same as followLinksToStore(), but apply toStorePath() to the
|
||||
result. */
|
||||
Path followLinksToStorePath(const Path & path);
|
||||
|
||||
|
||||
/* Constructs a unique store path name. */
|
||||
Path makeStorePath(const string & type,
|
||||
const Hash & hash, const string & suffix);
|
||||
@@ -236,18 +249,31 @@ extern boost::shared_ptr<StoreAPI> store;
|
||||
|
||||
/* Factory method: open the Nix database, either through the local or
|
||||
remote implementation. */
|
||||
boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
|
||||
boost::shared_ptr<StoreAPI> openStore();
|
||||
|
||||
|
||||
/* Display a set of paths in human-readable form (i.e., between quotes
|
||||
and separated by commas). */
|
||||
string showPaths(const PathSet & paths);
|
||||
|
||||
|
||||
string makeValidityRegistration(const PathSet & paths,
|
||||
bool showDerivers, bool showHash);
|
||||
|
||||
struct ValidPathInfo
|
||||
{
|
||||
Path path;
|
||||
Path deriver;
|
||||
Hash hash;
|
||||
PathSet references;
|
||||
time_t registrationTime;
|
||||
ValidPathInfo() : registrationTime(0) { }
|
||||
};
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str);
|
||||
typedef list<ValidPathInfo> ValidPathInfos;
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str,
|
||||
bool hashGiven = false);
|
||||
|
||||
|
||||
}
|
||||
|
||||
108
src/libstore/upgrade-schema.cc
Normal file
108
src/libstore/upgrade-schema.cc
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "db.hh"
|
||||
#include "hash.hh"
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
#include "globals.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "config.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
Hash parseHashField(const Path & path, const string & s);
|
||||
|
||||
|
||||
/* Upgrade from schema 4 (Nix 0.11) to schema 5 (Nix >= 0.12). The
|
||||
old schema uses Berkeley DB, the new one stores store path
|
||||
meta-information in files. */
|
||||
void LocalStore::upgradeStore12()
|
||||
{
|
||||
#if OLD_DB_COMPAT
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
/* Cygwin can't upgrade a read lock to a write lock... */
|
||||
lockFile(globalLock, ltNone, true);
|
||||
#endif
|
||||
|
||||
if (!lockFile(globalLock, ltWrite, false)) {
|
||||
printMsg(lvlError, "waiting for exclusive access to the Nix store...");
|
||||
lockFile(globalLock, ltWrite, true);
|
||||
}
|
||||
|
||||
printMsg(lvlError, "upgrading Nix store to new schema (this may take a while)...");
|
||||
|
||||
if (getSchema() >= nixSchemaVersion) return; /* somebody else beat us to it */
|
||||
|
||||
/* Open the old Nix database and tables. */
|
||||
Database nixDB;
|
||||
nixDB.open(nixDBPath);
|
||||
|
||||
/* dbValidPaths :: Path -> ()
|
||||
|
||||
The existence of a key $p$ indicates that path $p$ is valid
|
||||
(that is, produced by a succesful build). */
|
||||
TableId dbValidPaths = nixDB.openTable("validpaths");
|
||||
|
||||
/* dbReferences :: Path -> [Path]
|
||||
|
||||
This table lists the outgoing file system references for each
|
||||
output path that has been built by a Nix derivation. These are
|
||||
found by scanning the path for the hash components of input
|
||||
paths. */
|
||||
TableId dbReferences = nixDB.openTable("references");
|
||||
|
||||
/* dbReferrers :: Path -> Path
|
||||
|
||||
This table is just the reverse mapping of dbReferences. This
|
||||
table can have duplicate keys, each corresponding value
|
||||
denoting a single referrer. */
|
||||
// Not needed for conversion: it's just the inverse of
|
||||
// references.
|
||||
// TableId dbReferrers = nixDB.openTable("referrers");
|
||||
|
||||
/* dbDerivers :: Path -> [Path]
|
||||
|
||||
This table lists the derivation used to build a path. There
|
||||
can only be multiple such paths for fixed-output derivations
|
||||
(i.e., derivations specifying an expected hash). */
|
||||
TableId dbDerivers = nixDB.openTable("derivers");
|
||||
|
||||
Paths paths;
|
||||
nixDB.enumTable(noTxn, dbValidPaths, paths);
|
||||
|
||||
for (Paths::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
ValidPathInfo info;
|
||||
info.path = *i;
|
||||
|
||||
Paths references;
|
||||
nixDB.queryStrings(noTxn, dbReferences, *i, references);
|
||||
info.references.insert(references.begin(), references.end());
|
||||
|
||||
string s;
|
||||
nixDB.queryString(noTxn, dbValidPaths, *i, s);
|
||||
info.hash = parseHashField(*i, s);
|
||||
|
||||
nixDB.queryString(noTxn, dbDerivers, *i, info.deriver);
|
||||
|
||||
registerValidPath(info, true);
|
||||
std::cerr << ".";
|
||||
}
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
|
||||
|
||||
lockFile(globalLock, ltRead, true);
|
||||
|
||||
#else
|
||||
throw Error(
|
||||
"Your Nix store has a database in Berkeley DB format. To convert\n"
|
||||
"to the new format, please compile Nix with Berkeley DB support.");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -8,8 +8,9 @@ namespace nix {
|
||||
#define WORKER_MAGIC_1 0x6e697863
|
||||
#define WORKER_MAGIC_2 0x6478696f
|
||||
|
||||
#define PROTOCOL_VERSION 0x101
|
||||
#define PROTOCOL_VERSION 0x102
|
||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -105,8 +106,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
||||
/* If s points to a symlink, resolve it and restart (since
|
||||
the symlink target might contain new symlinks). */
|
||||
if (resolveSymlinks && isLink(s)) {
|
||||
followCount++;
|
||||
if (followCount >= maxFollow)
|
||||
if (++followCount >= maxFollow)
|
||||
throw Error(format("infinite symlink recursion in path `%1%'") % path);
|
||||
temp = absPath(readLink(s), dirOf(s))
|
||||
+ string(i, end);
|
||||
@@ -318,19 +318,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;
|
||||
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) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = tempName(tmpRoot);
|
||||
Path tmpDir = tempName(tmpRoot, prefix);
|
||||
if (mkdir(tmpDir.c_str(), 0777) == 0) {
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
@@ -350,13 +350,17 @@ Path createTempDir(const Path & tmpRoot)
|
||||
}
|
||||
|
||||
|
||||
void createDirs(const Path & path)
|
||||
Paths createDirs(const Path & path)
|
||||
{
|
||||
if (path == "/") return;
|
||||
createDirs(dirOf(path));
|
||||
if (!pathExists(path))
|
||||
Paths created;
|
||||
if (path == "/") return created;
|
||||
if (!pathExists(path)) {
|
||||
created = createDirs(dirOf(path));
|
||||
if (mkdir(path.c_str(), 0777) == -1)
|
||||
throw SysError(format("creating directory `%1%'") % path);
|
||||
created.push_back(path);
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
|
||||
@@ -509,14 +513,25 @@ string drainFD(int fd)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
AutoDelete::AutoDelete(const string & p) : path(p)
|
||||
AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
|
||||
{
|
||||
del = true;
|
||||
this->recursive = recursive;
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -752,10 +767,10 @@ void killUser(uid_t uid)
|
||||
if (errno != EINTR)
|
||||
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
||||
}
|
||||
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("killing processes beloging to uid `%1%': %1%\n")
|
||||
% uid % e.what();
|
||||
std::cerr << format("killing processes beloging to uid `%1%': %1%")
|
||||
% uid % e.what() << std::endl;
|
||||
quickExit(1);
|
||||
}
|
||||
quickExit(0);
|
||||
@@ -947,8 +962,15 @@ string statusToString(int status)
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
if (WIFEXITED(status))
|
||||
return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
|
||||
else if (WIFSIGNALED(status))
|
||||
return (format("failed due to signal %1%") % WTERMSIG(status)).str();
|
||||
else if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
#if HAVE_STRSIGNAL
|
||||
const char * description = strsignal(sig);
|
||||
return (format("failed due to signal %1% (%2%)") % sig % description).str();
|
||||
#else
|
||||
return (format("failed due to signal %1%") % sig).str();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
return "died abnormally";
|
||||
} else return "succeeded";
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
#define foreach(it_type, it, collection) \
|
||||
for (it_type it = collection.begin(); it != collection.end(); ++it)
|
||||
|
||||
|
||||
/* Return an environment variable. */
|
||||
string getEnv(const string & key, const string & def = "");
|
||||
|
||||
@@ -70,10 +74,11 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
|
||||
void makePathReadOnly(const Path & path);
|
||||
|
||||
/* 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. */
|
||||
void createDirs(const Path & path);
|
||||
/* Create a directory and all its parents, if necessary. Returns the
|
||||
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
|
||||
in binary mode (i.e., no end-of-line conversions). The path should
|
||||
@@ -166,8 +171,9 @@ class AutoDelete
|
||||
{
|
||||
Path path;
|
||||
bool del;
|
||||
bool recursive;
|
||||
public:
|
||||
AutoDelete(const Path & p);
|
||||
AutoDelete(const Path & p, bool recursive = true);
|
||||
~AutoDelete();
|
||||
void cancel();
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ Operations:
|
||||
--set: create a user environment containing a single derivation
|
||||
--uninstall / -e: remove derivations from the user environment
|
||||
--query / -q: perform a query on an environment or Nix expression
|
||||
--set-flag NAME VALUE: set derivation meta-attribute to given value
|
||||
|
||||
The previous operations take a list of derivation names. The special
|
||||
name `*' may be used to indicate all derivations.
|
||||
@@ -20,8 +21,6 @@ name `*' may be used to indicate all derivations.
|
||||
--delete-generations GENERATIONS...: deleted listed generations,
|
||||
`old' for all non-current generations
|
||||
|
||||
--import / -I FILE: set default Nix expression
|
||||
|
||||
--version: output version information
|
||||
--help: display help
|
||||
|
||||
@@ -55,7 +54,7 @@ Query flags:
|
||||
--xml: show output in XML format
|
||||
--status / -s: print installed/present status
|
||||
--no-name: hide derivation names
|
||||
--attr / -A: shows the unambiguous attribute name of the
|
||||
--attr-path / -P: shows the unambiguous attribute name of the
|
||||
derivation which can be used when installing with -A
|
||||
--system: print the platform type of the derivation
|
||||
--compare-versions / -c: compare version to available or installed
|
||||
@@ -63,8 +62,6 @@ Query flags:
|
||||
--out-path: print path of derivation output
|
||||
--description: print description
|
||||
--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:
|
||||
|
||||
@@ -74,3 +71,5 @@ Options:
|
||||
--keep-failed / -K: keep temporary directories of failed builds
|
||||
--preserve-installed: do not replace currently installed versions in `-i'
|
||||
--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
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "common-opts.hh"
|
||||
#include "xml-writer.hh"
|
||||
#include "store-api.hh"
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <cerrno>
|
||||
@@ -47,8 +46,9 @@ struct InstallSourceInfo
|
||||
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
|
||||
Path profile; /* for srcProfile */
|
||||
string systemFilter; /* for srcNixExprDrvs */
|
||||
bool prebuiltOnly;
|
||||
ATermMap autoArgs;
|
||||
InstallSourceInfo() : autoArgs() { };
|
||||
InstallSourceInfo() : prebuiltOnly(false) { };
|
||||
};
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ static bool parseInstallSourceOptions(Globals & globals,
|
||||
}
|
||||
else if (arg == "--attr" || arg == "-A")
|
||||
globals.instSource.type = srcAttrPath;
|
||||
else if (arg == "--prebuilt-only" || arg == "-b")
|
||||
globals.instSource.prebuiltOnly = true;
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
@@ -319,9 +321,16 @@ static int comparePriorities(EvalState & state,
|
||||
}
|
||||
|
||||
|
||||
static DrvInfos filterBySelector(EvalState & state,
|
||||
const DrvInfos & allElems,
|
||||
const Strings & args, bool newestOnly)
|
||||
static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -340,7 +349,8 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||
DrvName drvName(j->name);
|
||||
if (i->matches(drvName)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +409,7 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||
/* Check that all selectors have been used. */
|
||||
for (DrvNames::iterator i = selectors.begin();
|
||||
i != selectors.end(); ++i)
|
||||
if (i->hits == 0)
|
||||
if (i->hits == 0 && i->fullName != "*")
|
||||
throw Error(format("selector `%1%' matches no derivations")
|
||||
% i->fullName);
|
||||
|
||||
@@ -407,12 +417,18 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||
}
|
||||
|
||||
|
||||
static bool isPath(const string & s)
|
||||
{
|
||||
return s.find('/') != string::npos;
|
||||
}
|
||||
|
||||
|
||||
static void queryInstSources(EvalState & state,
|
||||
const InstallSourceInfo & instSource, const Strings & args,
|
||||
DrvInfos & elems, bool newestOnly)
|
||||
{
|
||||
InstallSourceType type = instSource.type;
|
||||
if (type == srcUnknown && args.size() > 0 && args.front()[0] == '/')
|
||||
if (type == srcUnknown && args.size() > 0 && isPath(args.front()))
|
||||
type = srcStorePaths;
|
||||
|
||||
switch (type) {
|
||||
@@ -429,7 +445,8 @@ static void queryInstSources(EvalState & state,
|
||||
loadDerivations(state, instSource.nixExprPath,
|
||||
instSource.systemFilter, instSource.autoArgs, "", allElems);
|
||||
|
||||
elems = filterBySelector(state, allElems, args, newestOnly);
|
||||
elems = filterBySelector(state, allElems, args,
|
||||
newestOnly, instSource.prebuiltOnly);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -463,23 +480,23 @@ static void queryInstSources(EvalState & state,
|
||||
for (Strings::const_iterator i = args.begin();
|
||||
i != args.end(); ++i)
|
||||
{
|
||||
assertStorePath(*i);
|
||||
Path path = followLinksToStorePath(*i);
|
||||
|
||||
DrvInfo elem;
|
||||
elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
|
||||
string name = baseNameOf(*i);
|
||||
string name = baseNameOf(path);
|
||||
string::size_type dash = name.find('-');
|
||||
if (dash != string::npos)
|
||||
name = string(name, dash + 1);
|
||||
|
||||
if (isDerivation(*i)) {
|
||||
elem.setDrvPath(*i);
|
||||
elem.setOutPath(findOutput(derivationFromPath(*i), "out"));
|
||||
if (isDerivation(path)) {
|
||||
elem.setDrvPath(path);
|
||||
elem.setOutPath(findOutput(derivationFromPath(path), "out"));
|
||||
if (name.size() >= drvExtension.size() &&
|
||||
string(name, name.size() - drvExtension.size()) == drvExtension)
|
||||
name = string(name, 0, name.size() - drvExtension.size());
|
||||
}
|
||||
else elem.setOutPath(*i);
|
||||
else elem.setOutPath(path);
|
||||
|
||||
elem.name = name;
|
||||
|
||||
@@ -495,7 +512,7 @@ static void queryInstSources(EvalState & state,
|
||||
case srcProfile: {
|
||||
elems = filterBySelector(state,
|
||||
queryInstalled(state, instSource.profile),
|
||||
args, newestOnly);
|
||||
args, newestOnly, instSource.prebuiltOnly);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -799,7 +816,7 @@ static void opSet(Globals & globals,
|
||||
}
|
||||
|
||||
|
||||
static void uninstallDerivations(Globals & globals, DrvNames & selectors,
|
||||
static void uninstallDerivations(Globals & globals, Strings & selectors,
|
||||
Path & profile)
|
||||
{
|
||||
PathLocks lock;
|
||||
@@ -812,11 +829,13 @@ static void uninstallDerivations(Globals & globals, DrvNames & selectors,
|
||||
{
|
||||
DrvName drvName(i->name);
|
||||
bool found = false;
|
||||
for (DrvNames::iterator j = selectors.begin();
|
||||
j != selectors.end(); ++j)
|
||||
if (j->matches(drvName)) {
|
||||
printMsg(lvlInfo,
|
||||
format("uninstalling `%1%'") % i->name);
|
||||
for (Strings::iterator j = selectors.begin(); j != selectors.end(); ++j)
|
||||
/* !!! the repeated calls to followLinksToStorePath() are
|
||||
expensive, should pre-compute them. */
|
||||
if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j))
|
||||
|| DrvName(*j).matches(drvName))
|
||||
{
|
||||
printMsg(lvlInfo, format("uninstalling `%1%'") % i->name);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -835,11 +854,7 @@ static void opUninstall(Globals & globals,
|
||||
{
|
||||
if (opFlags.size() > 0)
|
||||
throw UsageError(format("unknown flag `%1%'") % opFlags.front());
|
||||
|
||||
DrvNames drvNames = drvNamesFromArgs(opArgs);
|
||||
|
||||
uninstallDerivations(globals, drvNames,
|
||||
globals.profile);
|
||||
uninstallDerivations(globals, opArgs, globals.profile);
|
||||
}
|
||||
|
||||
|
||||
@@ -999,7 +1014,7 @@ static void opQuery(Globals & globals,
|
||||
|
||||
DrvInfos elems = filterBySelector(globals.state,
|
||||
source == sInstalled ? installedElems : availElems,
|
||||
remaining, false);
|
||||
remaining, false, prebuiltOnly);
|
||||
|
||||
DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
|
||||
|
||||
@@ -1040,12 +1055,6 @@ static void opQuery(Globals & globals,
|
||||
/* For XML output. */
|
||||
XMLAttrs attrs;
|
||||
|
||||
if (prebuiltOnly) {
|
||||
if (!store->isValidPath(i->queryOutPath(globals.state)) &&
|
||||
!store->hasSubstitutes(i->queryOutPath(globals.state)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (printStatus) {
|
||||
bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
|
||||
bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "dotgraph.hh"
|
||||
#include "util.hh"
|
||||
#include "store-api.hh"
|
||||
#include "db.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
@@ -16,8 +16,13 @@ Operations:
|
||||
|
||||
--gc: run the garbage collector
|
||||
|
||||
--dump: dump a path as a Nix archive
|
||||
--restore: restore a path from a Nix archive
|
||||
--dump: dump a path as a Nix archive, forgetting dependencies
|
||||
--restore: restore a path from a Nix archive, without
|
||||
registering validity
|
||||
|
||||
--export: export a path as a Nix archive, marking dependencies
|
||||
--import: import a path from a Nix archive, and register as
|
||||
valid
|
||||
|
||||
--init: initialise the Nix database
|
||||
--verify: verify Nix structures
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "shared.hh"
|
||||
#include "dotgraph.hh"
|
||||
#include "local-store.hh"
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
#include "help.txt.hh"
|
||||
|
||||
@@ -31,15 +30,11 @@ static int rootNr = 0;
|
||||
static bool indirectRoot = false;
|
||||
|
||||
|
||||
static Path fixPath(Path path)
|
||||
LocalStore & ensureLocalStore()
|
||||
{
|
||||
path = absPath(path);
|
||||
while (!isInStore(path)) {
|
||||
if (!isLink(path)) break;
|
||||
string target = readLink(path);
|
||||
path = absPath(target, dirOf(path));
|
||||
}
|
||||
return toStorePath(path);
|
||||
LocalStore * store2(dynamic_cast<LocalStore *>(store.get()));
|
||||
if (!store2) throw Error("you don't have sufficient rights to use --verify");
|
||||
return *store2;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +81,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
|
||||
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
*i = fixPath(*i);
|
||||
*i = followLinksToStorePath(*i);
|
||||
|
||||
if (opArgs.size() > 1) {
|
||||
PathSet drvPaths;
|
||||
@@ -296,7 +291,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
*i = fixPath(*i);
|
||||
*i = followLinksToStorePath(*i);
|
||||
if (forceRealise) realisePath(*i);
|
||||
Derivation drv = derivationFromPath(*i);
|
||||
cout << format("%1%\n") % findOutput(drv, "out");
|
||||
@@ -312,7 +307,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise);
|
||||
Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
if (query == qRequisites)
|
||||
storePathRequisites(path, includeOutputs, paths);
|
||||
else if (query == qReferences) store->queryReferences(path, paths);
|
||||
@@ -330,7 +325,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
Path deriver = store->queryDeriver(fixPath(*i));
|
||||
Path deriver = store->queryDeriver(followLinksToStorePath(*i));
|
||||
cout << format("%1%\n") %
|
||||
(deriver == "" ? "unknown-deriver" : deriver);
|
||||
}
|
||||
@@ -340,7 +335,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
Path path = useDeriver(fixPath(*i));
|
||||
Path path = useDeriver(followLinksToStorePath(*i));
|
||||
Derivation drv = derivationFromPath(path);
|
||||
StringPairs::iterator j = drv.env.find(bindingName);
|
||||
if (j == drv.env.end())
|
||||
@@ -354,7 +349,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise);
|
||||
Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
Hash hash = store->queryPathHash(path);
|
||||
assert(hash.type == htSHA256);
|
||||
cout << format("sha256:%1%\n") % printHash32(hash);
|
||||
@@ -365,7 +360,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
PathSet done;
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
printTree(fixPath(*i), "", "", done);
|
||||
printTree(followLinksToStorePath(*i), "", "", done);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -373,7 +368,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
PathSet roots;
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
roots.insert(maybeUseOutput(fixPath(*i), useOutput, forceRealise));
|
||||
roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise));
|
||||
printDotGraph(roots);
|
||||
break;
|
||||
}
|
||||
@@ -381,7 +376,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
case qResolve: {
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
cout << format("%1%\n") % fixPath(*i);
|
||||
cout << format("%1%\n") % followLinksToStorePath(*i);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -398,7 +393,7 @@ static void opReadLog(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
Path path = useDeriver(fixPath(*i));
|
||||
Path path = useDeriver(followLinksToStorePath(*i));
|
||||
|
||||
Path logPath = (format("%1%/%2%/%3%") %
|
||||
nixLogDir % drvsLogDir % baseNameOf(path)).str();
|
||||
@@ -413,34 +408,62 @@ static void opReadLog(Strings opFlags, Strings opArgs)
|
||||
}
|
||||
|
||||
|
||||
static void opRegisterValidity(Strings opFlags, Strings opArgs)
|
||||
static void opDumpDB(Strings opFlags, Strings opArgs)
|
||||
{
|
||||
bool reregister = false; // !!! maybe this should be the default
|
||||
|
||||
for (Strings::iterator i = opFlags.begin();
|
||||
i != opFlags.end(); ++i)
|
||||
if (*i == "--reregister") reregister = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
if (!opFlags.empty()) throw UsageError("unknown flag");
|
||||
if (!opArgs.empty())
|
||||
throw UsageError("no arguments expected");
|
||||
PathSet validPaths = store->queryValidPaths();
|
||||
/* !!! this isn't streamy; makeValidityRegistration() builds a
|
||||
potentially gigantic string. */
|
||||
cout << makeValidityRegistration(validPaths, true, true);
|
||||
}
|
||||
|
||||
if (!opArgs.empty()) throw UsageError("no arguments expected");
|
||||
|
||||
static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
|
||||
{
|
||||
ValidPathInfos infos;
|
||||
|
||||
while (1) {
|
||||
ValidPathInfo info = decodeValidPathInfo(cin);
|
||||
ValidPathInfo info = decodeValidPathInfo(cin, hashGiven);
|
||||
if (info.path == "") break;
|
||||
if (!store->isValidPath(info.path) || reregister) {
|
||||
/* !!! races */
|
||||
canonicalisePathMetaData(info.path);
|
||||
info.hash = hashPath(htSHA256, info.path);
|
||||
if (canonicalise)
|
||||
canonicalisePathMetaData(info.path);
|
||||
if (!hashGiven)
|
||||
info.hash = hashPath(htSHA256, info.path);
|
||||
infos.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
Transaction txn;
|
||||
createStoreTransaction(txn);
|
||||
registerValidPaths(txn, infos);
|
||||
txn.commit();
|
||||
ensureLocalStore().registerValidPaths(infos);
|
||||
}
|
||||
|
||||
|
||||
static void opLoadDB(Strings opFlags, Strings opArgs)
|
||||
{
|
||||
if (!opFlags.empty()) throw UsageError("unknown flag");
|
||||
if (!opArgs.empty())
|
||||
throw UsageError("no arguments expected");
|
||||
registerValidity(true, true, false);
|
||||
}
|
||||
|
||||
|
||||
static void opRegisterValidity(Strings opFlags, Strings opArgs)
|
||||
{
|
||||
bool reregister = false; // !!! maybe this should be the default
|
||||
bool hashGiven = false;
|
||||
|
||||
for (Strings::iterator i = opFlags.begin();
|
||||
i != opFlags.end(); ++i)
|
||||
if (*i == "--reregister") reregister = true;
|
||||
else if (*i == "--hash-given") hashGiven = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
if (!opArgs.empty()) throw UsageError("no arguments expected");
|
||||
|
||||
registerValidity(reregister, hashGiven, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -456,7 +479,7 @@ static void opCheckValidity(Strings opFlags, Strings opArgs)
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
{
|
||||
Path path = fixPath(*i);
|
||||
Path path = followLinksToStorePath(*i);
|
||||
if (!store->isValidPath(path))
|
||||
if (printInvalid)
|
||||
cout << format("%1%\n") % path;
|
||||
@@ -531,7 +554,7 @@ static void opDelete(Strings opFlags, Strings opArgs)
|
||||
PathSet pathsToDelete;
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
pathsToDelete.insert(fixPath(*i));
|
||||
pathsToDelete.insert(followLinksToStorePath(*i));
|
||||
|
||||
PathSet dummy;
|
||||
PrintFreed freed(true, false);
|
||||
@@ -584,12 +607,17 @@ static void opExport(Strings opFlags, Strings opArgs)
|
||||
|
||||
static void opImport(Strings opFlags, Strings opArgs)
|
||||
{
|
||||
if (!opFlags.empty()) throw UsageError("unknown flag");
|
||||
bool requireSignature = false;
|
||||
for (Strings::iterator i = opFlags.begin();
|
||||
i != opFlags.end(); ++i)
|
||||
if (*i == "--require-signature") requireSignature = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
if (!opArgs.empty()) throw UsageError("no arguments expected");
|
||||
|
||||
FdSource source(STDIN_FILENO);
|
||||
while (readInt(source) == 1)
|
||||
cout << format("%1%\n") % store->importPath(false, source) << std::flush;
|
||||
cout << format("%1%\n") % store->importPath(requireSignature, source) << std::flush;
|
||||
}
|
||||
|
||||
|
||||
@@ -617,11 +645,10 @@ static void opVerify(Strings opFlags, Strings opArgs)
|
||||
if (*i == "--check-contents") checkContents = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
verifyStore(checkContents);
|
||||
ensureLocalStore().verifyStore(checkContents);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void showOptimiseStats(OptimiseStats & stats)
|
||||
{
|
||||
printMsg(lvlError,
|
||||
@@ -647,12 +674,9 @@ static void opOptimise(Strings opFlags, Strings opArgs)
|
||||
if (*i == "--dry-run") dryRun = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
LocalStore * store2(dynamic_cast<LocalStore *>(store.get()));
|
||||
if (!store2) throw Error("you don't have sufficient rights to use --optimise");
|
||||
|
||||
OptimiseStats stats;
|
||||
try {
|
||||
store2->optimiseStore(dryRun, stats);
|
||||
ensureLocalStore().optimiseStore(dryRun, stats);
|
||||
} catch (...) {
|
||||
showOptimiseStats(stats);
|
||||
throw;
|
||||
@@ -688,6 +712,10 @@ void run(Strings args)
|
||||
op = opQuery;
|
||||
else if (arg == "--read-log" || arg == "-l")
|
||||
op = opReadLog;
|
||||
else if (arg == "--dump-db")
|
||||
op = opDumpDB;
|
||||
else if (arg == "--load-db")
|
||||
op = opLoadDB;
|
||||
else if (arg == "--register-validity")
|
||||
op = opRegisterValidity;
|
||||
else if (arg == "--check-validity")
|
||||
@@ -727,7 +755,7 @@ void run(Strings args)
|
||||
if (!op) throw UsageError("no operation specified");
|
||||
|
||||
if (op != opDump && op != opRestore) /* !!! hack */
|
||||
store = openStore(op != opGC);
|
||||
store = openStore();
|
||||
|
||||
op(opFlags, opArgs);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,8 @@ struct TunnelSource : Source
|
||||
};
|
||||
|
||||
|
||||
static void performOp(Source & from, Sink & to, unsigned int op)
|
||||
static void performOp(unsigned int clientVersion,
|
||||
Source & from, Sink & to, unsigned int op)
|
||||
{
|
||||
switch (op) {
|
||||
|
||||
@@ -422,6 +423,8 @@ static void performOp(Source & from, Sink & to, unsigned int op)
|
||||
verbosity = (Verbosity) readInt(from);
|
||||
maxBuildJobs = readInt(from);
|
||||
maxSilentTime = readInt(from);
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
|
||||
useBuildHook = readInt(from) != 0;
|
||||
startWork();
|
||||
stopWork();
|
||||
break;
|
||||
@@ -463,13 +466,15 @@ static void processConnection()
|
||||
/* If we can't accept clientVersion, then throw an error
|
||||
*here* (not above). */
|
||||
|
||||
#if 0
|
||||
/* Prevent users from doing something very dangerous. */
|
||||
if (geteuid() == 0 &&
|
||||
querySetting("build-users-group", "") == "")
|
||||
throw Error("if you run `nix-worker' as root, then you MUST set `build-users-group'!");
|
||||
#endif
|
||||
|
||||
/* Open the store. */
|
||||
store = boost::shared_ptr<StoreAPI>(new LocalStore(true));
|
||||
store = boost::shared_ptr<StoreAPI>(new LocalStore());
|
||||
|
||||
stopWork();
|
||||
|
||||
@@ -492,7 +497,7 @@ static void processConnection()
|
||||
opCount++;
|
||||
|
||||
try {
|
||||
performOp(from, to, op);
|
||||
performOp(clientVersion, from, to, op);
|
||||
} catch (Error & e) {
|
||||
stopWork(false, e.msg());
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
||||
fallback.sh nix-push.sh gc.sh gc-concurrent.sh verify.sh nix-pull.sh \
|
||||
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
||||
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
|
||||
remote-store.sh
|
||||
remote-store.sh export.sh
|
||||
|
||||
XFAIL_TESTS =
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ $nixstore -q --graph "$drvPath" > $TEST_ROOT/graph
|
||||
if test -n "$dot"; then
|
||||
# Does it parse?
|
||||
$dot < $TEST_ROOT/graph
|
||||
fi
|
||||
fi
|
||||
|
||||
outPath=$($nixstore -rvv "$drvPath")
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Hello World
|
||||
31
tests/export.sh
Normal file
31
tests/export.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
source common.sh
|
||||
|
||||
clearStore
|
||||
|
||||
outPath=$($nixstore -r $($nixinstantiate dependencies.nix))
|
||||
|
||||
$nixstore --export $outPath > $TEST_ROOT/exp
|
||||
|
||||
$nixstore --export $($nixstore -qR $outPath) > $TEST_ROOT/exp_all
|
||||
|
||||
|
||||
clearStore
|
||||
|
||||
if $nixstore --import < $TEST_ROOT/exp; then
|
||||
echo "importing a non-closure should fail"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
clearStore
|
||||
|
||||
$nixstore --import < $TEST_ROOT/exp_all
|
||||
|
||||
$nixstore --export $($nixstore -qR $outPath) > $TEST_ROOT/exp_all2
|
||||
|
||||
|
||||
clearStore
|
||||
|
||||
# Regression test: the derivers in exp_all2 are empty, which shouldn't
|
||||
# cause a failure.
|
||||
$nixstore --import < $TEST_ROOT/exp_all2
|
||||
@@ -1,7 +1,7 @@
|
||||
source common.sh
|
||||
|
||||
try () {
|
||||
echo -n "$2" > $TEST_ROOT/vector
|
||||
printf "%s" "$2" > $TEST_ROOT/vector
|
||||
hash=$($nixhash $EXTRA --flat --type "$1" $TEST_ROOT/vector)
|
||||
if test "$hash" != "$3"; then
|
||||
echo "hash $1, expected $3, got $hash"
|
||||
|
||||
@@ -77,8 +77,23 @@ sed "s|^$|PATH='$PATH'|" < $NIX_DATA_DIR/nix/corepkgs/nar/nar.sh > tmp
|
||||
chmod +x tmp
|
||||
mv tmp $NIX_DATA_DIR/nix/corepkgs/nar/nar.sh
|
||||
|
||||
# An uberhack for Mac OS X 10.5: download-using-manifests uses Perl,
|
||||
# and Perl links against Darwin's libutil.dylib (in /usr/lib), but
|
||||
# when running "make check", the libtool wrapper script around the Nix
|
||||
# binaries sets DYLD_LIBRARY_PATH so that Perl finds Nix's (completely
|
||||
# different) libutil --- so it barfs. So generate a shell wrapper
|
||||
# around download-using-manifests that clears DYLD_LIBRARY_PATH.
|
||||
mv $NIX_BIN_DIR/nix/download-using-manifests.pl $NIX_BIN_DIR/nix/download-using-manifests.pl.real
|
||||
cat > $NIX_BIN_DIR/nix/download-using-manifests.pl <<EOF
|
||||
#! $SHELL -e
|
||||
export DYLD_LIBRARY_PATH=
|
||||
exec $NIX_BIN_DIR/nix/download-using-manifests.pl.real "\$@"
|
||||
EOF
|
||||
chmod +x $NIX_BIN_DIR/nix/download-using-manifests.pl
|
||||
|
||||
# Initialise the database.
|
||||
$nixstore --init
|
||||
|
||||
# Did anything happen?
|
||||
test -e "$NIX_DB_DIR"/validpaths
|
||||
test -e "$NIX_DB_DIR"/info
|
||||
test -e "$NIX_DB_DIR"/referrer
|
||||
|
||||
1
tests/lang/eval-okay-context.exp
Normal file
1
tests/lang/eval-okay-context.exp
Normal file
@@ -0,0 +1 @@
|
||||
Str("foo eval-okay-context.nix bar",[])
|
||||
6
tests/lang/eval-okay-context.nix
Normal file
6
tests/lang/eval-okay-context.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
let s = "foo ${builtins.substring 33 100 (baseNameOf ./eval-okay-context.nix)} bar";
|
||||
in
|
||||
if s == "foo eval-okay-context.nix bar"
|
||||
then abort "context not discarded"
|
||||
else builtins.unsafeDiscardStringContext s
|
||||
|
||||
1
tests/lang/eval-okay-ind-string.exp
Normal file
1
tests/lang/eval-okay-ind-string.exp
Normal file
@@ -0,0 +1 @@
|
||||
Str("This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\n",[])
|
||||
120
tests/lang/eval-okay-ind-string.nix
Normal file
120
tests/lang/eval-okay-ind-string.nix
Normal file
@@ -0,0 +1,120 @@
|
||||
let
|
||||
|
||||
s1 = ''
|
||||
This is an indented multi-line string
|
||||
literal. An amount of whitespace at
|
||||
the start of each line matching the minimum
|
||||
indentation of all lines in the string
|
||||
literal together will be removed. Thus,
|
||||
in this case four spaces will be
|
||||
stripped from each line, even though
|
||||
THIS LINE is indented six spaces.
|
||||
|
||||
Also, empty lines don't count in the
|
||||
determination of the indentation level (the
|
||||
previous empty line has indentation 0, but
|
||||
it doesn't matter).
|
||||
'';
|
||||
|
||||
s2 = '' If the string starts with whitespace
|
||||
followed by a newline, it's stripped, but
|
||||
that's not the case here. Two spaces are
|
||||
stripped because of the " " at the start.
|
||||
'';
|
||||
|
||||
s3 = ''
|
||||
This line is indented
|
||||
a bit further.
|
||||
''; # indentation of last line doesn't count if it's empty
|
||||
|
||||
s4 = ''
|
||||
Anti-quotations, like ${if true then "so" else "not so"}, are
|
||||
also allowed.
|
||||
'';
|
||||
|
||||
s5 = ''
|
||||
The \ is not special here.
|
||||
' can be followed by any character except another ', e.g. 'x'.
|
||||
Likewise for $, e.g. $$ or $varName.
|
||||
But ' followed by ' is special, as is $ followed by {.
|
||||
If you want them, use anti-quotations: ${"''"}, ${"\${"}.
|
||||
'';
|
||||
|
||||
s6 = ''
|
||||
Tabs are not interpreted as whitespace (since we can't guess
|
||||
what tab settings are intended), so don't use them.
|
||||
This line starts with a space and a tab, so only one
|
||||
space will be stripped from each line.
|
||||
'';
|
||||
|
||||
s7 = ''
|
||||
Also note that if the last line (just before the closing ' ')
|
||||
consists only of whitespace, it's ignored. But here there is
|
||||
some non-whitespace stuff, so the line isn't removed. '';
|
||||
|
||||
s8 = '' ${""}
|
||||
This shows a hacky way to preserve an empty line after the start.
|
||||
But there's no reason to do so: you could just repeat the empty
|
||||
line.
|
||||
'';
|
||||
|
||||
s9 = ''
|
||||
${""} Similarly you can force an indentation level,
|
||||
in this case to 2 spaces. This works because the anti-quote
|
||||
is significant (not whitespace).
|
||||
'';
|
||||
|
||||
s10 = ''
|
||||
'';
|
||||
|
||||
s11 = '''';
|
||||
|
||||
s12 = '' '';
|
||||
|
||||
s13 = ''
|
||||
start on network-interfaces
|
||||
|
||||
start script
|
||||
|
||||
rm -f /var/run/opengl-driver
|
||||
${if true
|
||||
then "ln -sf 123 /var/run/opengl-driver"
|
||||
else if true
|
||||
then "ln -sf 456 /var/run/opengl-driver"
|
||||
else ""
|
||||
}
|
||||
|
||||
rm -f /var/log/slim.log
|
||||
|
||||
end script
|
||||
|
||||
env SLIM_CFGFILE=${"abc"}
|
||||
env SLIM_THEMESDIR=${"def"}
|
||||
env FONTCONFIG_FILE=/etc/fonts/fonts.conf # !!! cleanup
|
||||
env XKB_BINDIR=${"foo"}/bin # Needed for the Xkb extension.
|
||||
env LD_LIBRARY_PATH=${"libX11"}/lib:${"libXext"}/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)
|
||||
|
||||
${if true
|
||||
then "env XORG_DRI_DRIVER_PATH=${"nvidiaDrivers"}/X11R6/lib/modules/drivers/"
|
||||
else if true
|
||||
then "env XORG_DRI_DRIVER_PATH=${"mesa"}/lib/modules/dri"
|
||||
else ""
|
||||
}
|
||||
|
||||
exec ${"slim"}/bin/slim
|
||||
'';
|
||||
|
||||
s14 = ''
|
||||
Escaping of ' followed by ': '''
|
||||
Escaping of $ followed by {: ''${
|
||||
And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t.
|
||||
'';
|
||||
|
||||
# Regression test: antiquotation in '${x}' should work, but didn't.
|
||||
s15 = let x = "bla"; in ''
|
||||
foo
|
||||
'${x}'
|
||||
bar
|
||||
'';
|
||||
|
||||
in s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15
|
||||
1
tests/lang/eval-okay-readfile.exp
Normal file
1
tests/lang/eval-okay-readfile.exp
Normal file
@@ -0,0 +1 @@
|
||||
Str("builtins.readFile ./eval-okay-readfile.nix\n",[])
|
||||
1
tests/lang/eval-okay-readfile.nix
Normal file
1
tests/lang/eval-okay-readfile.nix
Normal file
@@ -0,0 +1 @@
|
||||
builtins.readFile ./eval-okay-readfile.nix
|
||||
@@ -9,12 +9,50 @@ reference=$NIX_STORE_DIR/abcdef
|
||||
touch $reference
|
||||
(echo $reference && echo && echo 0) | $nixstore --register-validity
|
||||
|
||||
echo "registering..."
|
||||
time for ((n = 0; n < $max; n++)); do
|
||||
echo "making registration..."
|
||||
|
||||
for ((n = 0; n < $max; n++)); do
|
||||
storePath=$NIX_STORE_DIR/$n
|
||||
touch $storePath
|
||||
(echo $storePath && echo && echo 1 && echo $reference)
|
||||
done | $nixstore --register-validity
|
||||
ref2=$NIX_STORE_DIR/$((n+1))
|
||||
if test $((n+1)) = $max; then
|
||||
ref2=$reference
|
||||
fi
|
||||
(echo $storePath && echo && echo 2 && echo $reference && echo $ref2)
|
||||
done > $TEST_ROOT/reg_info
|
||||
|
||||
echo "registering..."
|
||||
|
||||
time $nixstore --register-validity < $TEST_ROOT/reg_info
|
||||
|
||||
oldTime=$(cat test-tmp/db/info/1 | grep Registered-At)
|
||||
|
||||
echo "sleeping..."
|
||||
|
||||
sleep 2
|
||||
|
||||
echo "reregistering..."
|
||||
|
||||
time $nixstore --register-validity --reregister < $TEST_ROOT/reg_info
|
||||
|
||||
newTime=$(cat test-tmp/db/info/1 | grep Registered-At)
|
||||
|
||||
if test "$newTime" != "$oldTime"; then
|
||||
echo "reregistration changed original registration time"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test "$(cat test-tmp/db/referrer/1 | wc -w)" != 1; then
|
||||
echo "reregistration duplicated referrers"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "collecting garbage..."
|
||||
time $nixstore --gc 2> /dev/null
|
||||
ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref
|
||||
time $nixstore --gc
|
||||
|
||||
if test "$(cat test-tmp/db/referrer/abcdef | wc -w)" != 0; then
|
||||
echo "referrers not cleaned up"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ if test "$text" != "Hello World!"; then exit 1; fi
|
||||
$nixstore --delete $outPath
|
||||
if test -e $outPath/hello; then false; fi
|
||||
|
||||
if test "$(NIX_STORE_DIR=/foo $nixinstantiate --readonly-mode hash-check.nix)" != "/foo/bbfambd3ksry4ylik1772pn2qyw1k296-dependencies.drv"; then
|
||||
echo "hashDerivationModulo appears broken"
|
||||
echo 'Hello World' > ./dummy
|
||||
outPath="$(NIX_STORE_DIR=/foo $nixinstantiate --readonly-mode hash-check.nix)"
|
||||
if test "$outPath" != "/foo/lfy1s6ca46rm5r6w4gg9hc0axiakjcnm-dependencies.drv"; then
|
||||
echo "hashDerivationModulo appears broken, got $outPath"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -2,6 +2,8 @@ source common.sh
|
||||
|
||||
clearProfiles
|
||||
|
||||
set -x
|
||||
|
||||
# Query installed: should be empty.
|
||||
test "$($nixenv -p $profiles/test -q '*' | wc -l)" -eq 0
|
||||
|
||||
@@ -71,6 +73,15 @@ echo $outPath10
|
||||
$nixenv -p $profiles/test -i "$outPath10"
|
||||
$nixenv -p $profiles/test -q '*' | grep -q foo-1.0
|
||||
|
||||
# Uninstall foo-1.0, using a symlink to its store path.
|
||||
ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink
|
||||
$nixenv -p $profiles/test -e $TEST_ROOT/symlink
|
||||
if $nixenv -p $profiles/test -q '*' | grep -q foo; then false; fi
|
||||
|
||||
# Install foo-1.0, now using a symlink to its store path.
|
||||
$nixenv -p $profiles/test -i $TEST_ROOT/symlink
|
||||
$nixenv -p $profiles/test -q '*' | grep -q foo
|
||||
|
||||
# Delete all old generations.
|
||||
$nixenv -p $profiles/test --delete-generations old
|
||||
|
||||
|
||||
Reference in New Issue
Block a user