Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d6418d46e | ||
|
|
dec2f19502 | ||
|
|
6123144933 | ||
|
|
1dacd427cd | ||
|
|
ea6bf0c21f | ||
|
|
36e67ff16b | ||
|
|
fba17a9043 | ||
|
|
2d9bb56e55 | ||
|
|
5bc41d78ff | ||
|
|
9e4bb20455 | ||
|
|
dc341811d6 | ||
|
|
69befd33a9 | ||
|
|
a5684e09d3 | ||
|
|
411a3461dc | ||
|
|
05d02f798f | ||
|
|
6da92d96ae | ||
|
|
543d8a5942 | ||
|
|
fe95650487 | ||
|
|
3139481822 | ||
|
|
c086183843 | ||
|
|
4f4a14453a | ||
|
|
4ea034a5c5 | ||
|
|
792fd51f41 | ||
|
|
f440558acc | ||
|
|
bb659bad81 | ||
|
|
f6a8e7f4c2 | ||
|
|
b08f4b0da9 | ||
|
|
d7625b5c2d | ||
|
|
b8034e5581 | ||
|
|
9d8a80375d | ||
|
|
d6a7aa8f48 | ||
|
|
b8571d68c4 | ||
|
|
a737f51fd9 | ||
|
|
ff02f5336c | ||
|
|
4bd5282573 | ||
|
|
bce14d0f61 | ||
|
|
7cdefdbe73 | ||
|
|
d05bf04444 | ||
|
|
c1994fecf9 | ||
|
|
672c3acc71 | ||
|
|
7bdb85453d | ||
|
|
9deb822180 | ||
|
|
b1e3b1a4ac | ||
|
|
6b47de580f | ||
|
|
a5e0f64db3 | ||
|
|
221a2daf34 | ||
|
|
176c666f36 | ||
|
|
c945f015de | ||
|
|
28e0742966 | ||
|
|
a5fb4b5b7c | ||
|
|
faaae44f2e | ||
|
|
f53574ebd6 | ||
|
|
d5529f5b85 | ||
|
|
b072fc04a7 | ||
|
|
fecad91b67 | ||
|
|
0220da3e10 | ||
|
|
936f9d45ba | ||
|
|
4b83830d0c | ||
|
|
5904262640 | ||
|
|
2c1ecf8e81 | ||
|
|
88c07341a6 | ||
|
|
07a08bddf0 | ||
|
|
c57ed84e28 | ||
|
|
ef4f5ba85e | ||
|
|
06bb2d95b4 | ||
|
|
6f809194d7 | ||
|
|
57d18df7d0 | ||
|
|
92077b4547 | ||
|
|
33972629d7 | ||
|
|
ac1b754138 | ||
|
|
c28dfc0305 | ||
|
|
e9b92169a5 | ||
|
|
ecd830b3b9 | ||
|
|
6ec8dab06a | ||
|
|
b29d3f4aee | ||
|
|
efe4289464 | ||
|
|
afc6c1bad6 | ||
|
|
6cd6ce5608 | ||
|
|
34bb806f74 | ||
|
|
c6c024ca6f | ||
|
|
03eaef3d7a | ||
|
|
b3110a15e9 | ||
|
|
25a108bb9c | ||
|
|
d308aeaf53 | ||
|
|
297b762513 | ||
|
|
46222bbc43 | ||
|
|
58204a3c39 | ||
|
|
3fb7ae0586 | ||
|
|
161a2ccf7a | ||
|
|
a583a2bc59 | ||
|
|
263d668222 | ||
|
|
fd7d979c79 | ||
|
|
46ffcce0c3 | ||
|
|
8e74c0bfd1 | ||
|
|
3d77b28eac | ||
|
|
47701677e8 | ||
|
|
5d147e125c | ||
|
|
159e621d1a | ||
|
|
511455965e | ||
|
|
7df4ef983e | ||
|
|
0a470fc345 | ||
|
|
8ae6d55db1 | ||
|
|
e068f49f7d | ||
|
|
20866a7031 | ||
|
|
70e68e0ec6 | ||
|
|
e87d1a63bd | ||
|
|
7cf539c728 | ||
|
|
7b09e9f2c4 | ||
|
|
15e5ac8039 | ||
|
|
48858ad5ca | ||
|
|
2bc5de8635 | ||
|
|
dc5f2e7da6 | ||
|
|
a4921b8ceb | ||
|
|
16591eb3cc | ||
|
|
c3f5413e80 | ||
|
|
aeb810b01e | ||
|
|
25a00cae5b | ||
|
|
620d57f036 | ||
|
|
6fcc290619 | ||
|
|
ed09d0c0a4 | ||
|
|
656390062a | ||
|
|
212e96f39c | ||
|
|
b584a42e3d | ||
|
|
5116214343 | ||
|
|
7986711632 | ||
|
|
1917d750a0 | ||
|
|
7ccd946407 | ||
|
|
5558652709 | ||
|
|
1906cce6fc | ||
|
|
9b11165aec | ||
|
|
22144afa8d | ||
|
|
2b29e4b852 |
@@ -27,11 +27,9 @@ init-state:
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots
|
||||
ln -sfn $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool
|
||||
-$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(storedir)
|
||||
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests
|
||||
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
|
||||
|
||||
else
|
||||
|
||||
|
||||
+7
-1
@@ -68,6 +68,8 @@ if test "$sys_name" = sunos; then
|
||||
fi
|
||||
|
||||
|
||||
CFLAGS=${CFLAGS:--g -O3 -Wall}
|
||||
CXXFLAGS=${CXXFLAGS:--g -O3 -Wall}
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
@@ -125,6 +127,10 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
AC_CHECK_FUNCS([lutimes])
|
||||
|
||||
|
||||
# Check for sched_setaffinity.
|
||||
AC_CHECK_FUNCS([sched_setaffinity])
|
||||
|
||||
|
||||
# Check whether the store optimiser can optimise symlinks.
|
||||
AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
|
||||
ln -s bla tmp_link
|
||||
@@ -183,10 +189,10 @@ NEED_PROG(perl, perl)
|
||||
NEED_PROG(sed, sed)
|
||||
NEED_PROG(tar, tar)
|
||||
NEED_PROG(bzip2, bzip2)
|
||||
NEED_PROG(gzip, gzip)
|
||||
NEED_PROG(xz, xz)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(dblatex, dblatex)
|
||||
AC_PATH_PROG(gzip, gzip)
|
||||
AC_PATH_PROG(pv, pv, pv)
|
||||
|
||||
|
||||
|
||||
@@ -36,9 +36,6 @@ sub createLinks {
|
||||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
||||
$srcFile =~ /\/nix-support$/ ||
|
||||
$srcFile =~ /\/perllocal.pod$/ ||
|
||||
$srcFile =~ /\/easy-install.pth$/ ||
|
||||
$srcFile =~ /\/site.py$/ ||
|
||||
$srcFile =~ /\/site.pyc$/ ||
|
||||
$srcFile =~ /\/info\/dir$/ ||
|
||||
$srcFile =~ /\/log$/)
|
||||
{
|
||||
@@ -56,7 +53,7 @@ sub createLinks {
|
||||
elsif (-l _) {
|
||||
my $target = readlink $dstFile or die;
|
||||
if (!-d $target) {
|
||||
die "collission between directory `$srcFile' and non-directory `$target'";
|
||||
die "collision between directory `$srcFile' and non-directory `$target'";
|
||||
}
|
||||
unlink $dstFile or die "error unlinking `$dstFile': $!";
|
||||
mkdir $dstFile, 0755 ||
|
||||
@@ -78,7 +75,7 @@ sub createLinks {
|
||||
if (-l $dstFile) {
|
||||
my $target = readlink $dstFile;
|
||||
my $prevPriority = $priorities{$dstFile};
|
||||
die ( "collission between `$srcFile' and `$target'; "
|
||||
die ( "collision between `$srcFile' and `$target'; "
|
||||
. "use `nix-env --set-flag "
|
||||
. "priority NUMBER PKGNAME' to change the priority of "
|
||||
. "one of the conflicting packages\n" )
|
||||
|
||||
@@ -7,6 +7,7 @@ in {
|
||||
shell = "@shell@";
|
||||
coreutils = "@coreutils@";
|
||||
bzip2 = "@bzip2@";
|
||||
gzip = "@gzip@";
|
||||
xz = "@xz@";
|
||||
tar = "@tar@";
|
||||
tarFlags = "@tarFlags@";
|
||||
|
||||
+14
-8
@@ -6,25 +6,28 @@ let
|
||||
''
|
||||
export PATH=${nixBinDir}:${coreutils}
|
||||
|
||||
if [ $compressionType = "xz" ]; then
|
||||
ext=xz
|
||||
compressor="${xz} -9"
|
||||
if [ $compressionType = xz ]; then
|
||||
ext=.xz
|
||||
compressor="| ${xz} -7"
|
||||
elif [ $compressionType = bzip2 ]; then
|
||||
ext=.bz2
|
||||
compressor="| ${bzip2}"
|
||||
else
|
||||
ext=bz2
|
||||
compressor="${bzip2}"
|
||||
ext=
|
||||
compressor=
|
||||
fi
|
||||
|
||||
echo "packing ‘$storePath’..."
|
||||
mkdir $out
|
||||
dst=$out/tmp.nar.$ext
|
||||
dst=$out/tmp.nar$ext
|
||||
|
||||
set -o pipefail
|
||||
nix-store --dump "$storePath" | $compressor > $dst
|
||||
eval "nix-store --dump \"$storePath\" $compressor > $dst"
|
||||
|
||||
hash=$(nix-hash --flat --type $hashAlgo --base32 $dst)
|
||||
echo -n $hash > $out/nar-compressed-hash
|
||||
|
||||
mv $dst $out/$hash.nar.$ext
|
||||
mv $dst $out/$hash.nar$ext
|
||||
'';
|
||||
|
||||
in
|
||||
@@ -40,4 +43,7 @@ derivation {
|
||||
|
||||
# Don't build in a chroot because Nix's dependencies may not be there.
|
||||
__noChroot = true;
|
||||
|
||||
# Remote machines may not have ${nixBinDir} or ${coreutils} in the same prefixes
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@ let
|
||||
''
|
||||
mkdir $out
|
||||
cd $out
|
||||
pat="\.xz\$"
|
||||
if [[ "$src" =~ $pat ]]; then
|
||||
xzpat="\.xz\$"
|
||||
gzpat="\.gz\$"
|
||||
if [[ "$src" =~ $xzpat ]]; then
|
||||
${xz} -d < $src | ${tar} xf - ${tarFlags}
|
||||
elif [[ "$src" =~ $gzpat ]]; then
|
||||
${gzip} -d < $src | ${tar} xf - ${tarFlags}
|
||||
else
|
||||
${bzip2} -d < $src | ${tar} xf - ${tarFlags}
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#! /bin/sh
|
||||
if [ -e tests/test-tmp ]; then
|
||||
chmod -R u+w tests/test-tmp
|
||||
rm -rf tests/test-tmp
|
||||
fi
|
||||
|
||||
s=$(type -p nix-shell)
|
||||
exec $s release.nix -A tarball --command "
|
||||
export NIX_REMOTE=daemon
|
||||
export NIX_PATH='$NIX_PATH'
|
||||
export NIX_BUILD_SHELL=$(type -p bash)
|
||||
export c=\$configureFlags
|
||||
exec $s release.nix -A build.x86_64-linux --exclude tarball --command '
|
||||
configureFlags+=\" \$c --prefix=$(pwd)/inst --sysconfdir=$(pwd)/inst/etc\"
|
||||
return
|
||||
'" \
|
||||
"$@"
|
||||
@@ -16,7 +16,7 @@ dblatex_opts = \
|
||||
# Note: we use GIF for now, since the PNGs shipped with Docbook aren't
|
||||
# transparent.
|
||||
|
||||
man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
|
||||
man1_MANS = nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \
|
||||
nix-collect-garbage.1 nix-push.1 nix-pull.1 \
|
||||
nix-prefetch-url.1 nix-channel.1 \
|
||||
nix-install-package.1 nix-hash.1 nix-copy-closure.1
|
||||
|
||||
+56
-43
@@ -12,9 +12,9 @@ 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>
|
||||
built-in value, which is a set that contains all built-in functions
|
||||
and values. For instance, <function>derivation</function> is also
|
||||
available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
|
||||
<variablelist>
|
||||
@@ -39,17 +39,17 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.attrNames</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
<replaceable>set</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:
|
||||
<listitem><para>Return the names of the attributes in the set
|
||||
<replaceable>set</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>
|
||||
attrValues = set: map (name: builtins.getAttr name set) (builtins.attrNames set);</programlisting>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
@@ -68,8 +68,8 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
|
||||
<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
|
||||
<listitem><para>The 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.,
|
||||
|
||||
@@ -258,11 +258,11 @@ stdenv.mkDerivation {
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
<replaceable>s</replaceable> <replaceable>set</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
|
||||
named <replaceable>s</replaceable> from
|
||||
<replaceable>set</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>
|
||||
@@ -289,15 +289,15 @@ stdenv.mkDerivation {
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hasAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
<replaceable>s</replaceable> <replaceable>set</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>
|
||||
<literal>true</literal> if <replaceable>set</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>
|
||||
|
||||
@@ -331,12 +331,12 @@ stdenv.mkDerivation {
|
||||
<listitem><para>Load, parse and return the Nix expression in the
|
||||
file <replaceable>path</replaceable>. If <replaceable>path
|
||||
</replaceable> is a directory, the file <filename>default.nix
|
||||
</filename> in that directory is loaded. 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>
|
||||
</filename> in that directory is loaded. 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 a 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
|
||||
@@ -383,9 +383,9 @@ x: x + 456</programlisting>
|
||||
<varlistentry><term><function>builtins.intersectAttrs</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return an attribute set consisting of the
|
||||
attributes in the set <replaceable>e2</replaceable> that also
|
||||
exist in the set <replaceable>e1</replaceable>.</para></listitem>
|
||||
<listitem><para>Return a set consisting of the attributes in the
|
||||
set <replaceable>e2</replaceable> that also exist in the set
|
||||
<replaceable>e1</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -394,7 +394,7 @@ x: x + 456</programlisting>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to an attribute set, and
|
||||
<replaceable>e</replaceable> evaluates to a set, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -434,7 +434,7 @@ x: x + 456</programlisting>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a int, and
|
||||
<replaceable>e</replaceable> evaluates to an int, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -490,9 +490,9 @@ x: x + 456</programlisting>
|
||||
<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
|
||||
<listitem><para>Construct a set from a list specifying the names
|
||||
and values of each attribute. Each element of the list should be
|
||||
a 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:
|
||||
@@ -547,7 +547,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
a package name and version. The package name is everything up to
|
||||
but not including the first dash followed by a digit, and the
|
||||
version is everything following that dash. The result is returned
|
||||
in an attribute set <literal>{ name, version }</literal>. Thus,
|
||||
in a set <literal>{ name, version }</literal>. Thus,
|
||||
<literal>builtins.parseDrvName "nix-0.12pre12876"</literal>
|
||||
returns <literal>{ name = "nix"; version = "0.12pre12876";
|
||||
}</literal>.</para></listitem>
|
||||
@@ -598,12 +598,12 @@ in config.someSetting</programlisting>
|
||||
|
||||
|
||||
<varlistentry><term><function>removeAttrs</function>
|
||||
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
||||
<replaceable>set</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,
|
||||
<replaceable>list</replaceable> from
|
||||
<replaceable>set</replaceable>. The attributes don’t have to
|
||||
exist in <replaceable>set</replaceable>. For instance,
|
||||
|
||||
<screen>
|
||||
removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
@@ -792,7 +792,7 @@ in foo</programlisting>
|
||||
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
|
||||
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
|
||||
@@ -901,6 +901,19 @@ stdenv.mkDerivation (rec {
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.typeOf</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return a string representing the type of the value
|
||||
<replaceable>e</replaceable>, namely <literal>"int"</literal>,
|
||||
<literal>"bool"</literal>, <literal>"string"</literal>,
|
||||
<literal>"path"</literal>, <literal>"null"</literal>,
|
||||
<literal>"set"</literal>, <literal>"list"</literal> or
|
||||
<literal>"lambda"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
+28
-12
@@ -101,8 +101,8 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
<listitem><para>This option defines the maximum number of jobs
|
||||
that Nix will try to build in parallel. The default is
|
||||
<literal>1</literal>. You should generally set it to the number
|
||||
of CPUs in your system (e.g., <literal>2</literal> on a Athlon 64
|
||||
X2). It can be overriden using the <option
|
||||
of CPUs in your system (e.g., <literal>2</literal> on an Athlon 64
|
||||
X2). It can be overridden using the <option
|
||||
linkend='opt-max-jobs'>--max-jobs</option> (<option>-j</option>)
|
||||
command line switch.</para></listitem>
|
||||
|
||||
@@ -119,7 +119,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
<varname>enableParallelBuilding</varname> is set to
|
||||
<literal>true</literal>, the builder passes the
|
||||
<option>-j<replaceable>N</replaceable></option> flag to GNU Make.
|
||||
It can be overriden using the <option
|
||||
It can be overridden using the <option
|
||||
linkend='opt-cores'>--cores</option> command line switch and
|
||||
defaults to <literal>1</literal>. The value <literal>0</literal>
|
||||
means that the builder should use all available CPU cores in the
|
||||
@@ -134,10 +134,10 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
<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
|
||||
standard error. This is useful (for instance in an 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
|
||||
problems. It can be overridden using the <option
|
||||
linkend="opt-max-silent-time">--max-silent-time</option> command
|
||||
line switch.</para>
|
||||
|
||||
@@ -148,15 +148,16 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This option defines the maximum number of seconds that a
|
||||
builder can run. This is useful (for instance in a automated
|
||||
builder can run. This is useful (for instance in an automated
|
||||
build system) to catch builds that are stuck in an infinite loop
|
||||
but keep writing to their standard output or standard error. It
|
||||
can be overriden using the <option
|
||||
can be overridden using the <option
|
||||
linkend="opt-timeout">--timeout</option> command line
|
||||
switch.</para>
|
||||
|
||||
@@ -168,6 +169,20 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-log-size"><term><literal>build-max-log-size</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This option defines the maximum number of bytes that a
|
||||
builder can write to its stdout/stderr. If the builder exceeds
|
||||
this limit, it’s killed. A value of <literal>0</literal> (the
|
||||
default) means that there is no limit.</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
|
||||
@@ -425,12 +440,13 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
<varlistentry><term><literal>auto-optimise-store</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
Nix automatically detects files in the store that have identical
|
||||
<listitem><para>If set to <literal>true</literal>, Nix
|
||||
automatically detects files in the store that have identical
|
||||
contents, and replaces them with hard links to a single copy.
|
||||
This saves disk space. If set to <literal>false</literal>, you
|
||||
can still run <command>nix-store --optimise</command> to get rid
|
||||
of duplicate files.</para></listitem>
|
||||
This saves disk space. If set to <literal>false</literal> (the
|
||||
default), you can still run <command>nix-store
|
||||
--optimise</command> to get rid of duplicate
|
||||
files.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<varlistentry xml:id="envar-other-stores"><term><envar>NIX_OTHER_STORES</envar></term>
|
||||
|
||||
<listitem><para>This variable contains the paths of remote Nix
|
||||
installations from whichs paths can be copied, separated by colons.
|
||||
installations from which packages can be copied, separated by colons.
|
||||
<phrase condition="manual">See <xref linkend="sec-sharing-packages"
|
||||
/> for details.</phrase> Each path should be the
|
||||
<filename>/nix</filename> directory of a remote Nix installation
|
||||
@@ -312,6 +312,24 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>NIX_SHOW_STATS</envar></term>
|
||||
|
||||
<listitem><para>If set to <literal>1</literal>, Nix will print some
|
||||
evaluation statistics, such as the number of values
|
||||
allocated.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>NIX_COUNT_CALLS</envar></term>
|
||||
|
||||
<listitem><para>If set to <literal>1</literal>, Nix will print how
|
||||
often functions were called during Nix expression evaluation. This
|
||||
is useful for profiling your Nix expressions.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>GC_INITIAL_HEAP_SIZE</envar></term>
|
||||
|
||||
<listitem><para>If Nix has been configured to use the Boehm garbage
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,41 @@
|
||||
<appendix xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:id="chap-hacking">
|
||||
|
||||
<title>Hacking</title>
|
||||
|
||||
<para>This section provides some notes on how to hack on Nix. To get
|
||||
the latest version of Nix from GitHub:
|
||||
<screen>
|
||||
$ git clone git://github.com/NixOS/nix.git
|
||||
$ cd hydra
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>To build it and its dependencies:
|
||||
<screen>
|
||||
$ nix-build release.nix -A build.x86_64-linux
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>To build all dependencies and start a shell in which all
|
||||
environment variables are set up so that those dependencies can be
|
||||
found:
|
||||
<screen>
|
||||
$ ./dev-shell
|
||||
</screen>
|
||||
To build Nix itself in this shell:
|
||||
<screen>
|
||||
[nix-shell]$ ./bootstrap
|
||||
[nix-shell]$ configurePhase
|
||||
[nix-shell]$ make
|
||||
</screen>
|
||||
To test it:
|
||||
<screen>
|
||||
[nix-shell]$ make install
|
||||
[nix-shell]$ make installcheck
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</appendix>
|
||||
@@ -48,7 +48,7 @@ Debian, Ubuntu, Mac OS X and various other systems from the <link
|
||||
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
|
||||
You can also get builds of the latest development release from our
|
||||
<link
|
||||
xlink:href="http://hydra.nixos.org/view/nix/trunk/latest">continuous
|
||||
xlink:href="http://hydra.nixos.org/job/nix/trunk/release/latest-finished#tabs-constituents">continuous
|
||||
build system</link>.</para>
|
||||
|
||||
<para>For Fedora, RPM packages are available. These can be installed
|
||||
@@ -188,7 +188,7 @@ a source distribution.</para>
|
||||
downloaded from the <link
|
||||
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
|
||||
You can also grab the <link
|
||||
xlink:href="http://hydra.nixos.org/view/nix/trunk/latest/tarball/download-by-type/file/source-dist">most
|
||||
xlink:href="http://hydra.nixos.org/job/nix/trunk/release/latest-finished#tabs-constituents">most
|
||||
recent development release</link>.</para>
|
||||
|
||||
<para>Alternatively, the most recent sources of Nix can be obtained
|
||||
|
||||
+10
-8
@@ -19,15 +19,15 @@
|
||||
</author>
|
||||
|
||||
<copyright>
|
||||
<year>2004-2012</year>
|
||||
<year>2004-2013</year>
|
||||
<holder>Eelco Dolstra</holder>
|
||||
</copyright>
|
||||
|
||||
<date>May 2012</date>
|
||||
|
||||
<date>July 2013</date>
|
||||
|
||||
</info>
|
||||
|
||||
|
||||
|
||||
<xi:include href="introduction.xml" />
|
||||
<xi:include href="quick-start.xml" />
|
||||
<xi:include href="installation.xml" />
|
||||
@@ -40,17 +40,18 @@
|
||||
<title>Command Reference</title>
|
||||
<xi:include href="opt-common.xml" />
|
||||
<xi:include href="env-common.xml" />
|
||||
|
||||
|
||||
<section>
|
||||
<title>Main commands</title>
|
||||
<xi:include href="nix-env.xml" />
|
||||
<xi:include href="nix-instantiate.xml" />
|
||||
<xi:include href="nix-store.xml" />
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>Utilities</title>
|
||||
<xi:include href="nix-build.xml" />
|
||||
<xi:include href="nix-shell.xml" />
|
||||
<xi:include href="nix-channel.xml" />
|
||||
<xi:include href="nix-collect-garbage.xml" />
|
||||
<xi:include href="nix-copy-closure.xml" />
|
||||
@@ -66,17 +67,18 @@
|
||||
<title>Files</title>
|
||||
<xi:include href="conf-file.xml" />
|
||||
</section>
|
||||
|
||||
|
||||
</appendix>
|
||||
|
||||
<xi:include href="troubleshooting.xml" />
|
||||
<!-- <xi:include href="bugs.xml" /> -->
|
||||
<xi:include href="glossary.xml" />
|
||||
<xi:include href="hacking.xml" />
|
||||
|
||||
<appendix>
|
||||
<title>Nix Release Notes</title>
|
||||
<xi:include href="release-notes.xml"
|
||||
xpointer="xmlns(x=http://docbook.org/ns/docbook)xpointer(x:article/x:section)" />
|
||||
</appendix>
|
||||
|
||||
|
||||
</book>
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
</group>
|
||||
<replaceable>outlink</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<option>--run-env</option>
|
||||
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
|
||||
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
|
||||
</arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@@ -75,13 +70,6 @@ a root of the Nix garbage collector. This root disappears
|
||||
automatically when the <filename>result</filename> symlink is deleted
|
||||
or renamed. So don’t rename the symlink.</para></warning>
|
||||
|
||||
<para>The subcommand <command>nix-build --run-env</command> will build
|
||||
the dependencies of the derivation, but not the derivation itself. It
|
||||
will then start an interactive shell in which all environment
|
||||
variables defined by the derivation have been set to their
|
||||
corresponding values. This is useful for reproducing the environment
|
||||
of a derivation for development.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -134,32 +122,12 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>The following common options are supported:</para>
|
||||
|
||||
<variablelist condition="manpage">
|
||||
<xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
<para>The following options apply to <command>nix-build --run-env</command>.</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
|
||||
|
||||
<listitem><para>In the environment of the derivation, executeq the
|
||||
command <replaceable>cmd</replaceable> instead of the default
|
||||
interactive shell.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
|
||||
|
||||
<listitem><para>Do not build any dependencies whose store path
|
||||
matches the regular expression <replaceable>regexp</replaceable>.
|
||||
This option may be specified multiple times.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -176,20 +144,6 @@ lrwxrwxrwx <replaceable>...</replaceable> result -> /nix/store/d18hyl92g30l...
|
||||
$ ls ./result/bin/
|
||||
firefox firefox-config</screen>
|
||||
|
||||
<para>To build the dependencies of the package Pan, and start an
|
||||
interactive shell in which to build it:
|
||||
|
||||
<screen>
|
||||
$ nix-build '<nixpkgs>' --run-env -A pan
|
||||
$ tar xf $src
|
||||
$ cd pan-*
|
||||
$ ./configure
|
||||
$ make
|
||||
$ ./pan/gui/pan
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If a derivation has multiple outputs,
|
||||
<command>nix-build</command> will build the default (first) output.
|
||||
You can also build all outputs:
|
||||
|
||||
+22
-9
@@ -153,11 +153,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
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
|
||||
single 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>
|
||||
{
|
||||
@@ -209,6 +208,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
<arg choice='plain'><option>--preserve-installed</option></arg>
|
||||
<arg choice='plain'><option>-P</option></arg>
|
||||
</group>
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--remove-all</option></arg>
|
||||
<arg choice='plain'><option>-r</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
@@ -272,7 +275,7 @@ number of possible ways:
|
||||
linkend="ss-functions">functions</link> that are called with the
|
||||
active Nix expression as their single argument. The derivations
|
||||
returned by those function calls are installed. This allows
|
||||
derivations to be specified in a unambiguous way, which is necessary
|
||||
derivations to be specified in an unambiguous way, which is necessary
|
||||
if there are multiple derivations with the same
|
||||
name.</para></listitem>
|
||||
|
||||
@@ -318,6 +321,16 @@ number of possible ways:
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--remove-all</option></term>
|
||||
<term><option>-r</option></term>
|
||||
|
||||
<listitem><para>Remove all previously installed packages first.
|
||||
This is equivalent to running <literal>nix-env -e '*'</literal>
|
||||
first, except that everything happens in a single
|
||||
transaction.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
@@ -391,7 +404,7 @@ $ nix-env -f ./foo.nix -i -E \
|
||||
I.e., this evaluates to <literal>(f: (f {system =
|
||||
"i686-linux";}).subversionWithJava) (import ./foo.nix)</literal>, thus
|
||||
selecting the <literal>subversionWithJava</literal> attribute from the
|
||||
attribute set returned by calling the function defined in
|
||||
set returned by calling the function defined in
|
||||
<filename>./foo.nix</filename>.</para>
|
||||
|
||||
<para>A dry-run tells you which paths will be downloaded or built from
|
||||
@@ -692,7 +705,7 @@ 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'
|
||||
collision 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>
|
||||
|
||||
@@ -1244,7 +1257,7 @@ wrapper around <option>--list-generations</option> and
|
||||
$ nix-env --rollback
|
||||
switching from generation 92 to 91
|
||||
|
||||
$ nix-env --rolback
|
||||
$ nix-env --rollback
|
||||
error: no generation older than the current (91) exists</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<command>nix-push</command>
|
||||
<arg choice='plain'><option>--dest</option> <replaceable>dest-dir</replaceable></arg>
|
||||
<arg><option>--bzip2</option></arg>
|
||||
<arg><option>--none</option></arg>
|
||||
<arg><option>--force</option></arg>
|
||||
<arg><option>--link</option></arg>
|
||||
<arg><option>--manifest</option></arg>
|
||||
@@ -106,6 +107,12 @@ automatically.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--none</option></term>
|
||||
|
||||
<listitem><para>Do not compress NARs.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--force</option></term>
|
||||
|
||||
<listitem><para>Overwrite <filename>.narinfo</filename> files if
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-shell">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-shell</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo class="source">Nix</refmiscinfo>
|
||||
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-shell</refname>
|
||||
<refpurpose>start an interactive shell based on a Nix expression</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-shell</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>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
<replaceable>attrPath</replaceable>
|
||||
</arg>
|
||||
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
|
||||
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
|
||||
<arg><option>--pure</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-shell</command> will build
|
||||
the dependencies of the specified derivation, but not the derivation
|
||||
itself. It will then start an interactive shell in which all
|
||||
environment variables defined by the derivation have been set to their
|
||||
corresponding values, and the script <literal>$stdenv/setup</literal>
|
||||
has been sourced. This is useful for reproducing the environment of a
|
||||
derivation for development.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<para>All options not listed here are passed to <command>nix-store
|
||||
--realise</command>, except for <option>--arg</option> and
|
||||
<option>--attr</option> / <option>-A</option> which are passed to
|
||||
<command>nix-instantiate</command>. <phrase condition="manual">See
|
||||
also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
|
||||
|
||||
<listitem><para>In the environment of the derivation, run the
|
||||
shell command <replaceable>cmd</replaceable> instead of starting
|
||||
an interactive shell. However, if you end the shell command with
|
||||
<literal>return</literal>, you still get an interactive shell.
|
||||
This can be useful for doing any additional
|
||||
initialisation.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
|
||||
|
||||
<listitem><para>Do not build any dependencies whose store path
|
||||
matches the regular expression <replaceable>regexp</replaceable>.
|
||||
This option may be specified multiple times.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--pure</option></term>
|
||||
|
||||
<listitem><para>If this flag is specified, the environment is
|
||||
almost entirely cleared before the interactive shell is started,
|
||||
so you get an environment that more closely corresponds to the
|
||||
“real” Nix build. A few variables, in particular
|
||||
<envar>HOME</envar>, <envar>USER</envar> and
|
||||
<envar>DISPLAY</envar>, are retained. Note that
|
||||
<filename>~/.bashrc</filename> and (depending on your Bash
|
||||
installation) <filename>/etc/bashrc</filename> are still sourced,
|
||||
so any variables set there will affect the interactive
|
||||
shell.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>The following common options are supported:</para>
|
||||
|
||||
<variablelist condition="manpage">
|
||||
<xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To build the dependencies of the package Pan, and start an
|
||||
interactive shell in which to build it:
|
||||
|
||||
<screen>
|
||||
$ nix-shell '<nixpkgs>' -A pan
|
||||
$ unpackPhase
|
||||
$ cd pan-*
|
||||
$ configurePhase
|
||||
$ buildPhase
|
||||
$ ./pan/gui/pan
|
||||
</screen>
|
||||
|
||||
To clear the environment first, and do some additional automatic
|
||||
initialisation of the interactive shell:
|
||||
|
||||
<screen>
|
||||
$ nix-shell '<nixpkgs>' -A pan --pure \
|
||||
--command 'export NIX_DEBUG=1; export NIX_CORES=8; return'
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection condition="manpage"><title>Environment variables</title>
|
||||
|
||||
<variablelist>
|
||||
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
@@ -164,7 +164,7 @@ the specified store paths. Realisation is a somewhat overloaded term:
|
||||
exist in the file system). If the path is already valid, we are
|
||||
done immediately. Otherwise, the path and any missing paths in its
|
||||
closure may be produced through substitutes. If there are no
|
||||
(succesful) subsitutes, realisation fails.</para></listitem>
|
||||
(successful) subsitutes, realisation fails.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
@@ -1242,9 +1242,9 @@ variable <envar>_args</envar>.</para>
|
||||
$ nix-store --print-env $(nix-instantiate '<nixpkgs>' -A firefox)
|
||||
<replaceable>…</replaceable>
|
||||
export src; src='/nix/store/plpj7qrwcz94z2psh6fchsi7s8yihc7k-firefox-12.0.source.tar.bz2'
|
||||
export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnm-stdenv'
|
||||
export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnn-stdenv'
|
||||
export system; system='x86_64-linux'
|
||||
export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh'
|
||||
export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25c-default-builder.sh'
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
from, say, a network repository. If the repository is down, the
|
||||
realisation of the derivation will fail. When this option is
|
||||
specified, Nix will build the derivation instead. Thus,
|
||||
installation from binaries falls back on nstallation from source.
|
||||
installation from binaries falls back on installation from source.
|
||||
This option is not the default since it is generally not desirable
|
||||
for a transient failure in obtaining the substitutes to lead to a
|
||||
full build from source (with the related consumption of
|
||||
@@ -236,7 +236,7 @@
|
||||
interpreted by the <command>nix-log2xml</command> tool in the
|
||||
Nix source distribution. The resulting XML file can be fed into
|
||||
the <command>log2html.xsl</command> stylesheet to create an HTML
|
||||
file that can be browsed interactively, using Javascript to
|
||||
file that can be browsed interactively, using JavaScript to
|
||||
expand and collapse parts of the output.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -129,7 +129,7 @@ downloading binaries from <systemitem
|
||||
class='fqdomainname'>nixos.org</systemitem>, instead of building
|
||||
them from source. This might still take a while since all
|
||||
dependencies must be downloaded, but on a reasonably fast connection
|
||||
such as an DSL line it’s on the order of a few minutes.</para>
|
||||
such as a DSL line it’s on the order of a few minutes.</para>
|
||||
|
||||
<para>Naturally, packages can also be uninstalled:
|
||||
|
||||
@@ -225,7 +225,7 @@ uses is to create directory trees of symlinks to
|
||||
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
|
||||
environment <filename>/nix/store/5mq2jcn36ldl...-user-env</filename>
|
||||
environment <filename>/nix/store/0c1p5z4kda11...-user-env</filename>
|
||||
contains a symlink to just Subversion 1.1.2 (arrows in the figure
|
||||
indicate symlinks). This would be what we would obtain if we had done
|
||||
|
||||
|
||||
@@ -5,6 +5,197 @@
|
||||
<title>Nix Release Notes</title>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.6.1"><title>Release 1.6.1 (October 28, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. Changes of interest
|
||||
are:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Nix 1.6 accidentally changed the semantics of antiquoted
|
||||
paths in strings, such as <literal>"${/foo}/bar"</literal>. This
|
||||
release reverts to the Nix 1.5.3 behaviour.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Previously, Nix optimised expressions such as
|
||||
<literal>"${<replaceable>expr</replaceable>}"</literal> to
|
||||
<replaceable>expr</replaceable>. Thus it neither checked whether
|
||||
<replaceable>expr</replaceable> could be coerced to a string, nor
|
||||
applied such coercions. This meant that
|
||||
<literal>"${123}"</literal> evaluatued to <literal>123</literal>,
|
||||
and <literal>"${./foo}"</literal> evaluated to
|
||||
<literal>./foo</literal> (even though
|
||||
<literal>"${./foo} "</literal> evaluates to
|
||||
<literal>"/nix/store/<replaceable>hash</replaceable>-foo "</literal>).
|
||||
Nix now checks the type of antiquoted expressions and
|
||||
applies coercions.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now shows the exact position of undefined variables. In
|
||||
particular, undefined variable errors in a <literal>with</literal>
|
||||
previously didn't show <emphasis>any</emphasis> position
|
||||
information, so this makes it a lot easier to fix such
|
||||
errors.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Undefined variables are now treated consistently.
|
||||
Previously, the <function>tryEval</function> function would catch
|
||||
undefined variables inside a <literal>with</literal> but not
|
||||
outside. Now <function>tryEval</function> never catches undefined
|
||||
variables.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Bash completion in <command>nix-shell</command> now works
|
||||
correctly.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Stack traces are less verbose: they no longer show calls to
|
||||
builtin functions and only show a single line for each derivation
|
||||
on the call stack.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New built-in function: <function>builtins.typeOf</function>,
|
||||
which returns the type of its argument as a string.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.6.0"><title>Release 1.6 (September 10, 2013)</title>
|
||||
|
||||
<para>In addition to the usual bug fixes, this release has several new
|
||||
features:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>The command <command>nix-build --run-env</command> has been
|
||||
renamed to <command>nix-shell</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> now sources
|
||||
<filename>$stdenv/setup</filename> <emphasis>inside</emphasis> the
|
||||
interactive shell, rather than in a parent shell. This ensures
|
||||
that shell functions defined by <literal>stdenv</literal> can be
|
||||
used in the interactive shell.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> has a new flag
|
||||
<option>--pure</option> to clear the environment, so you get an
|
||||
environment that more closely corresponds to the “real” Nix build.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> now sets the shell prompt
|
||||
(<envar>PS1</envar>) to ensure that Nix shells are distinguishable
|
||||
from your regular shells.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-env</command> no longer requires a
|
||||
<literal>*</literal> argument to match all packages, so
|
||||
<literal>nix-env -qa</literal> is equivalent to <literal>nix-env
|
||||
-qa '*'</literal>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-env -i</command> has a new flag
|
||||
<option>--remove-all</option> (<option>-r</option>) to remove all
|
||||
previous packages from the profile. This makes it easier to do
|
||||
declarative package management similar to NixOS’s
|
||||
<option>environment.systemPackages</option>. For instance, if you
|
||||
have a specification <filename>my-packages.nix</filename> like this:
|
||||
|
||||
<programlisting>
|
||||
with import <nixpkgs> {};
|
||||
[ thunderbird
|
||||
geeqie
|
||||
...
|
||||
]
|
||||
</programlisting>
|
||||
|
||||
then after any change to this file, you can run:
|
||||
|
||||
<screen>
|
||||
$ nix-env -f my-packages.nix -ir
|
||||
</screen>
|
||||
|
||||
to update your profile to match the specification.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The ‘<literal>with</literal>’ language construct is now more
|
||||
lazy. It only evaluates its argument if a variable might actually
|
||||
refer to an attribute in the argument. For instance, this now
|
||||
works:
|
||||
|
||||
<programlisting>
|
||||
let
|
||||
pkgs = with pkgs; { foo = "old"; bar = foo; } // overrides;
|
||||
overrides = { foo = "new"; };
|
||||
in pkgs.bar
|
||||
</programlisting>
|
||||
|
||||
This evaluates to <literal>"new"</literal>, while previously it
|
||||
gave an “infinite recursion” error.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now has proper integer arithmetic operators. For
|
||||
instance, you can write <literal>x + y</literal> instead of
|
||||
<literal>builtins.add x y</literal>, or <literal>x <
|
||||
y</literal> instead of <literal>builtins.lessThan x y</literal>.
|
||||
The comparison operators also work on strings.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>On 64-bit systems, Nix integers are now 64 bits rather than
|
||||
32 bits.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>When using the Nix daemon, the <command>nix-daemon</command>
|
||||
worker process now runs on the same CPU as the client, on systems
|
||||
that support setting CPU affinity. This gives a significant speedup
|
||||
on some systems.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>If a stack overflow occurs in the Nix evaluator, you now get
|
||||
a proper error message (rather than “Segmentation fault”) on some
|
||||
systems.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>In addition to directories, you can now bind-mount regular
|
||||
files in chroots through the (now misnamed) option
|
||||
<option>build-chroot-dirs</option>.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>This release has contributions from Domen Kožar, Eelco Dolstra,
|
||||
Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
|
||||
Levy.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
@@ -1321,7 +1512,7 @@ irreversible.</para></warning>
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>You can now unambigously specify which derivation to
|
||||
<listitem><para>You can now unambiguously specify which derivation to
|
||||
build or install in <command>nix-env</command>,
|
||||
<command>nix-instantiate</command> and <command>nix-build</command>
|
||||
using the <option>--attr</option> / <option>-A</option> flags, which
|
||||
@@ -1461,7 +1652,7 @@ irreversible.</para></warning>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Make sure that the garbage collector can run
|
||||
succesfully when the disk is full
|
||||
successfully when the disk is full
|
||||
(<literal>NIX-18</literal>).</para></listitem>
|
||||
|
||||
<listitem><para><command>nix-env</command> now locks the profile
|
||||
@@ -1816,7 +2007,7 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
|
||||
</itemizedlist></para></listitem>
|
||||
|
||||
<listitem><para>A hash of the contents of a store path is now stored
|
||||
in the database after a succesful build. This allows you to check
|
||||
in the database after a successful build. This allows you to check
|
||||
whether store paths have been tampered with: <command>nix-store
|
||||
--verify --check-contents</command>.</para></listitem>
|
||||
|
||||
@@ -1980,7 +2171,7 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Derivations for other platforms are filtered out
|
||||
(which can be overriden using
|
||||
(which can be overridden using
|
||||
<option>--system-filter</option>).</para></listitem>
|
||||
|
||||
<listitem><para><option>--install</option> by default now
|
||||
|
||||
@@ -17,7 +17,7 @@ bug tracker</link> for a list of currently known issues.</para>
|
||||
$ nix-env -i docbook-xml
|
||||
...
|
||||
adding /nix/store/s5hyxgm62gk2...-docbook-xml-4.2
|
||||
collission between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd'
|
||||
collision between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd'
|
||||
and `/nix/store/06h377hr4b33...-docbook-xml-4.3/xml/dtd/docbook/calstblx.dtd'
|
||||
at /nix/store/...-builder.pl line 62.</screen>
|
||||
|
||||
@@ -68,7 +68,7 @@ in <filename>/nix/store</filename>, as can be seen using <command>ls
|
||||
$ ls -l /nix/store
|
||||
drwxrwxrwt 32000 nix nix 4620288 Sep 8 15:08 store</screen>
|
||||
|
||||
The <literal>ext2</literal> file system is limited to a inode link
|
||||
The <literal>ext2</literal> file system is limited to an inode link
|
||||
count of 32,000 (each subdirectory increasing the count by one).
|
||||
Furthermore, the <literal>st_nlink</literal> field of the
|
||||
<function>stat</function> system call is a 16-bit value.</para>
|
||||
|
||||
@@ -15,7 +15,7 @@ on to a more in-depth look at the Nix expression language.</para>
|
||||
For more extensive information on adding packages to the Nix Packages
|
||||
collection (such as functions in the standard environment and coding
|
||||
conventions), please consult <link
|
||||
xlink:href="http://hydra.nixos.org/job/nixpkgs/trunk/tarball/latest/download-by-type/doc/manual">its
|
||||
xlink:href="http://nixos.org/nixpkgs/manual/">its
|
||||
manual</link>.</para></note>
|
||||
|
||||
|
||||
@@ -118,10 +118,10 @@ the single Nix expression in that directory
|
||||
<varname>stdenv.mkDerivation</varname>.
|
||||
<varname>mkDerivation</varname> is a function provided by
|
||||
<varname>stdenv</varname> that builds a package from a set of
|
||||
<emphasis>attributes</emphasis>. An attribute set is just a list
|
||||
of key/value pairs where each value is an arbitrary Nix
|
||||
expression. They take the general form
|
||||
<literal>{ <replaceable>name1</replaceable> =
|
||||
<emphasis>attributes</emphasis>. A set is just a list of
|
||||
key/value pairs where each key is a string and each value is an
|
||||
arbitrary Nix expression. They take the general form <literal>{
|
||||
<replaceable>name1</replaceable> =
|
||||
<replaceable>expr1</replaceable>; <replaceable>...</replaceable>
|
||||
<replaceable>nameN</replaceable> =
|
||||
<replaceable>exprN</replaceable>; }</literal>.</para>
|
||||
@@ -384,9 +384,9 @@ some fragments of
|
||||
|
||||
<para>This is where the actual composition takes place. Here we
|
||||
<emphasis>call</emphasis> the function imported from
|
||||
<filename>../applications/misc/hello/ex-1</filename> with an
|
||||
attribute set containing the things that the function expects,
|
||||
namely <varname>fetchurl</varname>, <varname>stdenv</varname>, and
|
||||
<filename>../applications/misc/hello/ex-1</filename> with a set
|
||||
containing the things that the function expects, namely
|
||||
<varname>fetchurl</varname>, <varname>stdenv</varname>, and
|
||||
<varname>perl</varname>. We use inherit again to use the
|
||||
attributes defined in the surrounding scope (we could also have
|
||||
written <literal>fetchurl = fetchurl;</literal>, etc.).</para>
|
||||
@@ -617,7 +617,7 @@ Laziness means that arguments to functions are evaluated only when
|
||||
they are needed. Functional means that functions are
|
||||
<quote>normal</quote> values that can be passed around and manipulated
|
||||
in interesting ways. The language is not a full-featured, general
|
||||
purpose language. It's main job is to describe packages,
|
||||
purpose language. Its main job is to describe packages,
|
||||
compositions of packages, and the variability within
|
||||
packages.</para>
|
||||
|
||||
@@ -701,7 +701,7 @@ configureFlags = "
|
||||
number of spaces equal to the minimal indentation of the string as
|
||||
a whole (disregarding the indentation of empty lines). For
|
||||
instance, the first and second line are indented two space, while
|
||||
the third line is indented three spaces. Thus, two spaces are
|
||||
the third line is indented four spaces. Thus, two spaces are
|
||||
stripped from each line, so the resulting string is
|
||||
|
||||
<programlisting>
|
||||
@@ -714,18 +714,18 @@ configureFlags = "
|
||||
text on the initial line.</para>
|
||||
|
||||
<para>Antiquotation
|
||||
(<literal>${<replaceable>expr</replaceable>}}</literal>) is
|
||||
(<literal>${<replaceable>expr</replaceable>}</literal>) is
|
||||
supported in indented strings.</para>
|
||||
|
||||
<para>Since <literal>${</literal> and <literal>''</literal> have
|
||||
special meaning in indented strings, you need a way to quote them.
|
||||
<literal>${</literal> can be escaped by prefixing it with
|
||||
<literal>''</literal>, i.e., <literal>''${</literal>.
|
||||
<literal>''</literal> can be escaped by prefixing it with
|
||||
<literal>'</literal>, i.e., <literal>'''</literal>. Finally,
|
||||
linefeed, carriage-return and tab characters can be writted as
|
||||
<literal>''\n</literal>, <literal>''\r</literal>,
|
||||
<literal>''\t</literal>.</para>
|
||||
<literal>''</literal> (that is, two single quotes), i.e.,
|
||||
<literal>''${</literal>. <literal>''</literal> can be escaped by
|
||||
prefixing it with <literal>'</literal>, i.e.,
|
||||
<literal>'''</literal>. Finally, linefeed, carriage-return and
|
||||
tab characters can be written as <literal>''\n</literal>,
|
||||
<literal>''\r</literal>, <literal>''\t</literal>.</para>
|
||||
|
||||
<para>Indented strings are primarily useful in that they allow
|
||||
multi-line string literals to follow the indentation of the
|
||||
@@ -775,7 +775,7 @@ stdenv.mkDerivation {
|
||||
absolute at parse time relative to the directory of the Nix
|
||||
expression that contained it. For instance, if a Nix expression in
|
||||
<filename>/foo/bar/bla.nix</filename> refers to
|
||||
<filename>../xyzzy/fnord.nix</filename>, the absolutised path is
|
||||
<filename>../xyzzy/fnord.nix</filename>, the absolute path is
|
||||
<filename>/foo/xyzzy/fnord.nix</filename>.</para></listitem>
|
||||
|
||||
<listitem><para><emphasis>Booleans</emphasis> with values
|
||||
@@ -805,20 +805,21 @@ to be enclosed in parentheses. If they had been omitted, e.g.,
|
||||
[ 123 ./foo.nix "abc" f { x = y; } ]</programlisting>
|
||||
|
||||
the result would be a list of five elements, the fourth one being a
|
||||
function and the fifth being an attribute set.</para>
|
||||
function and the fifth being a set.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Attribute sets</title>
|
||||
<simplesect><title>Sets</title>
|
||||
|
||||
<para>Attribute sets are really the core of the language, since
|
||||
ultimately it's all about creating derivations, which are really just
|
||||
<para>Sets are really the core of the language, since ultimately the
|
||||
Nix language is all about creating derivations, which are really just
|
||||
sets of attributes to be passed to build scripts.</para>
|
||||
|
||||
<para>Attribute sets are just a list of name/value pairs enclosed in
|
||||
curly brackets, where each value is an arbitrary expression terminated
|
||||
by a semicolon. For example:
|
||||
<para>Sets are just a list of name/value pairs (called
|
||||
<emphasis>attributes</emphasis>) enclosed in curly brackets, where
|
||||
each value is an arbitrary expression terminated by a semicolon. For
|
||||
example:
|
||||
|
||||
<programlisting>
|
||||
{ x = 123;
|
||||
@@ -826,12 +827,12 @@ by a semicolon. For example:
|
||||
y = f { bla = 456; };
|
||||
}</programlisting>
|
||||
|
||||
This defines an attribute set with attributes named
|
||||
<varname>x</varname>, <varname>test</varname>, <varname>y</varname>.
|
||||
The order of the attributes is irrelevant. An attribute name may only
|
||||
occur once.</para>
|
||||
This defines a set with attributes named <varname>x</varname>,
|
||||
<varname>text</varname>, <varname>y</varname>. The order of the
|
||||
attributes is irrelevant. An attribute name may only occur
|
||||
once.</para>
|
||||
|
||||
<para>Attributes can be selected from an attribute set using the
|
||||
<para>Attributes can be selected from a set using the
|
||||
<literal>.</literal> operator. For instance,
|
||||
|
||||
<programlisting>
|
||||
@@ -864,10 +865,10 @@ This will evaluate to <literal>123</literal>.</para>
|
||||
<section><title>Language constructs</title>
|
||||
|
||||
|
||||
<simplesect><title>Recursive attribute sets</title>
|
||||
<simplesect><title>Recursive sets</title>
|
||||
|
||||
<para>Recursive attribute sets are just normal attribute sets, but the
|
||||
attributes can refer to each other. For example,
|
||||
<para>Recursive sets are just normal sets, but the attributes can
|
||||
refer to each other. For example,
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
@@ -880,11 +881,11 @@ evaluates to <literal>123</literal>. Note that without
|
||||
<literal>rec</literal> the binding <literal>x = y;</literal> would
|
||||
refer to the variable <varname>y</varname> in the surrounding scope,
|
||||
if one exists, and would be invalid if no such variable exists. That
|
||||
is, in a normal (non-recursive) attribute set, attributes are not
|
||||
added to the lexical scope; in a recursive set, they are.</para>
|
||||
is, in a normal (non-recursive) set, attributes are not added to the
|
||||
lexical scope; in a recursive set, they are.</para>
|
||||
|
||||
<para>Recursive attribute sets of course introduce the danger of
|
||||
infinite recursion. For example,
|
||||
<para>Recursive sets of course introduce the danger of infinite
|
||||
recursion. For example,
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
@@ -901,8 +902,8 @@ encountered</quote>).</para></footnote>.</para>
|
||||
|
||||
<simplesect><title>Let-expressions</title>
|
||||
|
||||
<para>A let-expression allows you define local
|
||||
variables for an expression. For instance,
|
||||
<para>A let-expression allows you define local variables for an
|
||||
expression. For instance,
|
||||
|
||||
<programlisting>
|
||||
let
|
||||
@@ -914,20 +915,14 @@ evaluates to <literal>"foobar"</literal>.
|
||||
|
||||
</para>
|
||||
|
||||
<note><para>There is also an obsolete form of let-expression,
|
||||
<literal>let { <replaceable>attrs</replaceable> }</literal>, which is
|
||||
translated to <literal>rec { <replaceable>attrs</replaceable>
|
||||
}.body</literal>. That is, the body of the let-expression is the
|
||||
<literal>body</literal> attribute of the attribute set.</para></note>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Inheriting attributes</title>
|
||||
|
||||
<para>When defining an attribute set it is often convenient to copy
|
||||
variables from the surrounding lexical scope (e.g., when you want to
|
||||
propagate attributes). This can be shortened using the
|
||||
<para>When defining a set it is often convenient to copy variables
|
||||
from the surrounding lexical scope (e.g., when you want to propagate
|
||||
attributes). This can be shortened using the
|
||||
<literal>inherit</literal> keyword. For instance,
|
||||
|
||||
<programlisting>
|
||||
@@ -936,10 +931,10 @@ let x = 123; in
|
||||
y = 456;
|
||||
}</programlisting>
|
||||
|
||||
evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that this
|
||||
works because <varname>x</varname> is added to the lexical scope by
|
||||
the <literal>let</literal> construct.) It is also possible to inherit
|
||||
attributes from another attribute set. For instance, in this fragment
|
||||
evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that
|
||||
this works because <varname>x</varname> is added to the lexical scope
|
||||
by the <literal>let</literal> construct.) It is also possible to
|
||||
inherit attributes from another set. For instance, in this fragment
|
||||
from <filename>all-packages.nix</filename>,
|
||||
|
||||
<programlisting>
|
||||
@@ -958,13 +953,12 @@ from <filename>all-packages.nix</filename>,
|
||||
libjpg = ...;
|
||||
...</programlisting>
|
||||
|
||||
the attribute set used in the function call to the function defined in
|
||||
the set used in the function call to the function defined in
|
||||
<filename>../tools/graphics/graphviz</filename> inherits a number of
|
||||
variables from the surrounding scope (<varname>fetchurl</varname>
|
||||
... <varname>yacc</varname>), but also inherits
|
||||
<varname>libXaw</varname> (the X Athena Widgets) from the
|
||||
<varname>xlibs</varname> (X11 client-side libraries) attribute
|
||||
set.</para>
|
||||
<varname>xlibs</varname> (X11 client-side libraries) set.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
@@ -1003,11 +997,11 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
|
||||
"fooabc" ]</literal>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>An <emphasis>attribute set pattern</emphasis> of the
|
||||
form <literal>{ name1, name2, …, nameN }</literal>
|
||||
matches an attribute set containing the listed attributes, and binds
|
||||
the values of those attributes to variables in the function body.
|
||||
For example, the function
|
||||
<listitem><para>A <emphasis>set pattern</emphasis> of the form
|
||||
<literal>{ name1, name2, …, nameN }</literal> matches a set
|
||||
containing the listed attributes, and binds the values of those
|
||||
attributes to variables in the function body. For example, the
|
||||
function
|
||||
|
||||
<programlisting>
|
||||
{ x, y, z }: z + y + x</programlisting>
|
||||
@@ -1174,9 +1168,8 @@ used in the Nix expression for Subversion.</para>
|
||||
<programlisting>
|
||||
with <replaceable>e1</replaceable>; <replaceable>e2</replaceable></programlisting>
|
||||
|
||||
introduces the attribute set <replaceable>e1</replaceable> into the
|
||||
lexical scope of the expression <replaceable>e2</replaceable>. For
|
||||
instance,
|
||||
introduces the set <replaceable>e1</replaceable> into the lexical
|
||||
scope of the expression <replaceable>e2</replaceable>. For instance,
|
||||
|
||||
<programlisting>
|
||||
let as = { x = "foo"; y = "bar"; };
|
||||
@@ -1235,7 +1228,7 @@ weakest binding).</para>
|
||||
</entry>
|
||||
<entry>none</entry>
|
||||
<entry>Select attribute denoted by the attribute path
|
||||
<replaceable>attrpath</replaceable> from attribute set
|
||||
<replaceable>attrpath</replaceable> from set
|
||||
<replaceable>e</replaceable>. (An attribute path is a
|
||||
dot-separated list of attribute names.) If the attribute
|
||||
doesn’t exist, return <replaceable>def</replaceable> if
|
||||
@@ -1251,8 +1244,8 @@ weakest binding).</para>
|
||||
<entry><replaceable>e</replaceable> <literal>?</literal>
|
||||
<replaceable>attrpath</replaceable></entry>
|
||||
<entry>none</entry>
|
||||
<entry>Test whether attribute set <replaceable>e</replaceable>
|
||||
contains the attribute denoted by <replaceable>attrpath</replaceable>;
|
||||
<entry>Test whether set <replaceable>e</replaceable> contains
|
||||
the attribute denoted by <replaceable>attrpath</replaceable>;
|
||||
return <literal>true</literal> or
|
||||
<literal>false</literal>.</entry>
|
||||
</row>
|
||||
@@ -1275,10 +1268,11 @@ weakest binding).</para>
|
||||
<entry><replaceable>e1</replaceable> <literal>//</literal>
|
||||
<replaceable>e2</replaceable></entry>
|
||||
<entry>right</entry>
|
||||
<entry>Return an attribute set consisting of the attributes in
|
||||
<entry>Return a set consisting of the attributes in
|
||||
<replaceable>e1</replaceable> and
|
||||
<replaceable>e2</replaceable> (with the latter taking
|
||||
precedence over the former in case of equally named attributes).</entry>
|
||||
precedence over the former in case of equally named
|
||||
attributes).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><replaceable>e1</replaceable> <literal>==</literal>
|
||||
@@ -1322,9 +1316,9 @@ weakest binding).</para>
|
||||
<section xml:id="ssec-derivation"><title>Derivations</title>
|
||||
|
||||
<para>The most important built-in function is
|
||||
<function>derivation</function>, which is used to describe a
|
||||
single derivation (a build action). It takes as input an attribute
|
||||
set, the attributes of which specify the inputs of the build.</para>
|
||||
<function>derivation</function>, which is used to describe a single
|
||||
derivation (a build action). It takes as input a set, the attributes
|
||||
of which specify the inputs of the build.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Requires: /usr/bin/perl
|
||||
Requires: curl
|
||||
Requires: perl-DBD-SQLite
|
||||
Requires: bzip2
|
||||
Requires: gzip
|
||||
Requires: xz
|
||||
BuildRequires: bzip2-devel
|
||||
BuildRequires: sqlite-devel
|
||||
|
||||
@@ -2,6 +2,7 @@ package Nix::Manifest;
|
||||
|
||||
use strict;
|
||||
use DBI;
|
||||
use DBD::SQLite;
|
||||
use Cwd;
|
||||
use File::stat;
|
||||
use File::Path;
|
||||
@@ -227,6 +228,9 @@ sub writeManifest {
|
||||
sub updateManifestDB {
|
||||
my $manifestDir = $Nix::Config::manifestDir;
|
||||
|
||||
my @manifests = glob "$manifestDir/*.nixmanifest";
|
||||
return undef if scalar @manifests == 0;
|
||||
|
||||
mkpath($manifestDir);
|
||||
|
||||
unlink "$manifestDir/cache.sqlite"; # remove obsolete cache
|
||||
@@ -311,7 +315,7 @@ EOF
|
||||
# unless we've already done so on a previous run.
|
||||
my %seen;
|
||||
|
||||
for my $manifestLink (glob "$manifestDir/*.nixmanifest") {
|
||||
for my $manifestLink (@manifests) {
|
||||
my $manifest = Cwd::abs_path($manifestLink);
|
||||
next unless -f $manifest;
|
||||
my $timestamp = lstat($manifest)->mtime;
|
||||
|
||||
@@ -20,6 +20,7 @@ void doInit()
|
||||
if (!store) {
|
||||
try {
|
||||
settings.processEnvironment();
|
||||
settings.lockCPU = false;
|
||||
store = openStore();
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
|
||||
+37
-5
@@ -6,7 +6,7 @@ let
|
||||
|
||||
pkgs = import <nixpkgs> {};
|
||||
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" "i686-cygwin" "i686-solaris" ];
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" ];
|
||||
|
||||
|
||||
jobs = rec {
|
||||
@@ -173,15 +173,15 @@ let
|
||||
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50;
|
||||
rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) 60;
|
||||
rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) 60;
|
||||
rpm_fedora19i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora19i386) 70;
|
||||
rpm_fedora19x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora19x86_64) 70;
|
||||
|
||||
|
||||
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
|
||||
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
|
||||
deb_debian70i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian70i386) 60;
|
||||
deb_debian70x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian70x86_64) 60;
|
||||
deb_debian7i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian7i386) 60;
|
||||
deb_debian7x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian7x86_64) 60;
|
||||
|
||||
deb_ubuntu1004i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1004i386) 50;
|
||||
deb_ubuntu1004x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1004x86_64) 50;
|
||||
deb_ubuntu1010i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1010i386) 50;
|
||||
deb_ubuntu1010x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1010x86_64) 50;
|
||||
deb_ubuntu1110i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1110i386) 60;
|
||||
@@ -192,6 +192,8 @@ let
|
||||
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70;
|
||||
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) 80;
|
||||
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) 80;
|
||||
deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386) 90;
|
||||
deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64) 90;
|
||||
|
||||
|
||||
# System tests.
|
||||
@@ -203,6 +205,36 @@ let
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
}).test;
|
||||
|
||||
|
||||
# Aggregate job containing the release-critical jobs.
|
||||
release = pkgs.releaseTools.aggregate {
|
||||
name = "nix-${tarball.version}";
|
||||
meta.description = "Release-critical builds";
|
||||
constituents =
|
||||
[ tarball
|
||||
build.i686-freebsd
|
||||
build.i686-linux
|
||||
build.x86_64-darwin
|
||||
build.x86_64-freebsd
|
||||
build.x86_64-linux
|
||||
binaryTarball.i686-freebsd
|
||||
binaryTarball.i686-linux
|
||||
binaryTarball.x86_64-darwin
|
||||
binaryTarball.x86_64-freebsd
|
||||
binaryTarball.x86_64-linux
|
||||
deb_debian7i386
|
||||
deb_debian7x86_64
|
||||
deb_ubuntu1304i386
|
||||
deb_ubuntu1304x86_64
|
||||
deb_ubuntu1310i386
|
||||
deb_ubuntu1310x86_64
|
||||
rpm_fedora19i386
|
||||
rpm_fedora19x86_64
|
||||
tests.remote_builds
|
||||
tests.nix_copy_closure
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ install-exec-local: download-using-manifests.pl copy-from-other-stores.pl downlo
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
ln -sf nix-build $(DESTDIR)$(bindir)/nix-shell
|
||||
|
||||
include ../substitute.mk
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ sub all { $_ || return 0 for @_; 1 }
|
||||
# Initialisation.
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my ($localSystem, $printBuildTrace) = @ARGV;
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace, $buildTimeout) = @ARGV;
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
@@ -213,7 +213,6 @@ my @inputs = split /\s/, readline(STDIN);
|
||||
my @outputs = split /\s/, readline(STDIN);
|
||||
|
||||
|
||||
print STDERR "building `$drvPath' on `$hostName'\n";
|
||||
print STDERR "@ build-remote $drvPath $hostName\n" if $printBuildTrace;
|
||||
|
||||
|
||||
@@ -262,8 +261,9 @@ close UPLOADLOCK;
|
||||
|
||||
# Perform the build.
|
||||
my $buildFlags =
|
||||
" --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false";
|
||||
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
|
||||
. " --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false --option build-use-substitutes false";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
# closed unexpectedly. This is necessary to ensure that no processes
|
||||
@@ -272,6 +272,7 @@ my $buildFlags =
|
||||
# is interrupted unless the `-tt' flag is used to force a pseudo-tty,
|
||||
# in which case every child receives SIGHUP; however, `-tt' doesn't
|
||||
# work on some platforms when connection sharing is used.)
|
||||
print STDERR "building `$drvPath' on `$hostName'\n";
|
||||
pipe STDIN, DUMMY; # make sure we have a readable STDIN
|
||||
if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & exec nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
|
||||
# Note that if we get exit code 100 from `nix-store -r', it
|
||||
|
||||
@@ -16,6 +16,9 @@ foreach my $dir (@remoteStoresAll) {
|
||||
push @remoteStores, glob($dir);
|
||||
}
|
||||
|
||||
exit if scalar @remoteStores == 0;
|
||||
print "\n";
|
||||
|
||||
|
||||
$ENV{"NIX_REMOTE"} = "";
|
||||
|
||||
@@ -27,7 +30,7 @@ sub findStorePath {
|
||||
next unless -e $sourcePath || -l $sourcePath;
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
return ($store, $sourcePath) if
|
||||
system("@bindir@/nix-store --check-validity $storePath") == 0;
|
||||
system("$binDir/nix-store --check-validity $storePath") == 0;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -53,16 +56,16 @@ if ($ARGV[0] eq "--query") {
|
||||
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
|
||||
my $deriver = `@bindir@/nix-store --query --deriver $storePath`;
|
||||
my $deriver = `$binDir/nix-store --query --deriver $storePath`;
|
||||
die "cannot query deriver of `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my @references = split "\n",
|
||||
`@bindir@/nix-store --query --references $storePath`;
|
||||
`$binDir/nix-store --query --references $storePath`;
|
||||
die "cannot query references of `$storePath'" if $? != 0;
|
||||
|
||||
my $narSize = `@bindir@/nix-store --query --size $storePath`;
|
||||
my $narSize = `$binDir/nix-store --query --size $storePath`;
|
||||
die "cannot query size of `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
|
||||
@@ -70,7 +73,7 @@ if ($ARGV[0] eq "--query") {
|
||||
print "$deriver\n";
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
print "$narSize\n";
|
||||
print "0\n";
|
||||
print "$narSize\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use DBI;
|
||||
use DBD::SQLite;
|
||||
use File::Basename;
|
||||
use IO::Select;
|
||||
use Nix::Config;
|
||||
@@ -199,10 +200,6 @@ sub getAvailableCaches {
|
||||
return if $gotCaches;
|
||||
$gotCaches = 1;
|
||||
|
||||
return if
|
||||
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
|
||||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
|
||||
|
||||
sub strToList {
|
||||
my ($s) = @_;
|
||||
return map { s/\/+$//; $_ } split(/ /, $s);
|
||||
@@ -517,8 +514,9 @@ sub downloadBinary {
|
||||
next unless defined $info;
|
||||
|
||||
my $decompressor;
|
||||
if ($info->{compression} eq "bzip2") { $decompressor = "$Nix::Config::bzip2 -d"; }
|
||||
elsif ($info->{compression} eq "xz") { $decompressor = "$Nix::Config::xz -d"; }
|
||||
if ($info->{compression} eq "bzip2") { $decompressor = "| $Nix::Config::bzip2 -d"; }
|
||||
elsif ($info->{compression} eq "xz") { $decompressor = "| $Nix::Config::xz -d"; }
|
||||
elsif ($info->{compression} eq "none") { $decompressor = ""; }
|
||||
else {
|
||||
print STDERR "unknown compression method ‘$info->{compression}’\n";
|
||||
next;
|
||||
@@ -526,7 +524,7 @@ sub downloadBinary {
|
||||
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
|
||||
print STDERR "\n*** Downloading ‘$url’ to ‘$storePath’...\n";
|
||||
checkURL $url;
|
||||
if (system("$Nix::Config::curl --fail --location --insecure '$url' | $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
||||
if (system("$Nix::Config::curl --fail --location --insecure '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
||||
warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n";
|
||||
next;
|
||||
}
|
||||
@@ -543,6 +541,13 @@ sub downloadBinary {
|
||||
}
|
||||
|
||||
|
||||
# Bail out right away if binary caches are disabled.
|
||||
exit 0 if
|
||||
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
|
||||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
|
||||
print "\n";
|
||||
flush STDOUT;
|
||||
|
||||
initCache();
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ my $curl = "$Nix::Config::curl --fail --location --insecure";
|
||||
|
||||
# Open the manifest cache and update it if necessary.
|
||||
my $dbh = updateManifestDB();
|
||||
exit 0 unless defined $dbh; # exit if there are no manifests
|
||||
print "\n";
|
||||
|
||||
|
||||
# $hashCache->{$algo}->{$path} yields the $algo-hash of $path.
|
||||
@@ -342,17 +344,18 @@ while (scalar @path > 0) {
|
||||
checkURL $narFile->{url};
|
||||
|
||||
my $decompressor =
|
||||
$narFile->{compressionType} eq "bzip2" ? "$Nix::Config::bzip2 -d" :
|
||||
$narFile->{compressionType} eq "xz" ? "$Nix::Config::xz -d" :
|
||||
$narFile->{compressionType} eq "bzip2" ? "| $Nix::Config::bzip2 -d" :
|
||||
$narFile->{compressionType} eq "xz" ? "| $Nix::Config::xz -d" :
|
||||
$narFile->{compressionType} eq "none" ? "" :
|
||||
die "unknown compression type `$narFile->{compressionType}'";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used a base to a patch.
|
||||
system("$curl '$narFile->{url}' | $decompressor > $tmpNar") == 0
|
||||
system("$curl '$narFile->{url}' $decompressor > $tmpNar") == 0
|
||||
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
|
||||
} else {
|
||||
# Unpack the archive to the target path.
|
||||
system("$curl '$narFile->{url}' | $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0
|
||||
system("$curl '$narFile->{url}' $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0
|
||||
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
|
||||
}
|
||||
|
||||
|
||||
+59
-22
@@ -3,23 +3,27 @@
|
||||
use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
|
||||
my $dryRun = 0;
|
||||
my $verbose = 0;
|
||||
my $runEnv = 0;
|
||||
my $runEnv = $0 =~ /nix-shell$/;
|
||||
my $pure = 0;
|
||||
|
||||
my @instArgs = ();
|
||||
my @buildArgs = ();
|
||||
my @exprs = ();
|
||||
|
||||
my $shell = $ENV{SHELL} || "/bin/sh";
|
||||
my $envCommand = "p=\$PATH; source \$stdenv/setup; PATH=\$PATH:\$p; exec $shell";
|
||||
my $envCommand = ""; # interactive shell
|
||||
my @envExclude = ();
|
||||
|
||||
my $myName = $runEnv ? "nix-shell" : "nix-build";
|
||||
|
||||
my $tmpDir = tempdir("nix-build.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
|
||||
my $tmpDir = tempdir("$myName.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $outLink = "./result";
|
||||
@@ -33,11 +37,11 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
my $arg = $ARGV[$n];
|
||||
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-build" or die;
|
||||
exec "man $myName" or die;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--version") {
|
||||
print "nix-build (Nix) $Nix::Config::version\n";
|
||||
print "$myName (Nix) $Nix::Config::version\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
@@ -117,14 +121,14 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--run-env") {
|
||||
elsif ($arg eq "--run-env") { # obsolete
|
||||
$runEnv = 1;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--command") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$envCommand = $ARGV[$n];
|
||||
$envCommand = "$ARGV[$n]\nexit $!";
|
||||
}
|
||||
|
||||
elsif ($arg eq "--exclude") {
|
||||
@@ -133,6 +137,10 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
push @envExclude, $ARGV[$n];
|
||||
}
|
||||
|
||||
elsif ($arg eq "--pure") {
|
||||
$pure = 1;
|
||||
}
|
||||
|
||||
elsif (substr($arg, 0, 1) eq "-") {
|
||||
push @buildArgs, $arg;
|
||||
}
|
||||
@@ -149,17 +157,22 @@ foreach my $expr (@exprs) {
|
||||
|
||||
# Instantiate.
|
||||
my @drvPaths;
|
||||
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
||||
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||
if (!close DRVPATHS) {
|
||||
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
if ($expr !~ /^\/.*\.drv$/) {
|
||||
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
||||
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||
if (!close DRVPATHS) {
|
||||
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
}
|
||||
} else {
|
||||
push @drvPaths, $expr;
|
||||
}
|
||||
|
||||
if ($runEnv) {
|
||||
die "$0: ‘--run-env’ requires a single derivation\n" if scalar @drvPaths != 1;
|
||||
my $drvPath = readlink $drvPaths[0] or die "cannot read symlink `$drvPaths[0]'";
|
||||
die "$0: a single derivation is required\n" if scalar @drvPaths != 1;
|
||||
my $drvPath = $drvPaths[0];
|
||||
$drvPath = readlink $drvPath or die "cannot read symlink `$drvPath'" if -l $drvPath;
|
||||
my $drv = derivationFromPath($drvPath);
|
||||
|
||||
# Build or fetch all dependencies of the derivation.
|
||||
@@ -168,16 +181,40 @@ foreach my $expr (@exprs) {
|
||||
or die "$0: failed to build all dependencies\n";
|
||||
|
||||
# Set the environment.
|
||||
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} || "/tmp";
|
||||
foreach (keys %{$drv->{env}}) {
|
||||
$ENV{$_} = $drv->{env}->{$_};
|
||||
if ($pure) {
|
||||
foreach my $name (keys %ENV) {
|
||||
next if $name eq "HOME" || $name eq "USER" || $name eq "LOGNAME" || $name eq "DISPLAY" || $name eq "PATH";
|
||||
delete $ENV{$name};
|
||||
}
|
||||
# NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
|
||||
$ENV{'__ETC_PROFILE_SOURCED'} = 1;
|
||||
}
|
||||
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} = $ENV{'TEMPDIR'} = $ENV{'TMP'} = $ENV{'TEMP'} = $ENV{'TMPDIR'} // "/tmp";
|
||||
$ENV{'NIX_STORE'} = $Nix::Config::storeDir;
|
||||
$ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}};
|
||||
|
||||
$ENV{'IN_NIX_SHELL'} = 1;
|
||||
|
||||
# Run a shell using the derivation's environment. For
|
||||
# convenience, source $stdenv/setup to setup additional
|
||||
# environment variables. Also don't lose the current $PATH
|
||||
# directories.
|
||||
exec($ENV{SHELL}, "-c", $envCommand);
|
||||
# environment variables and shell functions. Also don't lose
|
||||
# the current $PATH directories.
|
||||
my $rcfile = "$tmpDir/rc";
|
||||
writeFile(
|
||||
$rcfile,
|
||||
'unset BASH_ENV; ' .
|
||||
'[ -n "$PS1" ] && [ -e ~/.bashrc ] && source ~/.bashrc; ' .
|
||||
($pure ? '' : 'p=$PATH; ' ) .
|
||||
'dontAddDisableDepTrack=1; ' .
|
||||
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
||||
($pure ? '' : 'PATH=$PATH:$p; ') .
|
||||
'set +e; ' .
|
||||
'[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
||||
'unset NIX_ENFORCE_PURITY; ' .
|
||||
'shopt -u nullglob; ' .
|
||||
$envCommand);
|
||||
$ENV{BASH_ENV} = $rcfile;
|
||||
exec($ENV{NIX_BUILD_SHELL} // "bash", "--rcfile", $rcfile);
|
||||
die;
|
||||
}
|
||||
|
||||
@@ -206,7 +243,7 @@ foreach my $expr (@exprs) {
|
||||
|
||||
# Build.
|
||||
my @outPaths;
|
||||
$pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
||||
my $pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
||||
@buildArgs, @drvPaths2;
|
||||
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
|
||||
if (!close OUTPATHS) {
|
||||
|
||||
+3
-1
@@ -34,6 +34,8 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
exec "man nix-push" or die;
|
||||
} elsif ($arg eq "--bzip2") {
|
||||
$compressionType = "bzip2";
|
||||
} elsif ($arg eq "--none") {
|
||||
$compressionType = "none";
|
||||
} elsif ($arg eq "--force") {
|
||||
$force = 1;
|
||||
} elsif ($arg eq "--dest") {
|
||||
@@ -202,7 +204,7 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
|
||||
$compressedHash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
my $narName = "$compressedHash.nar." . ($compressionType eq "xz" ? "xz" : "bz2");
|
||||
my $narName = "$compressedHash.nar" . ($compressionType eq "xz" ? ".xz" : $compressionType eq "bzip2" ? ".bz2" : "");
|
||||
|
||||
my $narFile = "$narDir/$narName";
|
||||
(-f $narFile) or die "NAR file for $storePath not found";
|
||||
|
||||
+25
-25
@@ -6,9 +6,8 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
// !!! Shouldn't we return a pointer to a Value?
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v)
|
||||
Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
Strings tokens = tokenizeString<Strings>(attrPath, ".");
|
||||
|
||||
@@ -17,8 +16,8 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
string curPath;
|
||||
|
||||
state.mkThunk_(v, e);
|
||||
|
||||
Value * v = &vIn;
|
||||
|
||||
foreach (Strings::iterator, i, tokens) {
|
||||
|
||||
if (!curPath.empty()) curPath += ".";
|
||||
@@ -27,47 +26,48 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
/* Is *i an index (integer) or a normal attribute name? */
|
||||
enum { apAttr, apIndex } apType = apAttr;
|
||||
string attr = *i;
|
||||
int attrIndex = -1;
|
||||
unsigned int attrIndex;
|
||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||
|
||||
/* Evaluate the expression. */
|
||||
Value vTmp;
|
||||
state.autoCallFunction(autoArgs, v, vTmp);
|
||||
v = vTmp;
|
||||
state.forceValue(v);
|
||||
Value * vNew = state.allocValue();
|
||||
state.autoCallFunction(autoArgs, *v, *vNew);
|
||||
v = vNew;
|
||||
state.forceValue(*v);
|
||||
|
||||
/* It should evaluate to either an attribute set or an
|
||||
expression, according to what is specified in the
|
||||
attrPath. */
|
||||
/* It should evaluate to either a set or an expression,
|
||||
according to what is specified in the attrPath. */
|
||||
|
||||
if (apType == apAttr) {
|
||||
|
||||
if (v.type != tAttrs)
|
||||
if (v->type != tAttrs)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
|
||||
% curPath % showType(v));
|
||||
format("the expression selected by the selection path `%1%' should be a set but is %2%")
|
||||
% curPath % showType(*v));
|
||||
|
||||
Bindings::iterator a = v.attrs->find(state.symbols.create(attr));
|
||||
if (a == v.attrs->end())
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (a == v->attrs->end())
|
||||
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
|
||||
v = *a->value;
|
||||
v = &*a->value;
|
||||
}
|
||||
|
||||
else if (apType == apIndex) {
|
||||
|
||||
if (v.type != tList)
|
||||
if (v->type != tList)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path `%1%' should be a list but is %2%")
|
||||
% curPath % showType(v));
|
||||
% curPath % showType(*v));
|
||||
|
||||
if (attrIndex >= v.list.length)
|
||||
if (attrIndex >= v->list.length)
|
||||
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
|
||||
|
||||
v = *v.list.elems[attrIndex];
|
||||
v = v->list.elems[attrIndex];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v);
|
||||
Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn);
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ inline void EvalState::forceAttrs(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tAttrs)
|
||||
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
||||
throwTypeError("value is %1% while a set was expected", showType(v));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+143
-136
@@ -44,7 +44,7 @@ void Bindings::sort()
|
||||
{
|
||||
std::sort(begin(), end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Value & v)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ string showType(const Value & v)
|
||||
case tString: return "a string";
|
||||
case tPath: return "a path";
|
||||
case tNull: return "null";
|
||||
case tAttrs: return "an attribute set";
|
||||
case tAttrs: return "a set";
|
||||
case tList: return "a list";
|
||||
case tThunk: return "a thunk";
|
||||
case tApp: return "a function application";
|
||||
@@ -136,6 +136,7 @@ EvalState::EvalState()
|
||||
, sType(symbols.create("type"))
|
||||
, sMeta(symbols.create("meta"))
|
||||
, sName(symbols.create("name"))
|
||||
, sValue(symbols.create("value"))
|
||||
, sSystem(symbols.create("system"))
|
||||
, sOverrides(symbols.create("__overrides"))
|
||||
, sOutputs(symbols.create("outputs"))
|
||||
@@ -143,8 +144,8 @@ EvalState::EvalState()
|
||||
, sIgnoreNulls(symbols.create("__ignoreNulls"))
|
||||
, repair(false)
|
||||
, baseEnv(allocEnv(128))
|
||||
, baseEnvDispl(0)
|
||||
, staticBaseEnv(false, 0)
|
||||
, baseEnvDispl(0)
|
||||
{
|
||||
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
||||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
@@ -158,7 +159,7 @@ EvalState::EvalState()
|
||||
physical RAM, up to a maximum of 384 MiB) so that in most
|
||||
cases we don't need to garbage collect at all. (Collection
|
||||
has a fairly significant overhead.) The heap size can be
|
||||
overriden through libgc's GC_INITIAL_HEAP_SIZE environment
|
||||
overridden through libgc's GC_INITIAL_HEAP_SIZE environment
|
||||
variable. We should probably also provide a nix.conf
|
||||
setting for this. Note that GC_expand_hp() causes a lot of
|
||||
virtual, but not physical (resident) memory to be
|
||||
@@ -222,6 +223,12 @@ void EvalState::addPrimOp(const string & name,
|
||||
}
|
||||
|
||||
|
||||
void EvalState::getBuiltin(const string & name, Value & v)
|
||||
{
|
||||
v = *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
|
||||
}
|
||||
|
||||
|
||||
/* Every "format" object (even temporary) takes up a few hundred bytes
|
||||
of stack space, which is a real killer in the recursive
|
||||
evaluator. So here are some helper functions for throwing
|
||||
@@ -242,36 +249,26 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s))
|
||||
throw TypeError(s);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
|
||||
{
|
||||
throw TypeError(format(s) % pos % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2))
|
||||
{
|
||||
throw TypeError(format(s) % s1 % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw TypeError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw AssertionError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const char * s, const string & s1, const Pos & pos))
|
||||
{
|
||||
throw UndefinedVarError(format(s) % s1 % pos);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
||||
{
|
||||
e.addPrefix(format(s) % s2);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos))
|
||||
{
|
||||
e.addPrefix(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
|
||||
{
|
||||
e.addPrefix(format(s) % s2 % pos);
|
||||
@@ -291,7 +288,7 @@ void mkString(Value & v, const string & s, const PathSet & context)
|
||||
unsigned int n = 0;
|
||||
v.string.context = (const char * *)
|
||||
GC_MALLOC((context.size() + 1) * sizeof(char *));
|
||||
foreach (PathSet::const_iterator, i, context)
|
||||
foreach (PathSet::const_iterator, i, context)
|
||||
v.string.context[n++] = GC_STRDUP(i->c_str());
|
||||
v.string.context[n] = 0;
|
||||
}
|
||||
@@ -304,23 +301,29 @@ void mkPath(Value & v, const char * s)
|
||||
}
|
||||
|
||||
|
||||
inline Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
||||
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
{
|
||||
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
||||
|
||||
if (var.fromWith) {
|
||||
while (1) {
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end()) {
|
||||
if (countCalls && j->pos) attrSelects[*j->pos]++;
|
||||
return j->value;
|
||||
}
|
||||
if (env->prevWith == 0)
|
||||
throwEvalError("undefined variable `%1%'", var.name);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
|
||||
if (!var.fromWith) return env->values[var.displ];
|
||||
|
||||
while (1) {
|
||||
if (!env->haveWithAttrs) {
|
||||
if (noEval) return 0;
|
||||
Value * v = allocValue();
|
||||
evalAttrs(*env->up, (Expr *) env->values[0], *v);
|
||||
env->values[0] = v;
|
||||
env->haveWithAttrs = true;
|
||||
}
|
||||
} else
|
||||
return env->values[var.displ];
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end()) {
|
||||
if (countCalls && j->pos) attrSelects[*j->pos]++;
|
||||
return j->value;
|
||||
}
|
||||
if (!env->prevWith)
|
||||
throwUndefinedVarError("undefined variable `%1%' at %2%", var.name, var.pos);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -337,10 +340,10 @@ Env & EvalState::allocEnv(unsigned int size)
|
||||
nrValuesInEnvs += size;
|
||||
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
|
||||
|
||||
/* Clear the values because maybeThunk() expects this. */
|
||||
/* Clear the values because maybeThunk() and lookupVar fromWith expects this. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
env->values[i] = 0;
|
||||
|
||||
|
||||
return *env;
|
||||
}
|
||||
|
||||
@@ -352,7 +355,7 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EvalState::mkList(Value & v, unsigned int length)
|
||||
{
|
||||
v.type = tList;
|
||||
@@ -405,7 +408,7 @@ unsigned long nrAvoided = 0;
|
||||
|
||||
Value * ExprVar::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
Value * v = state.lookupVar(&env, info);
|
||||
Value * v = state.lookupVar(&env, *this, true);
|
||||
/* The value might not be initialised in the environment yet.
|
||||
In that case, ignore it. */
|
||||
if (v) { nrAvoided++; return v; }
|
||||
@@ -434,19 +437,35 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||
|
||||
void EvalState::evalFile(const Path & path, Value & v)
|
||||
{
|
||||
FileEvalCache::iterator i = fileEvalCache.find(path);
|
||||
if (i == fileEvalCache.end()) {
|
||||
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
|
||||
Expr * e = parseExprFromFile(path);
|
||||
try {
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
|
||||
throw;
|
||||
}
|
||||
fileEvalCache[path] = v;
|
||||
} else
|
||||
FileEvalCache::iterator i;
|
||||
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
||||
v = i->second;
|
||||
return;
|
||||
}
|
||||
|
||||
Path path2 = resolveExprPath(path);
|
||||
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
|
||||
v = i->second;
|
||||
return;
|
||||
}
|
||||
|
||||
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path2);
|
||||
Expr * e = parseExprFromFile(path2);
|
||||
try {
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the file `%1%':\n", path2);
|
||||
throw;
|
||||
}
|
||||
|
||||
fileEvalCache[path2] = v;
|
||||
if (path != path2) fileEvalCache[path] = v;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::resetFileCache()
|
||||
{
|
||||
fileEvalCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -470,7 +489,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
||||
{
|
||||
e->eval(*this, env, v);
|
||||
if (v.type != tAttrs)
|
||||
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
||||
throwTypeError("value is %1% while a set was expected", showType(v));
|
||||
}
|
||||
|
||||
|
||||
@@ -515,23 +534,17 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
environment, while the inherited attributes are evaluated
|
||||
in the original environment. */
|
||||
unsigned int displ = 0;
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) {
|
||||
/* !!! handle overrides? */
|
||||
Value * vAttr = state.lookupVar(&env, i->second.var);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
} else {
|
||||
Value * vAttr;
|
||||
if (hasOverrides) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i->second.e);
|
||||
} else
|
||||
vAttr = i->second.e->maybeThunk(state, env2);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
}
|
||||
|
||||
foreach (AttrDefs::iterator, i, attrs) {
|
||||
Value * vAttr;
|
||||
if (hasOverrides && !i->second.inherited) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i->second.e);
|
||||
} else
|
||||
vAttr = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
}
|
||||
|
||||
/* If the rec contains an attribute called `__overrides', then
|
||||
evaluate it, and add the attributes in that set to the rec.
|
||||
This allows overriding of recursive attributes, which is
|
||||
@@ -557,10 +570,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
else {
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited)
|
||||
v.attrs->push_back(Attr(i->first, state.lookupVar(&env, i->second.var), &i->second.pos));
|
||||
else
|
||||
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
|
||||
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,10 +587,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
environment. */
|
||||
unsigned int displ = 0;
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
if (i->second.inherited)
|
||||
env2.values[displ++] = state.lookupVar(&env, i->second.var);
|
||||
else
|
||||
env2.values[displ++] = i->second.e->maybeThunk(state, env2);
|
||||
env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
||||
|
||||
body->eval(state, env2, v);
|
||||
}
|
||||
@@ -596,7 +603,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value * v2 = state.lookupVar(&env, info);
|
||||
Value * v2 = state.lookupVar(&env, *this, false);
|
||||
state.forceValue(*v2);
|
||||
v = *v2;
|
||||
}
|
||||
@@ -613,7 +620,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
e->eval(state, env, vTmp);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||
nrLookups++;
|
||||
Bindings::iterator j;
|
||||
@@ -634,17 +641,16 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
pos = j->pos;
|
||||
if (state.countCalls && pos) state.attrSelects[*pos]++;
|
||||
}
|
||||
|
||||
|
||||
state.forceValue(*vAttrs);
|
||||
|
||||
|
||||
} catch (Error & e) {
|
||||
if (pos)
|
||||
if (pos && pos->file != state.sDerivationNix)
|
||||
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
|
||||
showAttrPath(attrPath), *pos);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
v = *vAttrs;
|
||||
}
|
||||
|
||||
@@ -668,7 +674,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||
vAttrs = j->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mkBool(v, true);
|
||||
}
|
||||
|
||||
@@ -703,10 +709,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
assert(primOp->type == tPrimOp);
|
||||
unsigned int arity = primOp->primOp->arity;
|
||||
unsigned int argsLeft = arity - argsDone;
|
||||
|
||||
|
||||
if (argsLeft == 1) {
|
||||
/* We have all the arguments, so call the primop. */
|
||||
|
||||
|
||||
/* Put all the arguments in an array. */
|
||||
Value * vArgs[arity];
|
||||
unsigned int n = arity - 1;
|
||||
@@ -717,12 +723,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
/* And call the primop. */
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[primOp->primOp->name]++;
|
||||
try {
|
||||
primOp->primOp->fun(*this, vArgs, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp->name);
|
||||
throw;
|
||||
}
|
||||
primOp->primOp->fun(*this, vArgs, v);
|
||||
} else {
|
||||
v.type = tPrimOpApp;
|
||||
v.primOpApp.left = allocValue();
|
||||
@@ -731,7 +732,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fun.type != tLambda)
|
||||
throwTypeError("attempt to call something which is not a function but %1%",
|
||||
showType(fun));
|
||||
@@ -749,7 +750,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
|
||||
else {
|
||||
forceAttrs(arg);
|
||||
|
||||
|
||||
if (!fun.lambda.fun->arg.empty())
|
||||
env2.values[displ++] = &arg;
|
||||
|
||||
@@ -782,7 +783,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
|
||||
nrFunctionCalls++;
|
||||
if (countCalls) functionCalls[fun.lambda.fun->pos]++;
|
||||
if (countCalls) functionCalls[fun.lambda.fun]++;
|
||||
|
||||
try {
|
||||
fun.lambda.fun->body->eval(*this, env2, v);
|
||||
@@ -824,9 +825,8 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||
Env & env2(state.allocEnv(1));
|
||||
env2.up = &env;
|
||||
env2.prevWith = prevWith;
|
||||
|
||||
env2.values[0] = state.allocValue();
|
||||
state.evalAttrs(env, attrs, *env2.values[0]);
|
||||
env2.haveWithAttrs = false;
|
||||
env2.values[0] = (Value *) attrs;
|
||||
|
||||
body->eval(state, env2, v);
|
||||
}
|
||||
@@ -837,7 +837,7 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||
(state.evalBool(env, cond) ? then : else_)->eval(state, env, v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
if (!state.evalBool(env, cond))
|
||||
@@ -845,7 +845,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
body->eval(state, env, v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
mkBool(v, !state.evalBool(env, e));
|
||||
@@ -899,9 +899,8 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
state.mkAttrs(v, v1.attrs->size() + v2.attrs->size());
|
||||
|
||||
/* Merge the attribute sets, preferring values from the second
|
||||
set. Make sure to keep the resulting vector in sorted
|
||||
order. */
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
Bindings::iterator i = v1.attrs->begin();
|
||||
Bindings::iterator j = v2.attrs->begin();
|
||||
|
||||
@@ -918,7 +917,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
while (i != v1.attrs->end()) v.attrs->push_back(*i++);
|
||||
while (j != v2.attrs->end()) v.attrs->push_back(*j++);
|
||||
|
||||
|
||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||
}
|
||||
|
||||
@@ -963,31 +962,39 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
std::ostringstream s;
|
||||
|
||||
bool first = true, isPath = false;
|
||||
Value vStr;
|
||||
NixInt n = 0;
|
||||
|
||||
bool first = !forceString;
|
||||
ValueType firstType = tString;
|
||||
|
||||
foreach (vector<Expr *>::iterator, i, *es) {
|
||||
(*i)->eval(state, env, vStr);
|
||||
Value vTmp;
|
||||
(*i)->eval(state, env, vTmp);
|
||||
|
||||
/* If the first element is a path, then the result will also
|
||||
be a path, we don't copy anything (yet - that's done later,
|
||||
since paths are copied when they are used in a derivation),
|
||||
and none of the strings are allowed to have contexts. */
|
||||
if (first) {
|
||||
isPath = !forceString && vStr.type == tPath;
|
||||
firstType = vTmp.type;
|
||||
first = false;
|
||||
}
|
||||
|
||||
s << state.coerceToString(vStr, context, false, !isPath);
|
||||
if (firstType == tInt) {
|
||||
if (vTmp.type != tInt)
|
||||
throwEvalError("cannot add %1% to an integer", showType(vTmp));
|
||||
n += vTmp.integer;
|
||||
} else
|
||||
s << state.coerceToString(vTmp, context, false, firstType == tString);
|
||||
}
|
||||
|
||||
if (isPath && !context.empty())
|
||||
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
|
||||
|
||||
if (isPath)
|
||||
if (firstType == tInt)
|
||||
mkInt(v, n);
|
||||
else if (firstType == tPath) {
|
||||
if (!context.empty())
|
||||
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
|
||||
mkPath(v, s.str().c_str());
|
||||
else
|
||||
} else
|
||||
mkString(v, s.str(), context);
|
||||
}
|
||||
|
||||
@@ -995,12 +1002,12 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
void EvalState::strictForceValue(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
strictForceValue(*i->value);
|
||||
}
|
||||
|
||||
|
||||
else if (v.type == tList) {
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
strictForceValue(*v.list.elems[n]);
|
||||
@@ -1008,7 +1015,7 @@ void EvalState::strictForceValue(Value & v)
|
||||
}
|
||||
|
||||
|
||||
int EvalState::forceInt(Value & v)
|
||||
NixInt EvalState::forceInt(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tInt)
|
||||
@@ -1046,7 +1053,7 @@ string EvalState::forceString(Value & v)
|
||||
void copyContext(const Value & v, PathSet & context)
|
||||
{
|
||||
if (v.string.context)
|
||||
for (const char * * p = v.string.context; *p; ++p)
|
||||
for (const char * * p = v.string.context; *p; ++p)
|
||||
context.insert(*p);
|
||||
}
|
||||
|
||||
@@ -1076,7 +1083,7 @@ bool EvalState::isDerivation(Value & v)
|
||||
if (i == v.attrs->end()) return false;
|
||||
forceValue(*i->value);
|
||||
if (i->value->type != tString) return false;
|
||||
return forceStringNoCtx(*i->value) == "derivation";
|
||||
return strcmp(i->value->string.s, "derivation") == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1096,7 +1103,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
Path path(canonPath(v.path));
|
||||
|
||||
if (!copyToStore) return path;
|
||||
|
||||
|
||||
if (nix::isDerivation(path))
|
||||
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
|
||||
|
||||
@@ -1118,8 +1125,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end())
|
||||
throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
|
||||
if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string");
|
||||
return coerceToString(*i->value, context, coerceMore, copyToStore);
|
||||
}
|
||||
|
||||
@@ -1145,7 +1151,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throwTypeError("cannot coerce %1% to a string", showType(v));
|
||||
}
|
||||
|
||||
@@ -1165,9 +1171,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
forceValue(v2);
|
||||
|
||||
/* !!! Hack to support some old broken code that relies on pointer
|
||||
equality tests between attribute sets. (Specifically,
|
||||
builderDefs calls uniqList on a list of attribute sets.) Will
|
||||
remove this eventually. */
|
||||
equality tests between sets. (Specifically, builderDefs calls
|
||||
uniqList on a list of sets.) Will remove this eventually. */
|
||||
if (&v1 == &v2) return true;
|
||||
|
||||
if (v1.type != v2.type) return false;
|
||||
@@ -1205,8 +1210,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
return true;
|
||||
|
||||
case tAttrs: {
|
||||
/* If both attribute sets denote a derivation (type =
|
||||
"derivation"), then compare their outPaths. */
|
||||
/* If both sets denote a derivation (type = "derivation"),
|
||||
then compare their outPaths. */
|
||||
if (isDerivation(v1) && isDerivation(v2)) {
|
||||
Bindings::iterator i = v1.attrs->find(sOutPath);
|
||||
Bindings::iterator j = v2.attrs->find(sOutPath);
|
||||
@@ -1221,7 +1226,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
||||
if (i->name != j->name || !eqValues(*i->value, *j->value))
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1256,10 +1261,11 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
|
||||
printMsg(v, format(" values allocated: %1% (%2% bytes)")
|
||||
% nrValues % (nrValues * sizeof(Value)));
|
||||
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
|
||||
printMsg(v, format(" sets allocated: %1%") % nrAttrsets);
|
||||
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
|
||||
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
|
||||
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
|
||||
printMsg(v, format(" size of symbol table: %1%") % symbols.totalSize());
|
||||
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
||||
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
||||
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
||||
@@ -1267,26 +1273,27 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
|
||||
|
||||
if (countCalls) {
|
||||
v = lvlInfo;
|
||||
|
||||
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
||||
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
||||
std::multimap<unsigned int, Symbol> primOpCalls_;
|
||||
PrimOpCalls_ primOpCalls_;
|
||||
foreach (PrimOpCalls::iterator, i, primOpCalls)
|
||||
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
|
||||
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
|
||||
typedef std::multimap<unsigned int, Pos> FunctionCalls_;
|
||||
std::multimap<unsigned int, Pos> functionCalls_;
|
||||
typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
|
||||
FunctionCalls_ functionCalls_;
|
||||
foreach (FunctionCalls::iterator, i, functionCalls)
|
||||
functionCalls_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
||||
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
|
||||
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
|
||||
|
||||
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
|
||||
typedef std::multimap<unsigned int, Pos> AttrSelects_;
|
||||
std::multimap<unsigned int, Pos> attrSelects_;
|
||||
AttrSelects_ attrSelects_;
|
||||
foreach (AttrSelects::iterator, i, attrSelects)
|
||||
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
||||
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
|
||||
|
||||
+39
-30
@@ -19,8 +19,8 @@ class EvalState;
|
||||
struct Attr;
|
||||
|
||||
|
||||
/* Attribute sets are represented as a vector of attributes, sorted by
|
||||
symbol (i.e. pointer to the attribute name in the symbol table). */
|
||||
/* Sets are represented as a vector of attributes, sorted by symbol
|
||||
(i.e. pointer to the attribute name in the symbol table). */
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::vector<Attr, gc_allocator<Attr> > BindingsBase;
|
||||
#else
|
||||
@@ -52,7 +52,8 @@ struct PrimOp
|
||||
struct Env
|
||||
{
|
||||
Env * up;
|
||||
unsigned int prevWith; // nr of levels up to next `with' environment
|
||||
unsigned short prevWith; // nr of levels up to next `with' environment
|
||||
bool haveWithAttrs;
|
||||
Value * values[0];
|
||||
};
|
||||
|
||||
@@ -87,23 +88,21 @@ struct EvalState;
|
||||
std::ostream & operator << (std::ostream & str, const Value & v);
|
||||
|
||||
|
||||
class EvalState
|
||||
class EvalState
|
||||
{
|
||||
public:
|
||||
SymbolTable symbols;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName,
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls;
|
||||
Symbol sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
bool repair;
|
||||
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
|
||||
/* A cache from path names to parse trees. */
|
||||
std::map<Path, Expr *> parseTrees;
|
||||
SrcToStore srcToStore;
|
||||
|
||||
/* A cache from path names to values. */
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -118,23 +117,25 @@ private:
|
||||
SearchPath::iterator searchPathInsertionPoint;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
EvalState();
|
||||
~EvalState();
|
||||
|
||||
void addToSearchPath(const string & s);
|
||||
|
||||
/* Parse a Nix expression from the specified file. If `path'
|
||||
refers to a directory, then "/default.nix" is appended. */
|
||||
Expr * parseExprFromFile(Path path);
|
||||
/* Parse a Nix expression from the specified file. */
|
||||
Expr * parseExprFromFile(const Path & path);
|
||||
|
||||
/* Parse a Nix expression from the specified string. */
|
||||
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
|
||||
Expr * parseExprFromString(const string & s, const Path & basePath);
|
||||
|
||||
|
||||
/* Evaluate an expression read from the given file to normal
|
||||
form. */
|
||||
void evalFile(const Path & path, Value & v);
|
||||
|
||||
void resetFileCache();
|
||||
|
||||
/* Look up a file in the search path. */
|
||||
Path findFile(const string & path);
|
||||
|
||||
@@ -158,7 +159,7 @@ public:
|
||||
void strictForceValue(Value & v);
|
||||
|
||||
/* Force `v', and then verify that it has the expected type. */
|
||||
int forceInt(Value & v);
|
||||
NixInt forceInt(Value & v);
|
||||
bool forceBool(Value & v);
|
||||
inline void forceAttrs(Value & v);
|
||||
inline void forceList(Value & v);
|
||||
@@ -174,7 +175,7 @@ public:
|
||||
/* String coercion. Converts strings, paths and derivations to a
|
||||
string. If `coerceMore' is set, also converts nulls, integers,
|
||||
booleans and lists to a string. If `copyToStore' is set,
|
||||
referenced paths are copied to the Nix store as a side effect.q */
|
||||
referenced paths are copied to the Nix store as a side effect. */
|
||||
string coerceToString(Value & v, PathSet & context,
|
||||
bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
@@ -183,39 +184,43 @@ public:
|
||||
path. Nothing is copied to the store. */
|
||||
Path coerceToPath(Value & v, PathSet & context);
|
||||
|
||||
private:
|
||||
public:
|
||||
|
||||
/* The base environment, containing the builtin functions and
|
||||
values. */
|
||||
Env & baseEnv;
|
||||
|
||||
unsigned int baseEnvDispl;
|
||||
|
||||
public:
|
||||
|
||||
/* The same, but used during parsing to resolve variables. */
|
||||
StaticEnv staticBaseEnv; // !!! should be private
|
||||
|
||||
private:
|
||||
|
||||
|
||||
unsigned int baseEnvDispl;
|
||||
|
||||
void createBaseEnv();
|
||||
|
||||
|
||||
void addConstant(const string & name, Value & v);
|
||||
|
||||
void addPrimOp(const string & name,
|
||||
unsigned int arity, PrimOpFun primOp);
|
||||
|
||||
inline Value * lookupVar(Env * env, const VarRef & var);
|
||||
|
||||
public:
|
||||
|
||||
void getBuiltin(const string & name, Value & v);
|
||||
|
||||
private:
|
||||
|
||||
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
|
||||
|
||||
friend class ExprVar;
|
||||
friend class ExprAttrs;
|
||||
friend class ExprLet;
|
||||
|
||||
Expr * parse(const char * text,
|
||||
const Path & path, const Path & basePath);
|
||||
Expr * parse(const char * text, const Path & path,
|
||||
const Path & basePath, StaticEnv & staticEnv);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/* Do a deep equality test between two values. That is, list
|
||||
elements and attributes are compared recursively. */
|
||||
bool eqValues(Value & v1, Value & v2);
|
||||
@@ -225,7 +230,7 @@ public:
|
||||
/* Automatically call a function for which each argument has a
|
||||
default value or has a binding in the `args' map. */
|
||||
void autoCallFunction(Bindings & args, Value & fun, Value & res);
|
||||
|
||||
|
||||
/* Allocation primitives. */
|
||||
Value * allocValue();
|
||||
Env & allocEnv(unsigned int size);
|
||||
@@ -259,7 +264,7 @@ private:
|
||||
typedef std::map<Symbol, unsigned int> PrimOpCalls;
|
||||
PrimOpCalls primOpCalls;
|
||||
|
||||
typedef std::map<Pos, unsigned int> FunctionCalls;
|
||||
typedef std::map<ExprLambda *, unsigned int> FunctionCalls;
|
||||
FunctionCalls functionCalls;
|
||||
|
||||
typedef std::map<Pos, unsigned int> AttrSelects;
|
||||
@@ -276,4 +281,8 @@ private:
|
||||
string showType(const Value & v);
|
||||
|
||||
|
||||
/* If `path' refers to a directory, then append "/default.nix". */
|
||||
Path resolveExprPath(Path path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+10
-12
@@ -41,7 +41,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
|
||||
|
||||
/* For each output... */
|
||||
for (unsigned int j = 0; j < i->value->list.length; ++j) {
|
||||
/* Evaluate the corresponding attribute set. */
|
||||
/* Evaluate the corresponding set. */
|
||||
string name = state.forceStringNoCtx(*i->value->list.elems[j]);
|
||||
Bindings::iterator out = attrs->find(state.symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
@@ -119,11 +119,10 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta)
|
||||
typedef set<Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to an attribute set of type
|
||||
`derivation', then put information about it in `drvs' (unless it's
|
||||
already in `doneExprs'). The result boolean indicates whether it
|
||||
makes sense for the caller to recursively search for derivations in
|
||||
`v'. */
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs' (unless it's already in
|
||||
`doneExprs'). The result boolean indicates whether it makes sense
|
||||
for the caller to recursively search for derivations in `v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const string & attrPath, DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
@@ -132,8 +131,8 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
state.forceValue(v);
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., an attribute set like
|
||||
`rec { x = derivation {...}; y = x;}'. */
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (done.find(v.attrs) != done.end()) return false;
|
||||
done.insert(v.attrs);
|
||||
|
||||
@@ -218,10 +217,9 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
if (combineChannels)
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
/* If the value of this attribute is itself an
|
||||
attribute set, should we recurse into it? => Only
|
||||
if it has a `recurseForDerivations = true'
|
||||
attribute. */
|
||||
/* If the value of this attribute is itself a set,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (v2.type == tAttrs) {
|
||||
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
|
||||
if (j != v2.attrs->end() && state.forceBool(*j->value))
|
||||
|
||||
+11
-7
@@ -15,14 +15,14 @@ using namespace nix;
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
static void initLoc(YYLTYPE * loc)
|
||||
{
|
||||
loc->first_line = loc->last_line = 1;
|
||||
loc->first_column = loc->last_column = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
{
|
||||
loc->first_line = loc->last_line;
|
||||
@@ -34,7 +34,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
if (*s == '\n') /* cr/lf */
|
||||
s++;
|
||||
/* fall through */
|
||||
case '\n':
|
||||
case '\n':
|
||||
++loc->last_line;
|
||||
loc->last_column = 1;
|
||||
break;
|
||||
@@ -68,7 +68,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
return new ExprString(symbols.create(t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#define YY_USER_INIT initLoc(yylloc)
|
||||
@@ -101,6 +101,8 @@ or { return OR_KW; }
|
||||
|
||||
\=\= { return EQ; }
|
||||
\!\= { return NEQ; }
|
||||
\<\= { return LEQ; }
|
||||
\>\= { return GEQ; }
|
||||
\&\& { return AND; }
|
||||
\|\| { return OR; }
|
||||
\-\> { return IMPL; }
|
||||
@@ -108,8 +110,10 @@ or { return OR_KW; }
|
||||
\+\+ { return CONCAT; }
|
||||
|
||||
{ID} { yylval->id = strdup(yytext); return ID; }
|
||||
{INT} { int n = atoi(yytext); /* !!! overflow */
|
||||
yylval->n = n;
|
||||
{INT} { errno = 0;
|
||||
yylval->n = strtol(yytext, 0, 10);
|
||||
if (errno != 0)
|
||||
throw ParseError(format("invalid integer `%1%'") % yytext);
|
||||
return INT;
|
||||
}
|
||||
|
||||
@@ -167,7 +171,7 @@ or { return OR_KW; }
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Horrible, disgusting hack: allow the parser to set the scanner
|
||||
start condition back to STRING. Necessary in interpolations like
|
||||
"foo${expr}bar"; after the close brace we have to go back to the
|
||||
|
||||
@@ -55,7 +55,7 @@ static string nextComponent(string::const_iterator & p,
|
||||
else
|
||||
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
|
||||
s += *p++;
|
||||
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ int compareVersions(const string & v1, const string & v2)
|
||||
{
|
||||
string::const_iterator p1 = v1.begin();
|
||||
string::const_iterator p2 = v2.begin();
|
||||
|
||||
|
||||
while (p1 != v1.end() || p2 != v2.end()) {
|
||||
string c1 = nextComponent(p1, v1.end());
|
||||
string c2 = nextComponent(p2, v2.end());
|
||||
@@ -95,11 +95,10 @@ int compareVersions(const string & v1, const string & v2)
|
||||
DrvNames drvNamesFromArgs(const Strings & opArgs)
|
||||
{
|
||||
DrvNames result;
|
||||
for (Strings::const_iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
foreach (Strings::const_iterator, i, opArgs)
|
||||
result.push_back(DrvName(*i));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+27
-23
@@ -38,7 +38,7 @@ void ExprPath::show(std::ostream & str)
|
||||
|
||||
void ExprVar::show(std::ostream & str)
|
||||
{
|
||||
str << info.name;
|
||||
str << name;
|
||||
}
|
||||
|
||||
void ExprSelect::show(std::ostream & str)
|
||||
@@ -174,7 +174,7 @@ void ExprPath::bindVars(const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void VarRef::bind(const StaticEnv & env)
|
||||
void ExprVar::bindVars(const StaticEnv & env)
|
||||
{
|
||||
/* Check whether the variable appears in the environment. If so,
|
||||
set its level and displacement. */
|
||||
@@ -198,17 +198,12 @@ void VarRef::bind(const StaticEnv & env)
|
||||
/* Otherwise, the variable must be obtained from the nearest
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1) throw EvalError(format("undefined variable `%1%'") % name);
|
||||
if (withLevel == -1) throw UndefinedVarError(format("undefined variable `%1%' at %2%") % name % pos);
|
||||
|
||||
fromWith = true;
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
void ExprVar::bindVars(const StaticEnv & env)
|
||||
{
|
||||
info.bind(env);
|
||||
}
|
||||
|
||||
void ExprSelect::bindVars(const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
@@ -224,20 +219,18 @@ void ExprAttrs::bindVars(const StaticEnv & env)
|
||||
{
|
||||
if (recursive) {
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
unsigned int displ = 0;
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
||||
|
||||
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(newEnv);
|
||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
||||
}
|
||||
|
||||
else
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(env);
|
||||
i->second.e->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprList::bindVars(const StaticEnv & env)
|
||||
@@ -249,9 +242,9 @@ void ExprList::bindVars(const StaticEnv & env)
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
unsigned int displ = 0;
|
||||
|
||||
|
||||
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||
|
||||
if (matchAttrs) {
|
||||
@@ -268,15 +261,14 @@ void ExprLambda::bindVars(const StaticEnv & env)
|
||||
void ExprLet::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
unsigned int displ = 0;
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
||||
|
||||
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(newEnv);
|
||||
|
||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
||||
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
@@ -293,8 +285,8 @@ void ExprWith::bindVars(const StaticEnv & env)
|
||||
prevWith = level;
|
||||
break;
|
||||
}
|
||||
|
||||
attrs->bindVars(env);
|
||||
|
||||
attrs->bindVars(env);
|
||||
StaticEnv newEnv(true, &env);
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
@@ -344,4 +336,16 @@ string ExprLambda::showNamePos()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Symbol table. */
|
||||
|
||||
size_t SymbolTable::totalSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
foreach (Symbols::const_iterator, i, symbols)
|
||||
n += i->size();
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+16
-22
@@ -16,20 +16,23 @@ MakeError(ThrownError, AssertionError)
|
||||
MakeError(Abort, EvalError)
|
||||
MakeError(TypeError, EvalError)
|
||||
MakeError(ImportError, EvalError) // error building an imported derivation
|
||||
MakeError(UndefinedVarError, Error)
|
||||
|
||||
|
||||
/* Position objects. */
|
||||
|
||||
struct Pos
|
||||
{
|
||||
string file;
|
||||
Symbol file;
|
||||
unsigned int line, column;
|
||||
Pos() : line(0), column(0) { };
|
||||
Pos(const string & file, unsigned int line, unsigned int column)
|
||||
Pos(const Symbol & file, unsigned int line, unsigned int column)
|
||||
: file(file), line(line), column(column) { };
|
||||
bool operator < (const Pos & p2) const
|
||||
{
|
||||
int d = file.compare(p2.file);
|
||||
if (!line) return p2.line;
|
||||
if (!p2.line) return false;
|
||||
int d = ((string) file).compare((string) p2.file);
|
||||
if (d < 0) return true;
|
||||
if (d > 0) return false;
|
||||
if (line < p2.line) return true;
|
||||
@@ -75,9 +78,9 @@ std::ostream & operator << (std::ostream & str, Expr & e);
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
int n;
|
||||
NixInt n;
|
||||
Value v;
|
||||
ExprInt(int n) : n(n) { mkInt(v, n); };
|
||||
ExprInt(NixInt n) : n(n) { mkInt(v, n); };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
@@ -107,32 +110,25 @@ struct ExprPath : Expr
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
|
||||
struct VarRef
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
|
||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||
or function argument) or from a "with". */
|
||||
bool fromWith;
|
||||
|
||||
|
||||
/* In the former case, the value is obtained by going `level'
|
||||
levels up from the current environment and getting the
|
||||
`displ'th value in that environment. In the latter case, the
|
||||
value is obtained by getting the attribute named `name' from
|
||||
the attribute set stored in the environment that is `level'
|
||||
levels up from the current one.*/
|
||||
the set stored in the environment that is `level' levels up
|
||||
from the current one.*/
|
||||
unsigned int level;
|
||||
unsigned int displ;
|
||||
|
||||
VarRef() { };
|
||||
VarRef(const Symbol & name) : name(name) { };
|
||||
void bind(const StaticEnv & env);
|
||||
};
|
||||
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
VarRef info;
|
||||
ExprVar(const Symbol & name) : info(name) { };
|
||||
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
@@ -159,12 +155,10 @@ struct ExprAttrs : Expr
|
||||
bool recursive;
|
||||
struct AttrDef {
|
||||
bool inherited;
|
||||
Expr * e; // if not inherited
|
||||
VarRef var; // if inherited
|
||||
Expr * e;
|
||||
Pos pos;
|
||||
unsigned int displ; // displacement
|
||||
AttrDef(Expr * e, const Pos & pos) : inherited(false), e(e), pos(pos) { };
|
||||
AttrDef(const Symbol & name, const Pos & pos) : inherited(true), var(name), pos(pos) { };
|
||||
AttrDef(Expr * e, const Pos & pos, bool inherited=false) : inherited(inherited), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
};
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
|
||||
+58
-46
@@ -12,24 +12,24 @@
|
||||
%expect-rr 1
|
||||
|
||||
%code requires {
|
||||
|
||||
|
||||
#ifndef BISON_HEADER
|
||||
#define BISON_HEADER
|
||||
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct ParseData
|
||||
struct ParseData
|
||||
{
|
||||
EvalState & state;
|
||||
SymbolTable & symbols;
|
||||
Expr * result;
|
||||
Path basePath;
|
||||
Path path;
|
||||
Symbol path;
|
||||
string error;
|
||||
Symbol sLetBody;
|
||||
ParseData(EvalState & state)
|
||||
@@ -38,7 +38,7 @@ namespace nix {
|
||||
, sLetBody(symbols.create("<let-body>"))
|
||||
{ };
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#define YY_DECL int yylex \
|
||||
@@ -63,14 +63,14 @@ using namespace nix;
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
Expr * e, const Pos & pos)
|
||||
@@ -121,7 +121,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
{
|
||||
if (es.empty()) return new ExprString(symbols.create(""));
|
||||
|
||||
|
||||
/* 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.) */
|
||||
@@ -170,7 +170,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
string s2;
|
||||
for (unsigned int j = 0; j < e->s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
@@ -203,7 +203,8 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
/* If this is a single string, then don't do a concatenation. */
|
||||
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,13 +233,13 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
%}
|
||||
|
||||
%union {
|
||||
// !!! We're probably leaking stuff here.
|
||||
// !!! We're probably leaking stuff here.
|
||||
nix::Expr * e;
|
||||
nix::ExprList * list;
|
||||
nix::ExprAttrs * attrs;
|
||||
nix::Formals * formals;
|
||||
nix::Formal * formal;
|
||||
int n;
|
||||
nix::NixInt n;
|
||||
char * id; // !!! -> Symbol
|
||||
char * path;
|
||||
char * uri;
|
||||
@@ -269,12 +270,15 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
%left OR
|
||||
%left AND
|
||||
%nonassoc EQ NEQ
|
||||
%left '<' '>' LEQ GEQ
|
||||
%right UPDATE
|
||||
%left NEG
|
||||
%left '+'
|
||||
%left NOT
|
||||
%left '+' '-'
|
||||
%left '*' '/'
|
||||
%right CONCAT
|
||||
%nonassoc '?'
|
||||
%nonassoc '~'
|
||||
%nonassoc NEGATE
|
||||
|
||||
%%
|
||||
|
||||
@@ -306,9 +310,14 @@ expr_if
|
||||
;
|
||||
|
||||
expr_op
|
||||
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
|
||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), new ExprInt(0)), $2); }
|
||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||
| expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1)); }
|
||||
| expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3)); }
|
||||
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
|
||||
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
|
||||
@@ -320,6 +329,9 @@ expr_op
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(false, l);
|
||||
}
|
||||
| expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), $1), $3); }
|
||||
| expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__mul")), $1), $3); }
|
||||
| expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__div")), $1), $3); }
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
@@ -338,17 +350,17 @@ expr_select
|
||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||
expr_simple OR_KW
|
||||
{ $$ = new ExprApp($1, new ExprVar(data->symbols.create("or"))); }
|
||||
{ $$ = new ExprApp($1, new ExprVar(CUR_POS, data->symbols.create("or"))); }
|
||||
| expr_simple { $$ = $1; }
|
||||
;
|
||||
|
||||
expr_simple
|
||||
: ID { $$ = new ExprVar(data->symbols.create($1)); }
|
||||
: ID { $$ = new ExprVar(CUR_POS, data->symbols.create($1)); }
|
||||
| INT { $$ = new ExprInt($1); }
|
||||
| '"' string_parts '"' {
|
||||
/* For efficiency, and to simplify parse trees a bit. */
|
||||
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
|
||||
else if ($2->size() == 1) $$ = $2->front();
|
||||
else if ($2->size() == 1 && dynamic_cast<ExprString *>($2->front())) $$ = $2->front();
|
||||
else $$ = new ExprConcatStrings(true, $2);
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
@@ -361,10 +373,10 @@ expr_simple
|
||||
/* The file wasn't found in the search path. However, we can't
|
||||
throw an error here, because the expression might never be
|
||||
evaluated. So return an expression that lazily calls
|
||||
‘abort’. */
|
||||
‘throw’. */
|
||||
$$ = path2 == ""
|
||||
? (Expr * ) new ExprApp(
|
||||
new ExprVar(data->symbols.create("throw")),
|
||||
new ExprVar(noPos, data->symbols.create("throw")),
|
||||
new ExprString(data->symbols.create(
|
||||
(format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str())))
|
||||
: (Expr * ) new ExprPath(path2);
|
||||
@@ -402,7 +414,7 @@ binds
|
||||
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
|
||||
Pos pos = makeCurPos(@3, data);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(*i, pos);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, *i), pos, true);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
@@ -454,7 +466,7 @@ formal
|
||||
: ID { $$ = new Formal(data->symbols.create($1), 0); }
|
||||
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
|
||||
|
||||
@@ -467,34 +479,30 @@ formal
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
Expr * EvalState::parse(const char * text,
|
||||
const Path & path, const Path & basePath)
|
||||
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
ParseData data(*this);
|
||||
data.basePath = basePath;
|
||||
data.path = path;
|
||||
data.path = data.symbols.create(path);
|
||||
|
||||
yylex_init(&scanner);
|
||||
yy_scan_string(text, scanner);
|
||||
int res = yyparse(scanner, &data);
|
||||
yylex_destroy(scanner);
|
||||
|
||||
|
||||
if (res) throw ParseError(data.error);
|
||||
|
||||
try {
|
||||
data.result->bindVars(staticBaseEnv);
|
||||
} catch (Error & e) {
|
||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
}
|
||||
|
||||
data.result->bindVars(staticEnv);
|
||||
|
||||
return data.result;
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromFile(Path path)
|
||||
Path resolveExprPath(Path path)
|
||||
{
|
||||
assert(path[0] == '/');
|
||||
|
||||
@@ -512,21 +520,25 @@ Expr * EvalState::parseExprFromFile(Path path)
|
||||
if (S_ISDIR(st.st_mode))
|
||||
path = canonPath(path + "/default.nix");
|
||||
|
||||
/* Read and parse the input file, unless it's already in the parse
|
||||
tree cache. */
|
||||
Expr * e = parseTrees[path];
|
||||
if (!e) {
|
||||
e = parse(readFile(path).c_str(), path, dirOf(path));
|
||||
parseTrees[path] = e;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
return e;
|
||||
|
||||
Expr * EvalState::parseExprFromFile(const Path & path)
|
||||
{
|
||||
return parse(readFile(path).c_str(), path, dirOf(path), staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
return parse(s.c_str(), "(string)", basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
|
||||
{
|
||||
return parse(s.c_str(), "(string)", basePath);
|
||||
return parseExprFromString(s, basePath, staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
@@ -541,7 +553,7 @@ void EvalState::addToSearchPath(const string & s)
|
||||
prefix = string(s, 0, pos);
|
||||
path = string(s, pos + 1);
|
||||
}
|
||||
|
||||
|
||||
path = absPath(path);
|
||||
if (pathExists(path)) {
|
||||
debug(format("adding path `%1%' to the search path") % path);
|
||||
@@ -554,7 +566,7 @@ Path EvalState::findFile(const string & path)
|
||||
{
|
||||
foreach (SearchPath::iterator, i, searchPath) {
|
||||
Path res;
|
||||
if (i->first.empty())
|
||||
if (i->first.empty())
|
||||
res = i->second + "/" + path;
|
||||
else {
|
||||
if (path.compare(0, i->first.size(), i->first) != 0 ||
|
||||
|
||||
+100
-64
@@ -37,7 +37,7 @@ std::pair<string, string> decodeContext(const string & s)
|
||||
|
||||
|
||||
/* Load and evaluate an expression from path specified by the
|
||||
argument. */
|
||||
argument. */
|
||||
static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
@@ -56,7 +56,7 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
unsigned long long downloadSize, narSize;
|
||||
queryMissing(*store, singleton<PathSet>(ctx),
|
||||
willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
|
||||
|
||||
/* !!! If using a substitute, we only need to fetch
|
||||
the selected output of this derivation. */
|
||||
store->buildPaths(singleton<PathSet>(ctx));
|
||||
@@ -82,8 +82,7 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
w.attrs->sort();
|
||||
Value fun;
|
||||
state.mkThunk_(fun,
|
||||
state.parseExprFromFile(state.findFile("nix/imported-drv-to-derivation.nix")));
|
||||
state.evalFile(state.findFile("nix/imported-drv-to-derivation.nix"), fun);
|
||||
state.forceFunction(fun);
|
||||
mkApp(v, fun, w);
|
||||
state.forceAttrs(v);
|
||||
@@ -93,6 +92,30 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Return a string representing the type of the expression. */
|
||||
static void prim_typeOf(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
string t;
|
||||
switch (args[0]->type) {
|
||||
case tInt: t = "int"; break;
|
||||
case tBool: t = "bool"; break;
|
||||
case tString: t = "string"; break;
|
||||
case tPath: t = "path"; break;
|
||||
case tNull: t = "null"; break;
|
||||
case tAttrs: t = "set"; break;
|
||||
case tList: t = "list"; break;
|
||||
case tLambda:
|
||||
case tPrimOp:
|
||||
case tPrimOpApp:
|
||||
t = "lambda";
|
||||
break;
|
||||
default: abort();
|
||||
}
|
||||
mkString(v, state.symbols.create(t));
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is the null value. */
|
||||
static void prim_isNull(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
@@ -109,7 +132,7 @@ static void prim_isFunction(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an Int. */
|
||||
/* Determine whether the argument is an integer. */
|
||||
static void prim_isInt(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -117,7 +140,7 @@ static void prim_isInt(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an String. */
|
||||
/* Determine whether the argument is a string. */
|
||||
static void prim_isString(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -125,7 +148,7 @@ static void prim_isString(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an Bool. */
|
||||
/* Determine whether the argument is a Boolean. */
|
||||
static void prim_isBool(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -135,24 +158,31 @@ static void prim_isBool(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
struct CompareValues
|
||||
{
|
||||
bool operator () (const Value & v1, const Value & v2) const
|
||||
bool operator () (const Value * v1, const Value * v2) const
|
||||
{
|
||||
if (v1.type != v2.type)
|
||||
if (v1->type != v2->type)
|
||||
throw EvalError("cannot compare values of different types");
|
||||
switch (v1.type) {
|
||||
switch (v1->type) {
|
||||
case tInt:
|
||||
return v1.integer < v2.integer;
|
||||
return v1->integer < v2->integer;
|
||||
case tString:
|
||||
return strcmp(v1.string.s, v2.string.s) < 0;
|
||||
return strcmp(v1->string.s, v2->string.s) < 0;
|
||||
case tPath:
|
||||
return strcmp(v1.path, v2.path) < 0;
|
||||
return strcmp(v1->path, v2->path) < 0;
|
||||
default:
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(v1) % showType(v2));
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
typedef list<Value *, gc_allocator<Value *> > ValueList;
|
||||
#else
|
||||
typedef list<Value *> ValueList;
|
||||
#endif
|
||||
|
||||
|
||||
static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
startNest(nest, lvlDebug, "finding dependencies");
|
||||
@@ -166,7 +196,7 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError("attribute `startSet' required");
|
||||
state.forceList(*startSet->value);
|
||||
|
||||
list<Value *> workSet;
|
||||
ValueList workSet;
|
||||
for (unsigned int n = 0; n < startSet->value->list.length; ++n)
|
||||
workSet.push_back(startSet->value->list.elems[n]);
|
||||
|
||||
@@ -180,11 +210,13 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
/* Construct the closure by applying the operator to element of
|
||||
`workSet', adding the result to `workSet', continuing until
|
||||
no new elements are found. */
|
||||
list<Value> res;
|
||||
set<Value, CompareValues> doneKeys; // !!! use Value *?
|
||||
ValueList res;
|
||||
// `doneKeys' doesn't need to be a GC root, because its values are
|
||||
// reachable from res.
|
||||
set<Value *, CompareValues> doneKeys;
|
||||
while (!workSet.empty()) {
|
||||
Value * e = *(workSet.begin());
|
||||
workSet.pop_front();
|
||||
Value * e = *(workSet.begin());
|
||||
workSet.pop_front();
|
||||
|
||||
state.forceAttrs(*e);
|
||||
|
||||
@@ -194,10 +226,10 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError("attribute `key' required");
|
||||
state.forceValue(*key->value);
|
||||
|
||||
if (doneKeys.find(*key->value) != doneKeys.end()) continue;
|
||||
doneKeys.insert(*key->value);
|
||||
res.push_back(*e);
|
||||
|
||||
if (doneKeys.find(key->value) != doneKeys.end()) continue;
|
||||
doneKeys.insert(key->value);
|
||||
res.push_back(e);
|
||||
|
||||
/* Call the `operator' function with `e' as argument. */
|
||||
Value call;
|
||||
mkApp(call, *op->value, *e);
|
||||
@@ -213,8 +245,8 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
/* Create the result list. */
|
||||
state.mkList(v, res.size());
|
||||
unsigned int n = 0;
|
||||
foreach (list<Value>::iterator, i, res)
|
||||
*(v.list.elems[n++] = state.allocValue()) = *i;
|
||||
foreach (ValueList::iterator, i, res)
|
||||
v.list.elems[n++] = *i;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,17 +279,17 @@ static void prim_addErrorContext(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||
* else => {success=false; value=false;} */
|
||||
static void prim_tryEval(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.mkAttrs(v, 2);
|
||||
try {
|
||||
state.forceValue(*args[0]);
|
||||
v.attrs->push_back(Attr(state.symbols.create("value"), args[0]));
|
||||
v.attrs->push_back(Attr(state.sValue, args[0]));
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("success")), true);
|
||||
} catch (AssertionError & e) {
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("value")), false);
|
||||
mkBool(*state.allocAttr(v, state.sValue), false);
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("success")), false);
|
||||
}
|
||||
v.attrs->sort();
|
||||
@@ -395,10 +427,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n")
|
||||
% key % *i->pos);
|
||||
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
|
||||
% drvName % posDrvName);
|
||||
e.addPrefix(format("while evaluating the attribute `%1%' of the derivation `%2%' at %3%:\n")
|
||||
% key % drvName % posDrvName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -634,7 +664,7 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name);
|
||||
refs.insert(path);
|
||||
}
|
||||
|
||||
|
||||
Path storePath = settings.readOnlyMode
|
||||
? computeStorePathForText(name, contents, refs)
|
||||
: store->addTextToStore(name, contents, refs, state.repair);
|
||||
@@ -651,7 +681,7 @@ struct FilterFromExpr : PathFilter
|
||||
{
|
||||
EvalState & state;
|
||||
Value & filter;
|
||||
|
||||
|
||||
FilterFromExpr(EvalState & state, Value & filter)
|
||||
: state(state), filter(filter)
|
||||
{
|
||||
@@ -672,12 +702,12 @@ struct FilterFromExpr : PathFilter
|
||||
state.callFunction(filter, arg1, fun2);
|
||||
|
||||
Value arg2;
|
||||
mkString(arg2,
|
||||
mkString(arg2,
|
||||
S_ISREG(st.st_mode) ? "regular" :
|
||||
S_ISDIR(st.st_mode) ? "directory" :
|
||||
S_ISLNK(st.st_mode) ? "symlink" :
|
||||
"unknown" /* not supported, will fail! */);
|
||||
|
||||
|
||||
Value res;
|
||||
state.callFunction(fun2, arg2, res);
|
||||
|
||||
@@ -708,12 +738,12 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Attribute sets
|
||||
* Sets
|
||||
*************************************************************/
|
||||
|
||||
|
||||
/* Return the names of the attributes in an attribute set as a sorted
|
||||
list of strings. */
|
||||
/* Return the names of the attributes in a set as a sorted list of
|
||||
strings. */
|
||||
static void prim_attrNames(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0]);
|
||||
@@ -755,7 +785,7 @@ static void prim_hasAttr(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an attribute set. */
|
||||
/* Determine whether the argument is a set. */
|
||||
static void prim_isAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -786,10 +816,11 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Builds an attribute set from a list specifying (name, value)
|
||||
pairs. To be precise, a list [{name = "name1"; value = value1;}
|
||||
... {name = "nameN"; value = valueN;}] is transformed to {name1 =
|
||||
value1; ... nameN = valueN;}. */
|
||||
/* Builds a set from a list specifying (name, value) pairs. To be
|
||||
precise, a list [{name = "name1"; value = value1;} ... {name =
|
||||
"nameN"; value = valueN;}] is transformed to {name1 = value1;
|
||||
... nameN = valueN;}. In case of duplicate occurences of the same
|
||||
name, the first takes precedence. */
|
||||
static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
@@ -801,36 +832,35 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
|
||||
for (unsigned int i = 0; i < args[0]->list.length; ++i) {
|
||||
Value & v2(*args[0]->list.elems[i]);
|
||||
state.forceAttrs(v2);
|
||||
|
||||
|
||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||
if (j == v2.attrs->end())
|
||||
throw TypeError("`name' attribute missing in a call to `listToAttrs'");
|
||||
string name = state.forceStringNoCtx(*j->value);
|
||||
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create("value"));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError("`value' attribute missing in a call to `listToAttrs'");
|
||||
|
||||
Symbol sym = state.symbols.create(name);
|
||||
if (seen.find(sym) == seen.end()) {
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError("`value' attribute missing in a call to `listToAttrs'");
|
||||
|
||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||
seen.insert(sym);
|
||||
}
|
||||
/* !!! Throw an error if `name' already exists? */
|
||||
}
|
||||
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
||||
|
||||
/* Return the right-biased intersection of two attribute sets as1 and
|
||||
as2, i.e. a set that contains every attribute from as2 that is also
|
||||
a member of as1. */
|
||||
/* Return the right-biased intersection of two sets as1 and as2,
|
||||
i.e. a set that contains every attribute from as2 that is also a
|
||||
member of as1. */
|
||||
static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0]);
|
||||
state.forceAttrs(*args[1]);
|
||||
|
||||
|
||||
state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
|
||||
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
@@ -933,7 +963,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
|
||||
state.mkList(v, args[1]->list.length);
|
||||
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
mkApp(*(v.list.elems[n] = state.allocValue()),
|
||||
mkApp(*(v.list.elems[n] = state.allocValue()),
|
||||
*args[0], *args[1]->list.elems[n]);
|
||||
}
|
||||
|
||||
@@ -1024,7 +1054,7 @@ static void prim_mul(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
static void prim_div(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
int i2 = state.forceInt(*args[1]);
|
||||
NixInt i2 = state.forceInt(*args[1]);
|
||||
if (i2 == 0) throw EvalError("division by zero");
|
||||
mkInt(v, state.forceInt(*args[0]) / i2);
|
||||
}
|
||||
@@ -1032,7 +1062,10 @@ static void prim_div(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
static void prim_lessThan(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
mkBool(v, state.forceInt(*args[0]) < state.forceInt(*args[1]));
|
||||
state.forceValue(*args[0]);
|
||||
state.forceValue(*args[1]);
|
||||
CompareValues comp;
|
||||
mkBool(v, comp(args[0], args[1]));
|
||||
}
|
||||
|
||||
|
||||
@@ -1102,7 +1135,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args
|
||||
if (p.at(0) == '=') p = "~" + string(p, 1);
|
||||
context2.insert(p);
|
||||
}
|
||||
|
||||
|
||||
mkString(v, s, context2);
|
||||
}
|
||||
|
||||
@@ -1164,10 +1197,10 @@ void EvalState::createBaseEnv()
|
||||
|
||||
mkBool(v, true);
|
||||
addConstant("true", v);
|
||||
|
||||
|
||||
mkBool(v, false);
|
||||
addConstant("false", v);
|
||||
|
||||
|
||||
v.type = tNull;
|
||||
addConstant("null", v);
|
||||
|
||||
@@ -1189,6 +1222,7 @@ void EvalState::createBaseEnv()
|
||||
|
||||
// Miscellaneous
|
||||
addPrimOp("import", 1, prim_import);
|
||||
addPrimOp("__typeOf", 1, prim_typeOf);
|
||||
addPrimOp("isNull", 1, prim_isNull);
|
||||
addPrimOp("__isFunction", 1, prim_isFunction);
|
||||
addPrimOp("__isString", 1, prim_isString);
|
||||
@@ -1215,7 +1249,7 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__toFile", 2, prim_toFile);
|
||||
addPrimOp("__filterSource", 2, prim_filterSource);
|
||||
|
||||
// Attribute sets
|
||||
// Sets
|
||||
addPrimOp("__attrNames", 1, prim_attrNames);
|
||||
addPrimOp("__getAttr", 2, prim_getAttr);
|
||||
addPrimOp("__hasAttr", 2, prim_hasAttr);
|
||||
@@ -1260,11 +1294,13 @@ void EvalState::createBaseEnv()
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
mkThunk_(v, parseExprFromFile(findFile("nix/derivation.nix")));
|
||||
string path = findFile("nix/derivation.nix");
|
||||
sDerivationNix = symbols.create(path);
|
||||
evalFile(path, v);
|
||||
addConstant("derivation", v);
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' attribute
|
||||
set, because attribute lookups expect it to be sorted. */
|
||||
/* Now that we've added all primops, sort the `builtins' set,
|
||||
because attribute lookups expect it to be sorted. */
|
||||
baseEnv.values[0]->attrs->sort();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
namespace nix {
|
||||
|
||||
/* Symbol table used by the parser and evaluator to represent and look
|
||||
up identifiers and attribute sets efficiently.
|
||||
SymbolTable::create() converts a string into a symbol. Symbols
|
||||
have the property that they can be compared efficiently (using a
|
||||
pointer equality test), because the symbol table stores only one
|
||||
copy of each string. */
|
||||
up identifiers and attributes efficiently. SymbolTable::create()
|
||||
converts a string into a symbol. Symbols have the property that
|
||||
they can be compared efficiently (using a pointer equality test),
|
||||
because the symbol table stores only one copy of each string. */
|
||||
|
||||
class Symbol
|
||||
{
|
||||
@@ -89,6 +88,8 @@ public:
|
||||
{
|
||||
return symbols.size();
|
||||
}
|
||||
|
||||
size_t totalSize() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+11
-8
@@ -31,17 +31,20 @@ struct PrimOp;
|
||||
struct Symbol;
|
||||
|
||||
|
||||
typedef long NixInt;
|
||||
|
||||
|
||||
struct Value
|
||||
{
|
||||
ValueType type;
|
||||
union
|
||||
union
|
||||
{
|
||||
int integer;
|
||||
NixInt integer;
|
||||
bool boolean;
|
||||
|
||||
/* Strings in the evaluator carry a so-called `context' (the
|
||||
ATermList) which is a list of strings representing store
|
||||
paths. This is to allow users to write things like
|
||||
|
||||
/* Strings in the evaluator carry a so-called `context' which
|
||||
is a list of strings representing store paths. This is to
|
||||
allow users to write things like
|
||||
|
||||
"--with-freetype2-library=" + freetype + "/lib"
|
||||
|
||||
@@ -63,7 +66,7 @@ struct Value
|
||||
const char * s;
|
||||
const char * * context; // must be in sorted order
|
||||
} string;
|
||||
|
||||
|
||||
const char * path;
|
||||
Bindings * attrs;
|
||||
struct {
|
||||
@@ -97,7 +100,7 @@ static inline void clearValue(Value & v)
|
||||
}
|
||||
|
||||
|
||||
static inline void mkInt(Value & v, int n)
|
||||
static inline void mkInt(Value & v, NixInt n)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tInt;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pkglib_LTLIBRARIES = libmain.la
|
||||
|
||||
libmain_la_SOURCES = shared.cc
|
||||
libmain_la_SOURCES = shared.cc stack.cc
|
||||
|
||||
libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#include <gc/gc.h>
|
||||
@@ -100,6 +101,9 @@ string getArg(const string & opt,
|
||||
}
|
||||
|
||||
|
||||
void detectStackOverflow();
|
||||
|
||||
|
||||
/* Initialize and reorder arguments, then call the actual argument
|
||||
processor. */
|
||||
static void initAndRun(int argc, char * * argv)
|
||||
@@ -131,6 +135,9 @@ static void initAndRun(int argc, char * * argv)
|
||||
if (sigaction(SIGCHLD, &act, 0))
|
||||
throw SysError("resetting SIGCHLD");
|
||||
|
||||
/* Register a SIGSEGV handler to detect stack overflows. */
|
||||
detectStackOverflow();
|
||||
|
||||
/* There is no privacy in the Nix system ;-) At least not for
|
||||
now. In particular, store objects should be readable by
|
||||
everybody. */
|
||||
@@ -296,6 +303,9 @@ int main(int argc, char * * argv)
|
||||
if (e.prefix() != "" && !showTrace)
|
||||
printMsg(lvlError, "(use `--show-trace' to show detailed location information)");
|
||||
return e.status;
|
||||
} catch (std::bad_alloc & e) {
|
||||
printMsg(lvlError, "error: out of memory");
|
||||
return 1;
|
||||
} catch (std::exception & e) {
|
||||
printMsg(lvlError, format("error: %1%") % e.what());
|
||||
return 1;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
|
||||
{
|
||||
/* Detect stack overflows by comparing the faulting address with
|
||||
the stack pointer. Unfortunately, getting the stack pointer is
|
||||
not portable. */
|
||||
bool haveSP = true;
|
||||
char * sp;
|
||||
#if defined(__x86_64__) && defined(REG_RSP)
|
||||
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_RSP];
|
||||
#elif defined(REG_ESP)
|
||||
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_ESP];
|
||||
#else
|
||||
haveSP = false;
|
||||
#endif
|
||||
|
||||
if (haveSP) {
|
||||
ptrdiff_t diff = (char *) info->si_addr - sp;
|
||||
if (diff < 0) diff = -diff;
|
||||
if (diff < 4096) {
|
||||
char msg[] = "error: stack overflow (possible infinite recursion)\n";
|
||||
write(2, msg, strlen(msg));
|
||||
_exit(1); // maybe abort instead?
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore default behaviour (i.e. segfault and dump core). */
|
||||
struct sigaction act;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_handler = SIG_DFL;
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGSEGV, &act, 0)) abort();
|
||||
}
|
||||
|
||||
|
||||
void detectStackOverflow()
|
||||
{
|
||||
#if defined(SA_SIGINFO) && defined (SA_ONSTACK)
|
||||
/* Install a SIGSEGV handler to detect stack overflows. This
|
||||
requires an alternative stack, otherwise the signal cannot be
|
||||
delivered when we're out of stack space. */
|
||||
stack_t stack;
|
||||
stack.ss_size = 4096 * 4 + MINSIGSTKSZ;
|
||||
stack.ss_sp = new char[stack.ss_size];
|
||||
if (!stack.ss_sp) throw Error("cannot allocate alternative stack");
|
||||
stack.ss_flags = 0;
|
||||
if (sigaltstack(&stack, 0) == -1) throw SysError("cannot set alternative stack");
|
||||
|
||||
struct sigaction act;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_sigaction = sigsegvHandler;
|
||||
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
if (sigaction(SIGSEGV, &act, 0))
|
||||
throw SysError("resetting SIGCHLD");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+60
-17
@@ -7,6 +7,7 @@
|
||||
#include "local-store.hh"
|
||||
#include "util.hh"
|
||||
#include "archive.hh"
|
||||
#include "affinity.hh"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -366,6 +368,8 @@ void Goal::trace(const format & f)
|
||||
/* Common initialisation performed in child processes. */
|
||||
static void commonChildInit(Pipe & logPipe)
|
||||
{
|
||||
restoreAffinity();
|
||||
|
||||
/* Put the child in a separate session (and thus a separate
|
||||
process group) so that it has no controlling terminal (meaning
|
||||
that e.g. ssh cannot open /dev/tty) and it doesn't receive
|
||||
@@ -568,6 +572,7 @@ static void runSetuidHelper(const string & command,
|
||||
args.push_back(0);
|
||||
|
||||
restoreSIGPIPE();
|
||||
restoreAffinity();
|
||||
|
||||
execve(program.c_str(), (char * *) &args[0], 0);
|
||||
throw SysError(format("executing `%1%'") % program);
|
||||
@@ -695,7 +700,9 @@ HookInstance::HookInstance()
|
||||
throw SysError("dupping builder's stdout/stderr");
|
||||
|
||||
execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
|
||||
(format("%1%") % settings.maxSilentTime).str().c_str(),
|
||||
(format("%1%") % settings.printBuildTrace).str().c_str(),
|
||||
(format("%1%") % settings.buildTimeout).str().c_str(),
|
||||
NULL);
|
||||
|
||||
throw SysError(format("executing `%1%'") % buildHook);
|
||||
@@ -806,6 +813,9 @@ private:
|
||||
BZFILE * bzLogFile;
|
||||
AutoCloseFD fdLogFile;
|
||||
|
||||
/* Number of bytes received from the builder's stdout/stderr. */
|
||||
unsigned long logSize;
|
||||
|
||||
/* Pipe for the builder's standard output/error. */
|
||||
Pipe builderOut;
|
||||
|
||||
@@ -986,7 +996,7 @@ void DerivationGoal::killChild()
|
||||
|
||||
void DerivationGoal::cancel(bool timeout)
|
||||
{
|
||||
if (timeout)
|
||||
if (settings.printBuildTrace && timeout)
|
||||
printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
|
||||
killChild();
|
||||
amDone(ecFailed);
|
||||
@@ -1072,7 +1082,7 @@ void DerivationGoal::haveDerivation()
|
||||
/* We are first going to try to create the invalid output paths
|
||||
through substitutes. If that doesn't work, we'll build
|
||||
them. */
|
||||
if (settings.useSubstitutes)
|
||||
if (settings.useSubstitutes && !willBuildLocally(drv))
|
||||
foreach (PathSet::iterator, i, invalidOutputs)
|
||||
addWaitee(worker.makeSubstitutionGoal(*i, repair));
|
||||
|
||||
@@ -1263,6 +1273,13 @@ PathSet outputPaths(const DerivationOutputs & outputs)
|
||||
}
|
||||
|
||||
|
||||
static string get(const StringPairs & map, const string & key)
|
||||
{
|
||||
StringPairs::const_iterator i = map.find(key);
|
||||
return i == map.end() ? (string) "" : i->second;
|
||||
}
|
||||
|
||||
|
||||
static bool canBuildLocally(const string & platform)
|
||||
{
|
||||
return platform == settings.thisSystem
|
||||
@@ -1273,6 +1290,12 @@ static bool canBuildLocally(const string & platform)
|
||||
}
|
||||
|
||||
|
||||
bool willBuildLocally(const Derivation & drv)
|
||||
{
|
||||
return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv.platform);
|
||||
}
|
||||
|
||||
|
||||
void DerivationGoal::tryToBuild()
|
||||
{
|
||||
trace("trying to build");
|
||||
@@ -1337,11 +1360,10 @@ void DerivationGoal::tryToBuild()
|
||||
|
||||
/* Don't do a remote build if the derivation has the attribute
|
||||
`preferLocalBuild' set. */
|
||||
bool preferLocalBuild =
|
||||
drv.env["preferLocalBuild"] == "1" && canBuildLocally(drv.platform);
|
||||
bool buildLocally = willBuildLocally(drv);
|
||||
|
||||
/* Is the build hook willing to accept this job? */
|
||||
if (!preferLocalBuild) {
|
||||
if (!buildLocally) {
|
||||
switch (tryBuildHook()) {
|
||||
case rpAccept:
|
||||
/* Yes, it has started doing so. Wait until we get
|
||||
@@ -1364,7 +1386,7 @@ void DerivationGoal::tryToBuild()
|
||||
derivation prefers to be done locally, do it even if
|
||||
maxBuildJobs is 0. */
|
||||
unsigned int curBuilds = worker.getNrLocalBuilds();
|
||||
if (curBuilds >= settings.maxBuildJobs && !(preferLocalBuild && curBuilds == 0)) {
|
||||
if (curBuilds >= settings.maxBuildJobs && !(buildLocally && curBuilds == 0)) {
|
||||
worker.waitForBuildSlot(shared_from_this());
|
||||
outputLocks.unlock();
|
||||
return;
|
||||
@@ -1587,9 +1609,8 @@ void DerivationGoal::buildDone()
|
||||
/* Release the build user, if applicable. */
|
||||
buildUser.release();
|
||||
|
||||
if (settings.printBuildTrace) {
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
|
||||
}
|
||||
|
||||
amDone(ecSuccess);
|
||||
}
|
||||
@@ -1605,7 +1626,7 @@ HookReply DerivationGoal::tryBuildHook()
|
||||
/* Tell the hook about system features (beyond the system type)
|
||||
required from the build machine. (The hook could parse the
|
||||
drv file itself, but this is easier.) */
|
||||
Strings features = tokenizeString<Strings>(drv.env["requiredSystemFeatures"]);
|
||||
Strings features = tokenizeString<Strings>(get(drv.env, "requiredSystemFeatures"));
|
||||
foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */
|
||||
|
||||
/* Send the request to the hook. */
|
||||
@@ -1667,7 +1688,7 @@ HookReply DerivationGoal::tryBuildHook()
|
||||
set<int> fds;
|
||||
fds.insert(hook->fromHook.readSide);
|
||||
fds.insert(hook->builderOut.readSide);
|
||||
worker.childStarted(shared_from_this(), hook->pid, fds, false, true);
|
||||
worker.childStarted(shared_from_this(), hook->pid, fds, false, false);
|
||||
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ build-started %1% - %2% %3%")
|
||||
@@ -1763,7 +1784,7 @@ void DerivationGoal::startBuilder()
|
||||
fixed-output derivations is by definition pure (since we
|
||||
already know the cryptographic hash of the output). */
|
||||
if (fixedOutput) {
|
||||
Strings varNames = tokenizeString<Strings>(drv.env["impureEnvVars"]);
|
||||
Strings varNames = tokenizeString<Strings>(get(drv.env, "impureEnvVars"));
|
||||
foreach (Strings::iterator, i, varNames) env[*i] = getEnv(*i);
|
||||
}
|
||||
|
||||
@@ -1774,7 +1795,7 @@ void DerivationGoal::startBuilder()
|
||||
temporary build directory. The text files have the format used
|
||||
by `nix-store --register-validity'. However, the deriver
|
||||
fields are left empty. */
|
||||
string s = drv.env["exportReferencesGraph"];
|
||||
string s = get(drv.env, "exportReferencesGraph");
|
||||
Strings ss = tokenizeString<Strings>(s);
|
||||
if (ss.size() % 2 != 0)
|
||||
throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s);
|
||||
@@ -1860,7 +1881,7 @@ void DerivationGoal::startBuilder()
|
||||
if (fixedOutput) useChroot = false;
|
||||
|
||||
/* Hack to allow derivations to disable chroot builds. */
|
||||
if (drv.env["__noChroot"] == "1") useChroot = false;
|
||||
if (get(drv.env, "__noChroot") == "1") useChroot = false;
|
||||
|
||||
if (useChroot) {
|
||||
#if CHROOT_ENABLED
|
||||
@@ -2105,11 +2126,19 @@ void DerivationGoal::initChild()
|
||||
filesystem that we want in the chroot
|
||||
environment. */
|
||||
foreach (DirsInChroot::iterator, i, dirsInChroot) {
|
||||
struct stat st;
|
||||
Path source = i->second;
|
||||
Path target = chrootRootDir + i->first;
|
||||
if (source == "/proc") continue; // backwards compatibility
|
||||
debug(format("bind mounting `%1%' to `%2%'") % source % target);
|
||||
createDirs(target);
|
||||
if (stat(source.c_str(), &st) == -1)
|
||||
throw SysError(format("getting attributes of path `%1%'") % source);
|
||||
if (S_ISDIR(st.st_mode))
|
||||
createDirs(target);
|
||||
else {
|
||||
createDirs(dirOf(target));
|
||||
writeFile(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);
|
||||
}
|
||||
@@ -2147,7 +2176,11 @@ void DerivationGoal::initChild()
|
||||
#ifdef CAN_DO_LINUX32_BUILDS
|
||||
/* Change the personality to 32-bit if we're doing an
|
||||
i686-linux build on an x86_64-linux machine. */
|
||||
if (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux") {
|
||||
struct utsname utsbuf;
|
||||
uname(&utsbuf);
|
||||
if (drv.platform == "i686-linux" &&
|
||||
(settings.thisSystem == "x86_64-linux" ||
|
||||
(!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64")))) {
|
||||
if (personality(0x0008 | 0x8000000 /* == PER_LINUX32_3GB */) == -1)
|
||||
throw SysError("cannot set i686-linux personality");
|
||||
}
|
||||
@@ -2339,7 +2372,7 @@ void DerivationGoal::computeClosure()
|
||||
refer to), check that all references are in that list. !!!
|
||||
allowedReferences should really be per-output. */
|
||||
if (drv.env.find("allowedReferences") != drv.env.end()) {
|
||||
PathSet allowed = parseReferenceSpecifiers(drv, drv.env["allowedReferences"]);
|
||||
PathSet allowed = parseReferenceSpecifiers(drv, get(drv.env, "allowedReferences"));
|
||||
foreach (PathSet::iterator, i, references)
|
||||
if (allowed.find(*i) == allowed.end())
|
||||
throw BuildError(format("output is not allowed to refer to path `%1%'") % *i);
|
||||
@@ -2372,6 +2405,8 @@ string drvsLogDir = "drvs";
|
||||
|
||||
Path DerivationGoal::openLogFile()
|
||||
{
|
||||
logSize = 0;
|
||||
|
||||
if (!settings.keepLog) return "";
|
||||
|
||||
string baseName = baseNameOf(drvPath);
|
||||
@@ -2447,6 +2482,14 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
|
||||
if ((hook && fd == hook->builderOut.readSide) ||
|
||||
(!hook && fd == builderOut.readSide))
|
||||
{
|
||||
logSize += data.size();
|
||||
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
||||
printMsg(lvlError,
|
||||
format("%1% killed after writing more than %2% bytes of log output")
|
||||
% getName() % settings.maxLogSize);
|
||||
cancel(true); // not really a timeout, but close enough
|
||||
return;
|
||||
}
|
||||
if (verbosity >= settings.buildVerbosity)
|
||||
writeToStderr(data);
|
||||
if (bzLogFile) {
|
||||
@@ -2605,7 +2648,7 @@ SubstitutionGoal::~SubstitutionGoal()
|
||||
|
||||
void SubstitutionGoal::cancel(bool timeout)
|
||||
{
|
||||
if (timeout)
|
||||
if (settings.printBuildTrace && timeout)
|
||||
printMsg(lvlError, format("@ substituter-failed %1% timeout") % storePath);
|
||||
if (pid != -1) {
|
||||
pid_t savedPid = pid;
|
||||
|
||||
+40
-42
@@ -258,8 +258,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
only succeed if the owning process has died. In that case
|
||||
we don't care about its temporary roots. */
|
||||
if (lockFile(*fd, ltWrite, false)) {
|
||||
printMsg(lvlError, format("removing stale temporary roots file `%1%'")
|
||||
% path);
|
||||
printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
|
||||
unlink(path.c_str());
|
||||
writeFull(*fd, (const unsigned char *) "d", 1);
|
||||
continue;
|
||||
@@ -290,46 +289,47 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
}
|
||||
|
||||
|
||||
static void findRoots(StoreAPI & store, const Path & path,
|
||||
bool recurseSymlinks, bool deleteStale, Roots & roots)
|
||||
static void foundRoot(StoreAPI & store,
|
||||
const Path & path, const Path & target, Roots & roots)
|
||||
{
|
||||
Path storePath = toStorePath(target);
|
||||
if (store.isValidPath(storePath))
|
||||
roots[path] = storePath;
|
||||
else
|
||||
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
|
||||
}
|
||||
|
||||
|
||||
static void findRoots(StoreAPI & store, const Path & path, Roots & roots)
|
||||
{
|
||||
try {
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % path);
|
||||
|
||||
printMsg(lvlVomit, format("looking at `%1%'") % path);
|
||||
struct stat st = lstat(path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
findRoots(store, path + "/" + *i, recurseSymlinks, deleteStale, roots);
|
||||
findRoots(store, path + "/" + *i, roots);
|
||||
}
|
||||
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
Path target = absPath(readLink(path), dirOf(path));
|
||||
Path target = readLink(path);
|
||||
if (isInStore(target))
|
||||
foundRoot(store, path, target, roots);
|
||||
|
||||
if (isInStore(target)) {
|
||||
debug(format("found root `%1%' in `%2%'")
|
||||
% target % path);
|
||||
Path storePath = toStorePath(target);
|
||||
if (store.isValidPath(storePath))
|
||||
roots[path] = storePath;
|
||||
else
|
||||
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
|
||||
% path % storePath);
|
||||
}
|
||||
|
||||
else if (recurseSymlinks) {
|
||||
if (pathExists(target))
|
||||
findRoots(store, target, false, deleteStale, roots);
|
||||
else if (deleteStale) {
|
||||
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
|
||||
/* Note that we only delete when recursing, i.e.,
|
||||
when we are still in the `gcroots' tree. We
|
||||
never delete stuff outside that tree. */
|
||||
unlink(path.c_str());
|
||||
/* Handle indirect roots. */
|
||||
else {
|
||||
target = absPath(target, dirOf(path));
|
||||
if (!pathExists(target)) {
|
||||
if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
|
||||
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
|
||||
unlink(path.c_str());
|
||||
}
|
||||
} else {
|
||||
struct stat st2 = lstat(target);
|
||||
if (!S_ISLNK(st2.st_mode)) return;
|
||||
Path target2 = readLink(target);
|
||||
if (isInStore(target2)) foundRoot(store, target, target2, roots);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,18 +346,16 @@ static void findRoots(StoreAPI & store, const Path & path,
|
||||
}
|
||||
|
||||
|
||||
static Roots findRoots(StoreAPI & store, bool deleteStale)
|
||||
{
|
||||
Roots roots;
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
|
||||
findRoots(store, rootsDir, true, deleteStale, roots);
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
Roots LocalStore::findRoots()
|
||||
{
|
||||
return nix::findRoots(*this, false);
|
||||
Roots roots;
|
||||
|
||||
/* Process direct roots in {gcroots,manifests,profiles}. */
|
||||
nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, roots);
|
||||
nix::findRoots(*this, settings.nixStateDir + "/manifests", roots);
|
||||
nix::findRoots(*this, settings.nixStateDir + "/profiles", roots);
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
@@ -637,7 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
/* Find the roots. Since we've grabbed the GC lock, the set of
|
||||
permanent roots cannot increase now. */
|
||||
printMsg(lvlError, format("finding garbage collector roots..."));
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(*this, true);
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
|
||||
|
||||
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ Settings::Settings()
|
||||
impersonateLinux26 = false;
|
||||
keepLog = true;
|
||||
compressLog = true;
|
||||
maxLogSize = 0;
|
||||
cacheFailure = false;
|
||||
pollInterval = 5;
|
||||
checkRootReachability = false;
|
||||
@@ -53,6 +54,7 @@ Settings::Settings()
|
||||
gcKeepDerivations = true;
|
||||
autoOptimiseStore = false;
|
||||
envKeepDerivations = false;
|
||||
lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
|
||||
}
|
||||
|
||||
|
||||
@@ -70,8 +72,10 @@ void Settings::processEnvironment()
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
#if 0
|
||||
if (getEnv("NIX_OTHER_STORES") != "")
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
|
||||
#endif
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
|
||||
} else
|
||||
@@ -138,6 +142,7 @@ void Settings::update()
|
||||
get(impersonateLinux26, "build-impersonate-linux-26");
|
||||
get(keepLog, "build-keep-log");
|
||||
get(compressLog, "build-compress-log");
|
||||
get(maxLogSize, "build-max-log-size");
|
||||
get(cacheFailure, "build-cache-failure");
|
||||
get(pollInterval, "build-poll-interval");
|
||||
get(checkRootReachability, "gc-check-reachability");
|
||||
|
||||
@@ -153,6 +153,10 @@ struct Settings {
|
||||
/* Whether to compress logs. */
|
||||
bool compressLog;
|
||||
|
||||
/* Maximum number of bytes a builder can write to stdout/stderr
|
||||
before being killed (0 means no limit). */
|
||||
unsigned long maxLogSize;
|
||||
|
||||
/* Whether to cache build failures. */
|
||||
bool cacheFailure;
|
||||
|
||||
@@ -179,6 +183,9 @@ struct Settings {
|
||||
(to prevent them from being GCed). */
|
||||
bool envKeepDerivations;
|
||||
|
||||
/* Whether to lock the Nix client and worker to the same CPU. */
|
||||
bool lockCPU;
|
||||
|
||||
private:
|
||||
SettingsMap settings, overrides;
|
||||
|
||||
|
||||
+243
-188
@@ -5,6 +5,7 @@
|
||||
#include "pathlocks.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "derivations.hh"
|
||||
#include "affinity.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
@@ -47,11 +48,15 @@ static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
{
|
||||
int err = sqlite3_errcode(db);
|
||||
if (err == SQLITE_BUSY) {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printMsg(lvlError, "warning: SQLite database is busy");
|
||||
warned = true;
|
||||
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
||||
if (err == SQLITE_PROTOCOL)
|
||||
printMsg(lvlError, "warning: SQLite database is busy (SQLITE_PROTOCOL)");
|
||||
else {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printMsg(lvlError, "warning: SQLite database is busy");
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
/* Sleep for a while since retrying the transaction right away
|
||||
is likely to fail again. */
|
||||
@@ -70,6 +75,11 @@ static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
}
|
||||
|
||||
|
||||
/* Convenience macros for retrying a SQLite transaction. */
|
||||
#define retry_sqlite while (1) { try {
|
||||
#define end_retry_sqlite break; } catch (SQLiteBusy & e) { } }
|
||||
|
||||
|
||||
SQLite::~SQLite()
|
||||
{
|
||||
try {
|
||||
@@ -317,8 +327,10 @@ LocalStore::~LocalStore()
|
||||
{
|
||||
try {
|
||||
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
|
||||
if (i->second.disabled) continue;
|
||||
i->second.to.close();
|
||||
i->second.from.close();
|
||||
i->second.error.close();
|
||||
i->second.pid.wait(true);
|
||||
}
|
||||
} catch (...) {
|
||||
@@ -378,10 +390,10 @@ void LocalStore::openDB(bool create)
|
||||
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "setting journal mode");
|
||||
|
||||
/* Increase the auto-checkpoint interval to 8192 pages. This
|
||||
/* Increase the auto-checkpoint interval to 40000 pages. This
|
||||
seems enough to ensure that instantiating the NixOS system
|
||||
derivation is done in a single fsync(). */
|
||||
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 8192;", 0, 0, 0) != SQLITE_OK)
|
||||
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
|
||||
throwSQLiteError(db, "setting autocheckpoint interval");
|
||||
|
||||
/* Initialise the database schema, if necessary. */
|
||||
@@ -409,7 +421,7 @@ void LocalStore::openDB(bool create)
|
||||
stmtInvalidatePath.create(db,
|
||||
"delete from ValidPaths where path = ?;");
|
||||
stmtRegisterFailedPath.create(db,
|
||||
"insert into FailedPaths (path, time) values (?, ?);");
|
||||
"insert or ignore into FailedPaths (path, time) values (?, ?);");
|
||||
stmtHasPathFailed.create(db,
|
||||
"select time from FailedPaths where path = ?;");
|
||||
stmtQueryFailedPaths.create(db,
|
||||
@@ -680,57 +692,64 @@ void LocalStore::addReference(unsigned long long referrer, unsigned long long re
|
||||
|
||||
void LocalStore::registerFailedPath(const Path & path)
|
||||
{
|
||||
if (hasPathFailed(path)) return;
|
||||
SQLiteStmtUse use(stmtRegisterFailedPath);
|
||||
stmtRegisterFailedPath.bind(path);
|
||||
stmtRegisterFailedPath.bind(time(0));
|
||||
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("registering failed path `%1%'") % path);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtRegisterFailedPath);
|
||||
stmtRegisterFailedPath.bind(path);
|
||||
stmtRegisterFailedPath.bind(time(0));
|
||||
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("registering failed path `%1%'") % path);
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::hasPathFailed(const Path & path)
|
||||
{
|
||||
SQLiteStmtUse use(stmtHasPathFailed);
|
||||
stmtHasPathFailed.bind(path);
|
||||
int res = sqlite3_step(stmtHasPathFailed);
|
||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
||||
throwSQLiteError(db, "querying whether path failed");
|
||||
return res == SQLITE_ROW;
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtHasPathFailed);
|
||||
stmtHasPathFailed.bind(path);
|
||||
int res = sqlite3_step(stmtHasPathFailed);
|
||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
||||
throwSQLiteError(db, "querying whether path failed");
|
||||
return res == SQLITE_ROW;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryFailedPaths()
|
||||
{
|
||||
SQLiteStmtUse use(stmtQueryFailedPaths);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryFailedPaths);
|
||||
|
||||
PathSet res;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
PathSet res;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error querying failed paths");
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error querying failed paths");
|
||||
|
||||
return res;
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::clearFailedPaths(const PathSet & paths)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
SQLiteStmtUse use(stmtClearFailedPath);
|
||||
stmtClearFailedPath.bind(*i);
|
||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
||||
}
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
SQLiteStmtUse use(stmtClearFailedPath);
|
||||
stmtClearFailedPath.bind(*i);
|
||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
txn.commit();
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -755,44 +774,47 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||
|
||||
assertStorePath(path);
|
||||
|
||||
/* Get the path info. */
|
||||
SQLiteStmtUse use1(stmtQueryPathInfo);
|
||||
retry_sqlite {
|
||||
|
||||
stmtQueryPathInfo.bind(path);
|
||||
/* Get the path info. */
|
||||
SQLiteStmtUse use1(stmtQueryPathInfo);
|
||||
|
||||
int r = sqlite3_step(stmtQueryPathInfo);
|
||||
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
||||
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
|
||||
stmtQueryPathInfo.bind(path);
|
||||
|
||||
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
||||
int r = sqlite3_step(stmtQueryPathInfo);
|
||||
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
||||
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
|
||||
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
|
||||
assert(s);
|
||||
info.hash = parseHashField(path, s);
|
||||
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
||||
|
||||
info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
|
||||
|
||||
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
||||
if (s) info.deriver = s;
|
||||
|
||||
/* Note that narSize = NULL yields 0. */
|
||||
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
|
||||
|
||||
/* Get the references. */
|
||||
SQLiteStmtUse use2(stmtQueryReferences);
|
||||
|
||||
stmtQueryReferences.bind(info.id);
|
||||
|
||||
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
||||
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
|
||||
assert(s);
|
||||
info.references.insert(s);
|
||||
}
|
||||
info.hash = parseHashField(path, s);
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
||||
info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
|
||||
|
||||
return info;
|
||||
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
||||
if (s) info.deriver = s;
|
||||
|
||||
/* Note that narSize = NULL yields 0. */
|
||||
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
|
||||
|
||||
/* Get the references. */
|
||||
SQLiteStmtUse use2(stmtQueryReferences);
|
||||
|
||||
stmtQueryReferences.bind(info.id);
|
||||
|
||||
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
||||
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
||||
assert(s);
|
||||
info.references.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
||||
|
||||
return info;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -823,7 +845,7 @@ unsigned long long LocalStore::queryValidPathId(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isValidPath(const Path & path)
|
||||
bool LocalStore::isValidPath_(const Path & path)
|
||||
{
|
||||
SQLiteStmtUse use(stmtQueryPathInfo);
|
||||
stmtQueryPathInfo.bind(path);
|
||||
@@ -834,33 +856,44 @@ bool LocalStore::isValidPath(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isValidPath(const Path & path)
|
||||
{
|
||||
retry_sqlite {
|
||||
return isValidPath_(path);
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
||||
{
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (isValidPath(*i)) res.insert(*i);
|
||||
return res;
|
||||
retry_sqlite {
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (isValidPath_(*i)) res.insert(*i);
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryAllValidPaths()
|
||||
{
|
||||
SQLiteStmt stmt;
|
||||
stmt.create(db, "select path from ValidPaths");
|
||||
retry_sqlite {
|
||||
SQLiteStmt stmt;
|
||||
stmt.create(db, "select path from ValidPaths");
|
||||
|
||||
PathSet res;
|
||||
PathSet res;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmt, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmt, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error getting valid paths");
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error getting valid paths");
|
||||
|
||||
return res;
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -872,10 +905,8 @@ void LocalStore::queryReferences(const Path & path,
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||
void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
SQLiteStmtUse use(stmtQueryReferrers);
|
||||
|
||||
stmtQueryReferrers.bind(path);
|
||||
@@ -892,6 +923,15 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||
{
|
||||
assertStorePath(path);
|
||||
retry_sqlite {
|
||||
queryReferrers_(path, referrers);
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
Path LocalStore::queryDeriver(const Path & path)
|
||||
{
|
||||
return queryPathInfo(path).deriver;
|
||||
@@ -902,65 +942,67 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
SQLiteStmtUse use(stmtQueryValidDerivers);
|
||||
stmtQueryValidDerivers.bind(path);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryValidDerivers);
|
||||
stmtQueryValidDerivers.bind(path);
|
||||
|
||||
PathSet derivers;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1);
|
||||
assert(s);
|
||||
derivers.insert(s);
|
||||
}
|
||||
PathSet derivers;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1);
|
||||
assert(s);
|
||||
derivers.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
||||
|
||||
return derivers;
|
||||
return derivers;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
PathSet outputs;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1);
|
||||
assert(s);
|
||||
outputs.insert(s);
|
||||
}
|
||||
|
||||
PathSet outputs;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1);
|
||||
assert(s);
|
||||
outputs.insert(s);
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
|
||||
|
||||
return outputs;
|
||||
return outputs;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
StringSet outputNames;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
|
||||
assert(s);
|
||||
outputNames.insert(s);
|
||||
}
|
||||
|
||||
StringSet outputNames;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
|
||||
assert(s);
|
||||
outputNames.insert(s);
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting output names of `%1%'") % path);
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting output names of `%1%'") % path);
|
||||
|
||||
return outputNames;
|
||||
return outputNames;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -968,19 +1010,19 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
|
||||
{
|
||||
if (hashPart.size() != 32) throw Error("invalid hash part");
|
||||
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
Path prefix = settings.nixStore + "/" + hashPart;
|
||||
|
||||
SQLiteStmtUse use(stmtQueryPathFromHashPart);
|
||||
stmtQueryPathFromHashPart.bind(prefix);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryPathFromHashPart);
|
||||
stmtQueryPathFromHashPart.bind(prefix);
|
||||
|
||||
int res = sqlite3_step(stmtQueryPathFromHashPart);
|
||||
if (res == SQLITE_DONE) return "";
|
||||
if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database");
|
||||
int res = sqlite3_step(stmtQueryPathFromHashPart);
|
||||
if (res == SQLITE_DONE) return "";
|
||||
if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database");
|
||||
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
||||
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
||||
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -988,7 +1030,7 @@ void LocalStore::setSubstituterEnv()
|
||||
{
|
||||
if (didSetSubstituterEnv) return;
|
||||
|
||||
/* Pass configuration options (including those overriden with
|
||||
/* Pass configuration options (including those overridden with
|
||||
--option) to substituters. */
|
||||
setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
|
||||
|
||||
@@ -998,7 +1040,7 @@ void LocalStore::setSubstituterEnv()
|
||||
|
||||
void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter & run)
|
||||
{
|
||||
if (run.pid != -1) return;
|
||||
if (run.disabled || run.pid != -1) return;
|
||||
|
||||
debug(format("starting substituter program `%1%'") % substituter);
|
||||
|
||||
@@ -1019,6 +1061,7 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||
|
||||
case 0: /* child */
|
||||
try {
|
||||
restoreAffinity();
|
||||
if (dup2(toPipe.readSide, STDIN_FILENO) == -1)
|
||||
throw SysError("dupping stdin");
|
||||
if (dup2(fromPipe.writeSide, STDOUT_FILENO) == -1)
|
||||
@@ -1039,6 +1082,23 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||
run.to = toPipe.writeSide.borrow();
|
||||
run.from = run.fromBuf.fd = fromPipe.readSide.borrow();
|
||||
run.error = errorPipe.readSide.borrow();
|
||||
|
||||
toPipe.readSide.close();
|
||||
fromPipe.writeSide.close();
|
||||
errorPipe.writeSide.close();
|
||||
|
||||
/* The substituter may exit right away if it's disabled in any way
|
||||
(e.g. copy-from-other-stores.pl will exit if no other stores
|
||||
are configured). */
|
||||
try {
|
||||
getLineFromSubstituter(run);
|
||||
} catch (EndOfFile & e) {
|
||||
run.to.close();
|
||||
run.from.close();
|
||||
run.error.close();
|
||||
run.disabled = true;
|
||||
if (run.pid.wait(true) != 0) throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1052,6 +1112,8 @@ string LocalStore::getLineFromSubstituter(RunningSubstituter & run)
|
||||
if (run.fromBuf.hasData()) goto haveData;
|
||||
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(run.from, &fds);
|
||||
@@ -1072,7 +1134,7 @@ string LocalStore::getLineFromSubstituter(RunningSubstituter & run)
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("reading from substituter's stderr");
|
||||
}
|
||||
if (n == 0) throw Error(format("substituter `%1%' died unexpectedly") % run.program);
|
||||
if (n == 0) throw EndOfFile(format("substituter `%1%' died unexpectedly") % run.program);
|
||||
err.append(buf, n);
|
||||
string::size_type p;
|
||||
while ((p = err.find('\n')) != string::npos) {
|
||||
@@ -1114,6 +1176,7 @@ PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||
if (res.size() == paths.size()) break;
|
||||
RunningSubstituter & run(runningSubstituters[*i]);
|
||||
startSubstituter(*i, run);
|
||||
if (run.disabled) continue;
|
||||
string s = "have ";
|
||||
foreach (PathSet::const_iterator, j, paths)
|
||||
if (res.find(*j) == res.end()) { s += *j; s += " "; }
|
||||
@@ -1137,6 +1200,7 @@ void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
||||
{
|
||||
RunningSubstituter & run(runningSubstituters[substituter]);
|
||||
startSubstituter(substituter, run);
|
||||
if (run.disabled) return;
|
||||
|
||||
string s = "info ";
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
@@ -1196,39 +1260,33 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
* expense of some speed of the path registering operation. */
|
||||
if (settings.syncBeforeRegistering) sync();
|
||||
|
||||
while (1) {
|
||||
try {
|
||||
SQLiteTxn txn(db);
|
||||
PathSet paths;
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
PathSet paths;
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
if (isValidPath(i->path))
|
||||
updatePathInfo(*i);
|
||||
else
|
||||
addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
unsigned long long referrer = queryValidPathId(i->path);
|
||||
foreach (PathSet::iterator, j, i->references)
|
||||
addReference(referrer, queryValidPathId(*j));
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
error if a cycle is detected and roll back the
|
||||
transaction. Cycles can only occur when a derivation
|
||||
has multiple outputs. */
|
||||
topoSortPaths(*this, paths);
|
||||
|
||||
txn.commit();
|
||||
break;
|
||||
} catch (SQLiteBusy & e) {
|
||||
/* Retry; the `txn' destructor will roll back the current
|
||||
transaction. */
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
if (isValidPath_(i->path))
|
||||
updatePathInfo(*i);
|
||||
else
|
||||
addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
unsigned long long referrer = queryValidPathId(i->path);
|
||||
foreach (PathSet::iterator, j, i->references)
|
||||
addReference(referrer, queryValidPathId(*j));
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
error if a cycle is detected and roll back the
|
||||
transaction. Cycles can only occur when a derivation
|
||||
has multiple outputs. */
|
||||
topoSortPaths(*this, paths);
|
||||
|
||||
txn.commit();
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -1619,23 +1677,20 @@ void LocalStore::invalidatePathChecked(const Path & path)
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
while (1) {
|
||||
try {
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
if (isValidPath(path)) {
|
||||
PathSet referrers; queryReferrers(path, referrers);
|
||||
referrers.erase(path); /* ignore self-references */
|
||||
if (!referrers.empty())
|
||||
throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
|
||||
% path % showPaths(referrers));
|
||||
invalidatePath(path);
|
||||
}
|
||||
if (isValidPath_(path)) {
|
||||
PathSet referrers; queryReferrers_(path, referrers);
|
||||
referrers.erase(path); /* ignore self-references */
|
||||
if (!referrers.empty())
|
||||
throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
|
||||
% path % showPaths(referrers));
|
||||
invalidatePath(path);
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
break;
|
||||
} catch (SQLiteBusy & e) { };
|
||||
}
|
||||
txn.commit();
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ struct RunningSubstituter
|
||||
Pid pid;
|
||||
AutoCloseFD to, from, error;
|
||||
FdSource fromBuf;
|
||||
bool disabled;
|
||||
RunningSubstituter() : disabled(false) { };
|
||||
};
|
||||
|
||||
|
||||
@@ -175,7 +177,7 @@ public:
|
||||
|
||||
/* 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
|
||||
path of a derivation, that it has been produced by a successful
|
||||
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. */
|
||||
@@ -302,6 +304,10 @@ private:
|
||||
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
|
||||
|
||||
void optimisePath_(OptimiseStats & stats, const Path & path);
|
||||
|
||||
// Internal versions that are not wrapped in retry_sqlite.
|
||||
bool isValidPath_(const Path & path);
|
||||
void queryReferrers_(const Path & path, PathSet & referrers);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -120,7 +120,8 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
||||
if (invalid.empty()) continue;
|
||||
|
||||
todoDrv.insert(*i);
|
||||
if (settings.useSubstitutes) query.insert(invalid.begin(), invalid.end());
|
||||
if (settings.useSubstitutes && !willBuildLocally(drv))
|
||||
query.insert(invalid.begin(), invalid.end());
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -143,7 +144,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
||||
|
||||
PathSet outputs;
|
||||
bool mustBuild = false;
|
||||
if (settings.useSubstitutes) {
|
||||
if (settings.useSubstitutes && !willBuildLocally(drv)) {
|
||||
foreach (DerivationOutputs::iterator, j, drv.outputs) {
|
||||
if (!wantOutput(j->first, i2.second)) continue;
|
||||
if (!store.isValidPath(j->second.path)) {
|
||||
|
||||
@@ -32,5 +32,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
||||
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
|
||||
unsigned long long & downloadSize, unsigned long long & narSize);
|
||||
|
||||
bool willBuildLocally(const Derivation & drv);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "remote-store.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "archive.hh"
|
||||
#include "affinity.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
@@ -15,7 +16,6 @@
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -71,8 +71,19 @@ void RemoteStore::openConnection(bool reserveSpace)
|
||||
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
||||
throw Error("Nix daemon protocol version not supported");
|
||||
writeInt(PROTOCOL_VERSION, to);
|
||||
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
|
||||
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
|
||||
if (cpu != -1) {
|
||||
writeInt(1, to);
|
||||
writeInt(cpu, to);
|
||||
} else
|
||||
writeInt(0, to);
|
||||
}
|
||||
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
|
||||
writeInt(reserveSpace, to);
|
||||
|
||||
processStderr();
|
||||
}
|
||||
catch (Error & e) {
|
||||
|
||||
@@ -18,10 +18,7 @@ GCOptions::GCOptions()
|
||||
|
||||
bool isInStore(const Path & path)
|
||||
{
|
||||
return path[0] == '/'
|
||||
&& string(path, 0, settings.nixStore.size()) == settings.nixStore
|
||||
&& path.size() >= settings.nixStore.size() + 2
|
||||
&& path[settings.nixStore.size()] == '/';
|
||||
return isInDir(path, settings.nixStore);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace nix {
|
||||
#define WORKER_MAGIC_1 0x6e697863
|
||||
#define WORKER_MAGIC_2 0x6478696f
|
||||
|
||||
#define PROTOCOL_VERSION 0x10d
|
||||
#define PROTOCOL_VERSION 0x10e
|
||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
pkglib_LTLIBRARIES = libutil.la
|
||||
|
||||
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
|
||||
archive.cc xml-writer.cc
|
||||
archive.cc xml-writer.cc affinity.cc
|
||||
|
||||
libutil_la_LIBADD = ../boost/format/libformat.la
|
||||
|
||||
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
|
||||
archive.hh xml-writer.hh types.hh
|
||||
archive.hh xml-writer.hh types.hh affinity.hh
|
||||
|
||||
if !HAVE_OPENSSL
|
||||
libutil_la_SOURCES += \
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "types.hh"
|
||||
#include "util.hh"
|
||||
#include "affinity.hh"
|
||||
|
||||
#if HAVE_SCHED_H
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
#if HAVE_SCHED_SETAFFINITY
|
||||
static bool didSaveAffinity = false;
|
||||
static cpu_set_t savedAffinity;
|
||||
#endif
|
||||
|
||||
|
||||
void setAffinityTo(int cpu)
|
||||
{
|
||||
#if HAVE_SCHED_SETAFFINITY
|
||||
if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
|
||||
didSaveAffinity = true;
|
||||
printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
|
||||
cpu_set_t newAffinity;
|
||||
CPU_ZERO(&newAffinity);
|
||||
CPU_SET(cpu, &newAffinity);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
|
||||
printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int lockToCurrentCPU()
|
||||
{
|
||||
#if HAVE_SCHED_SETAFFINITY
|
||||
int cpu = sched_getcpu();
|
||||
if (cpu != -1) setAffinityTo(cpu);
|
||||
return cpu;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void restoreAffinity()
|
||||
{
|
||||
#if HAVE_SCHED_SETAFFINITY
|
||||
if (!didSaveAffinity) return;
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
|
||||
printMsg(lvlError, "failed to restore affinity %1%");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace nix {
|
||||
|
||||
void setAffinityTo(int cpu);
|
||||
int lockToCurrentCPU();
|
||||
void restoreAffinity();
|
||||
|
||||
}
|
||||
@@ -282,6 +282,7 @@ struct RestoreSink : ParseSink
|
||||
void createRegularFile(const Path & path)
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
fd.close();
|
||||
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
if (fd == -1) throw SysError(format("creating file `%1%'") % p);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ struct BufferedSource : Source
|
||||
|
||||
size_t read(unsigned char * data, size_t len);
|
||||
|
||||
/* Underlying read call, to be overriden. */
|
||||
/* Underlying read call, to be overridden. */
|
||||
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
|
||||
|
||||
bool hasData();
|
||||
|
||||
+25
-10
@@ -12,6 +12,10 @@
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
@@ -148,6 +152,15 @@ string baseNameOf(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
bool isInDir(const Path & path, const Path & dir)
|
||||
{
|
||||
return path[0] == '/'
|
||||
&& string(path, 0, dir.size()) == dir
|
||||
&& path.size() >= dir.size() + 2
|
||||
&& path[dir.size()] == '/';
|
||||
}
|
||||
|
||||
|
||||
struct stat lstat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
@@ -616,8 +629,8 @@ AutoCloseFD::AutoCloseFD(int fd)
|
||||
|
||||
AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd)
|
||||
{
|
||||
/* Copying a AutoCloseFD isn't allowed (who should get to close
|
||||
it?). But as a edge case, allow copying of closed
|
||||
/* Copying an AutoCloseFD isn't allowed (who should get to close
|
||||
it?). But as an edge case, allow copying of closed
|
||||
AutoCloseFDs. This is necessary due to tiresome reasons
|
||||
involving copy constructor use on default object values in STL
|
||||
containers (like when you do `map[value]' where value isn't in
|
||||
@@ -790,6 +803,7 @@ void Pid::kill()
|
||||
|
||||
int Pid::wait(bool block)
|
||||
{
|
||||
assert(pid != -1);
|
||||
while (1) {
|
||||
int status;
|
||||
int res = waitpid(pid, &status, block ? 0 : WNOHANG);
|
||||
@@ -841,7 +855,16 @@ void killUser(uid_t uid)
|
||||
throw SysError("setting uid");
|
||||
|
||||
while (true) {
|
||||
#ifdef __APPLE__
|
||||
/* OSX's kill syscall takes a third parameter that, among other
|
||||
things, determines if kill(-1, signo) affects the calling
|
||||
process. In the OSX libc, it's set to true, which means
|
||||
"follow POSIX", which we don't want here
|
||||
*/
|
||||
if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break;
|
||||
#else
|
||||
if (kill(-1, SIGKILL) == 0) break;
|
||||
#endif
|
||||
if (errno == ESRCH) break; /* no more processes */
|
||||
if (errno != EINTR)
|
||||
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
||||
@@ -1060,14 +1083,6 @@ bool statusOk(int status)
|
||||
}
|
||||
|
||||
|
||||
string int2String(int n)
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << n;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
|
||||
bool hasSuffix(const string & s, const string & suffix)
|
||||
{
|
||||
return s.size() >= suffix.size() && string(s, s.size() - suffix.size()) == suffix;
|
||||
|
||||
+10
-1
@@ -45,6 +45,10 @@ Path dirOf(const Path & path);
|
||||
following the final `/'. */
|
||||
string baseNameOf(const Path & path);
|
||||
|
||||
/* Check whether a given path is a descendant of the given
|
||||
directory. */
|
||||
bool isInDir(const Path & path, const Path & dir);
|
||||
|
||||
/* Get status of `path'. */
|
||||
struct stat lstat(const Path & path);
|
||||
|
||||
@@ -315,7 +319,12 @@ template<class N> bool string2Int(const string & s, N & n)
|
||||
return str && str.get() == EOF;
|
||||
}
|
||||
|
||||
string int2String(int n);
|
||||
template<class N> string int2String(N n)
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << n;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
|
||||
/* Return true iff `s' ends in `suffix'. */
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "serialise.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "archive.hh"
|
||||
#include "affinity.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#include <cstring>
|
||||
@@ -25,8 +26,13 @@ using namespace nix;
|
||||
that lack it, we only notice the disconnection the next time we try
|
||||
to write to the client. So if you have a builder that never
|
||||
generates output on stdout/stderr, the daemon will never notice
|
||||
that the client has disconnected until the builder terminates. */
|
||||
#ifdef O_ASYNC
|
||||
that the client has disconnected until the builder terminates.
|
||||
|
||||
GNU/Hurd does have O_ASYNC, but its Unix-domain socket translator
|
||||
(pflocal) does not implement F_SETOWN. See
|
||||
<http://lists.gnu.org/archive/html/bug-guix/2013-07/msg00021.html> for
|
||||
details.*/
|
||||
#if defined O_ASYNC && !defined __GNU__
|
||||
#define HAVE_HUP_NOTIFICATION
|
||||
#ifndef SIGPOLL
|
||||
#define SIGPOLL SIGIO
|
||||
@@ -666,6 +672,9 @@ static void processConnection(bool trusted)
|
||||
to.flush();
|
||||
unsigned int clientVersion = readInt(from);
|
||||
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
|
||||
setAffinityTo(readInt(from));
|
||||
|
||||
bool reserveSpace = true;
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
|
||||
reserveSpace = readInt(from) != 0;
|
||||
@@ -713,7 +722,7 @@ static void processConnection(bool trusted)
|
||||
try {
|
||||
performOp(trusted, clientVersion, from, to, op);
|
||||
} catch (Error & e) {
|
||||
/* If we're not in a state were we can send replies, then
|
||||
/* If we're not in a state where we can send replies, then
|
||||
something went wrong processing the input of the
|
||||
client. This can happen especially if I/O errors occur
|
||||
during addTextToStore() / importPath(). If that
|
||||
@@ -722,6 +731,10 @@ static void processConnection(bool trusted)
|
||||
if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
|
||||
stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
|
||||
if (!errorAllowed) break;
|
||||
} catch (std::bad_alloc & e) {
|
||||
if (canSendStderr)
|
||||
stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
|
||||
throw;
|
||||
}
|
||||
|
||||
to.flush();
|
||||
@@ -884,7 +897,7 @@ static void daemonLoop()
|
||||
processConnection(trusted);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
writeToStderr("child error: " + string(e.what()) + "\n");
|
||||
writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+80
-60
@@ -54,6 +54,7 @@ struct Globals
|
||||
EvalState state;
|
||||
bool dryRun;
|
||||
bool preserveInstalled;
|
||||
bool removeAll;
|
||||
string forceName;
|
||||
bool prebuiltOnly;
|
||||
};
|
||||
@@ -94,18 +95,14 @@ static bool parseInstallSourceOptions(Globals & globals,
|
||||
}
|
||||
|
||||
|
||||
static bool isNixExpr(const Path & path)
|
||||
static bool isNixExpr(const Path & path, struct stat & st)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("getting information about `%1%'") % path);
|
||||
|
||||
return !S_ISDIR(st.st_mode) || pathExists(path + "/default.nix");
|
||||
return S_ISREG(st.st_mode) || (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix"));
|
||||
}
|
||||
|
||||
|
||||
static void getAllExprs(EvalState & state,
|
||||
const Path & path, ExprAttrs & attrs)
|
||||
const Path & path, StringSet & attrs, Value & v)
|
||||
{
|
||||
Strings names = readDirectory(path);
|
||||
StringSet namesSorted(names.begin(), names.end());
|
||||
@@ -122,7 +119,7 @@ static void getAllExprs(EvalState & state,
|
||||
if (stat(path2.c_str(), &st) == -1)
|
||||
continue; // ignore dangling symlinks in ~/.nix-defexpr
|
||||
|
||||
if (isNixExpr(path2)) {
|
||||
if (isNixExpr(path2, st) && (!S_ISREG(st.st_mode) || hasSuffix(path2, ".nix"))) {
|
||||
/* Strip off the `.nix' filename suffix (if applicable),
|
||||
otherwise the attribute cannot be selected with the
|
||||
`-A' option. Useful if you want to stick a Nix
|
||||
@@ -130,32 +127,50 @@ static void getAllExprs(EvalState & state,
|
||||
string attrName = *i;
|
||||
if (hasSuffix(attrName, ".nix"))
|
||||
attrName = string(attrName, 0, attrName.size() - 4);
|
||||
attrs.attrs[state.symbols.create(attrName)] =
|
||||
ExprAttrs::AttrDef(state.parseExprFromFile(absPath(path2)), noPos);
|
||||
if (attrs.find(attrName) != attrs.end()) {
|
||||
printMsg(lvlError, format("warning: name collision in input Nix expressions, skipping `%1%'") % path2);
|
||||
continue;
|
||||
}
|
||||
attrs.insert(attrName);
|
||||
/* Load the expression on demand. */
|
||||
Value & vFun(*state.allocValue());
|
||||
Value & vArg(*state.allocValue());
|
||||
state.getBuiltin("import", vFun);
|
||||
mkString(vArg, path2);
|
||||
mkApp(*state.allocAttr(v, state.symbols.create(attrName)), vFun, vArg);
|
||||
}
|
||||
else
|
||||
else if (S_ISDIR(st.st_mode))
|
||||
/* `path2' is a directory (with no default.nix in it);
|
||||
recurse into it. */
|
||||
getAllExprs(state, path2, attrs);
|
||||
getAllExprs(state, path2, attrs, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Expr * loadSourceExpr(EvalState & state, const Path & path)
|
||||
static void loadSourceExpr(EvalState & state, const Path & path, Value & v)
|
||||
{
|
||||
if (isNixExpr(path)) return state.parseExprFromFile(absPath(path));
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("getting information about `%1%'") % path);
|
||||
|
||||
if (isNixExpr(path, st)) {
|
||||
state.evalFile(path, v);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The path is a directory. Put the Nix expressions in the
|
||||
directory in an attribute set, with the file name of each
|
||||
expression as the attribute name. Recurse into subdirectories
|
||||
(but keep the attribute set flat, not nested, to make it easier
|
||||
for a user to have a ~/.nix-defexpr directory that includes
|
||||
some system-wide directory). */
|
||||
ExprAttrs * attrs = new ExprAttrs;
|
||||
attrs->attrs[state.symbols.create("_combineChannels")] =
|
||||
ExprAttrs::AttrDef(new ExprList(), noPos);
|
||||
getAllExprs(state, path, *attrs);
|
||||
return attrs;
|
||||
directory in a set, with the file name of each expression as
|
||||
the attribute name. Recurse into subdirectories (but keep the
|
||||
set flat, not nested, to make it easier for a user to have a
|
||||
~/.nix-defexpr directory that includes some system-wide
|
||||
directory). */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
state.mkAttrs(v, 16);
|
||||
state.mkList(*state.allocAttr(v, state.symbols.create("_combineChannels")), 0);
|
||||
StringSet attrs;
|
||||
getAllExprs(state, path, attrs, v);
|
||||
v.attrs->sort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +178,10 @@ static void loadDerivations(EvalState & state, Path nixExprPath,
|
||||
string systemFilter, Bindings & autoArgs,
|
||||
const string & pathPrefix, DrvInfos & elems)
|
||||
{
|
||||
Value v;
|
||||
findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v);
|
||||
Value vRoot;
|
||||
loadSourceExpr(state, nixExprPath, vRoot);
|
||||
|
||||
Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot));
|
||||
|
||||
getDerivations(state, v, pathPrefix, autoArgs, elems, true);
|
||||
|
||||
@@ -227,13 +244,13 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
||||
const Strings & args, bool newestOnly)
|
||||
{
|
||||
DrvNames selectors = drvNamesFromArgs(args);
|
||||
if (selectors.empty())
|
||||
selectors.push_back(DrvName("*"));
|
||||
|
||||
DrvInfos elems;
|
||||
set<unsigned int> done;
|
||||
|
||||
for (DrvNames::iterator i = selectors.begin();
|
||||
i != selectors.end(); ++i)
|
||||
{
|
||||
foreach (DrvNames::iterator, i, selectors) {
|
||||
typedef list<std::pair<DrvInfo, unsigned int> > Matches;
|
||||
Matches matches;
|
||||
unsigned int n = 0;
|
||||
@@ -305,8 +322,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
||||
}
|
||||
|
||||
/* Check that all selectors have been used. */
|
||||
for (DrvNames::iterator i = selectors.begin();
|
||||
i != selectors.end(); ++i)
|
||||
foreach (DrvNames::iterator, i, selectors)
|
||||
if (i->hits == 0 && i->fullName != "*")
|
||||
throw Error(format("selector `%1%' matches no derivations")
|
||||
% i->fullName);
|
||||
@@ -356,13 +372,15 @@ static void queryInstSources(EvalState & state,
|
||||
(import ./foo.nix)' = `(import ./foo.nix).bar'. */
|
||||
case srcNixExprs: {
|
||||
|
||||
Expr * e1 = loadSourceExpr(state, instSource.nixExprPath);
|
||||
Value vArg;
|
||||
loadSourceExpr(state, instSource.nixExprPath, vArg);
|
||||
|
||||
foreach (Strings::const_iterator, i, args) {
|
||||
Expr * e2 = state.parseExprFromString(*i, absPath("."));
|
||||
Expr * call = new ExprApp(e2, e1);
|
||||
Value v; state.eval(call, v);
|
||||
getDerivations(state, v, "", instSource.autoArgs, elems, true);
|
||||
Expr * eFun = state.parseExprFromString(*i, absPath("."));
|
||||
Value vFun, vTmp;
|
||||
state.eval(eFun, vFun);
|
||||
mkApp(vTmp, vFun, vArg);
|
||||
getDerivations(state, vTmp, "", instSource.autoArgs, elems, true);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -413,10 +431,10 @@ static void queryInstSources(EvalState & state,
|
||||
}
|
||||
|
||||
case srcAttrPath: {
|
||||
Value vRoot;
|
||||
loadSourceExpr(state, instSource.nixExprPath, vRoot);
|
||||
foreach (Strings::const_iterator, i, args) {
|
||||
Value v;
|
||||
findAlongAttrPath(state, *i, instSource.autoArgs,
|
||||
loadSourceExpr(state, instSource.nixExprPath), v);
|
||||
Value & v(*findAlongAttrPath(state, *i, instSource.autoArgs, vRoot));
|
||||
getDerivations(state, v, "", instSource.autoArgs, elems, true);
|
||||
}
|
||||
break;
|
||||
@@ -472,29 +490,31 @@ static void installDerivations(Globals & globals,
|
||||
newNames.insert(DrvName(i->name).name);
|
||||
}
|
||||
|
||||
/* Add in the already installed derivations, unless they have the
|
||||
same name as a to-be-installed element. */
|
||||
|
||||
while (true) {
|
||||
string lockToken = optimisticLockProfile(profile);
|
||||
|
||||
DrvInfos installedElems = queryInstalled(globals.state, profile);
|
||||
|
||||
DrvInfos allElems(newElems);
|
||||
foreach (DrvInfos::iterator, i, installedElems) {
|
||||
DrvName drvName(i->name);
|
||||
MetaInfo meta = i->queryMetaInfo(globals.state);
|
||||
if (!globals.preserveInstalled &&
|
||||
newNames.find(drvName.name) != newNames.end() &&
|
||||
!keep(meta))
|
||||
printMsg(lvlInfo,
|
||||
format("replacing old `%1%'") % i->name);
|
||||
else
|
||||
allElems.push_back(*i);
|
||||
}
|
||||
|
||||
foreach (DrvInfos::iterator, i, newElems)
|
||||
printMsg(lvlInfo, format("installing `%1%'") % i->name);
|
||||
/* Add in the already installed derivations, unless they have
|
||||
the same name as a to-be-installed element. */
|
||||
if (!globals.removeAll) {
|
||||
DrvInfos installedElems = queryInstalled(globals.state, profile);
|
||||
|
||||
foreach (DrvInfos::iterator, i, installedElems) {
|
||||
DrvName drvName(i->name);
|
||||
MetaInfo meta = i->queryMetaInfo(globals.state);
|
||||
if (!globals.preserveInstalled &&
|
||||
newNames.find(drvName.name) != newNames.end() &&
|
||||
!keep(meta))
|
||||
printMsg(lvlInfo, format("replacing old `%1%'") % i->name);
|
||||
else
|
||||
allElems.push_back(*i);
|
||||
}
|
||||
|
||||
foreach (DrvInfos::iterator, i, newElems)
|
||||
printMsg(lvlInfo, format("installing `%1%'") % i->name);
|
||||
}
|
||||
|
||||
printMissing(globals.state, newElems);
|
||||
|
||||
@@ -514,6 +534,8 @@ static void opInstall(Globals & globals,
|
||||
if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
|
||||
else if (arg == "--preserve-installed" || arg == "-P")
|
||||
globals.preserveInstalled = true;
|
||||
else if (arg == "--remove-all" || arg == "-r")
|
||||
globals.removeAll = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % arg);
|
||||
}
|
||||
|
||||
@@ -804,7 +826,7 @@ void printTable(Table & table)
|
||||
}
|
||||
|
||||
|
||||
/* This function compares the version of a element against the
|
||||
/* This function compares the version of an element against the
|
||||
versions in the given set of elements. `cvLess' means that only
|
||||
lower versions are in the set, `cvEqual' means that at most an
|
||||
equal version is in the set, and `cvGreater' means that there is at
|
||||
@@ -895,9 +917,6 @@ static void opQuery(Globals & globals,
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
if (remaining.size() == 0)
|
||||
printMsg(lvlInfo, "warning: you probably meant to specify the argument '*' to show all packages");
|
||||
|
||||
|
||||
/* Obtain derivation information from the specified source. */
|
||||
DrvInfos availElems, installedElems;
|
||||
@@ -1284,6 +1303,7 @@ void run(Strings args)
|
||||
|
||||
globals.dryRun = false;
|
||||
globals.preserveInstalled = false;
|
||||
globals.removeAll = false;
|
||||
globals.prebuiltOnly = false;
|
||||
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ) {
|
||||
|
||||
@@ -39,44 +39,48 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
bool parseOnly, bool strict, Bindings & autoArgs,
|
||||
bool evalOnly, bool xmlOutput, bool location, Expr * e)
|
||||
{
|
||||
if (parseOnly)
|
||||
if (parseOnly) {
|
||||
std::cout << format("%1%\n") % *e;
|
||||
else
|
||||
foreach (Strings::const_iterator, i, attrPaths) {
|
||||
Value v;
|
||||
findAlongAttrPath(state, *i, autoArgs, e, v);
|
||||
state.forceValue(v);
|
||||
return;
|
||||
}
|
||||
|
||||
PathSet context;
|
||||
if (evalOnly)
|
||||
if (xmlOutput)
|
||||
printValueAsXML(state, strict, location, v, std::cout, context);
|
||||
else {
|
||||
if (strict) state.strictForceValue(v);
|
||||
std::cout << v << std::endl;
|
||||
}
|
||||
Value vRoot;
|
||||
state.eval(e, vRoot);
|
||||
|
||||
foreach (Strings::const_iterator, i, attrPaths) {
|
||||
Value & v(*findAlongAttrPath(state, *i, autoArgs, vRoot));
|
||||
state.forceValue(v);
|
||||
|
||||
PathSet context;
|
||||
if (evalOnly)
|
||||
if (xmlOutput)
|
||||
printValueAsXML(state, strict, location, v, std::cout, context);
|
||||
else {
|
||||
DrvInfos drvs;
|
||||
getDerivations(state, v, "", autoArgs, drvs, false);
|
||||
foreach (DrvInfos::iterator, i, drvs) {
|
||||
Path drvPath = i->queryDrvPath(state);
|
||||
if (strict) state.strictForceValue(v);
|
||||
std::cout << v << std::endl;
|
||||
}
|
||||
else {
|
||||
DrvInfos drvs;
|
||||
getDerivations(state, v, "", autoArgs, drvs, false);
|
||||
foreach (DrvInfos::iterator, i, drvs) {
|
||||
Path drvPath = i->queryDrvPath(state);
|
||||
|
||||
/* What output do we want? */
|
||||
string outputName = i->queryOutputName(state);
|
||||
if (outputName == "")
|
||||
throw Error(format("derivation `%1%' lacks an `outputName' attribute ") % drvPath);
|
||||
/* What output do we want? */
|
||||
string outputName = i->queryOutputName(state);
|
||||
if (outputName == "")
|
||||
throw Error(format("derivation `%1%' lacks an `outputName' attribute ") % drvPath);
|
||||
|
||||
if (gcRoot == "")
|
||||
printGCWarning();
|
||||
else {
|
||||
Path rootName = gcRoot;
|
||||
if (++rootNr > 1) rootName += "-" + int2String(rootNr);
|
||||
drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot);
|
||||
}
|
||||
std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : "");
|
||||
if (gcRoot == "")
|
||||
printGCWarning();
|
||||
else {
|
||||
Path rootName = gcRoot;
|
||||
if (++rootNr > 1) rootName += "-" + int2String(rootNr);
|
||||
drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot);
|
||||
}
|
||||
std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +164,7 @@ void run(Strings args)
|
||||
files.push_back("./default.nix");
|
||||
|
||||
foreach (Strings::iterator, i, files) {
|
||||
Expr * e = state.parseExprFromFile(lookupFileArg(state, *i));
|
||||
Expr * e = state.parseExprFromFile(resolveExprPath(lookupFileArg(state, *i)));
|
||||
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
||||
evalOnly, xmlOutput, xmlOutputSourceLocation, e);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ clearStore
|
||||
nix-build --option binary-caches "file://$cacheDir" dependencies.nix --dry-run # get info
|
||||
|
||||
mkdir $cacheDir/tmp
|
||||
mv $cacheDir/*.nar.xz $cacheDir/tmp/
|
||||
mv $cacheDir/*.nar* $cacheDir/tmp/
|
||||
|
||||
NIX_DEBUG_SUBST=1 nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result --fallback
|
||||
|
||||
|
||||
@@ -17,5 +17,6 @@ in
|
||||
mkDerivation {
|
||||
name = "build-hook";
|
||||
builder = ./dependencies.builder0.sh;
|
||||
inherit input1 input2;
|
||||
input1 = " " + input1 + "/.";
|
||||
input2 = " ${input2}/.";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
[ "${input1: -2}" = /. ]
|
||||
[ "${input2: -2}" = /. ]
|
||||
|
||||
mkdir $out
|
||||
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ let {
|
||||
|
||||
input2 = mkDerivation {
|
||||
name = "dependencies-input-2";
|
||||
builder = ./dependencies.builder2.sh;
|
||||
builder = "${./dependencies.builder2.sh}";
|
||||
};
|
||||
|
||||
body = mkDerivation {
|
||||
name = "dependencies";
|
||||
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
||||
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
||||
input1 = input1 + "/.";
|
||||
inherit input2;
|
||||
input2 = "${input2}/.";
|
||||
meta.description = "Random test package";
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ for i in lang/eval-okay-*.nix; do
|
||||
if test -e lang/$i.flags; then
|
||||
flags=$(cat lang/$i.flags)
|
||||
fi
|
||||
if ! NIX_PATH=lang/dir3:lang/dir4:$NIX_PATH nix-instantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
|
||||
if ! NIX_PATH=lang/dir3:lang/dir4_PATH nix-instantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
|
||||
echo "FAIL: $i should evaluate"
|
||||
fail=1
|
||||
elif ! diff lang/$i.out lang/$i.exp; then
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# This must fail to evaluate, since ./fnord doesn't exist. If it did
|
||||
# exist, it would produce "/nix/store/<hash>-fnord/xyzzy" (with an
|
||||
# appropriate context).
|
||||
"${./fnord}/xyzzy"
|
||||
@@ -0,0 +1 @@
|
||||
"${x: x}"
|
||||
@@ -0,0 +1 @@
|
||||
"${./fnord}"
|
||||
@@ -0,0 +1 @@
|
||||
''${x: x}''
|
||||
@@ -1 +1 @@
|
||||
1275
|
||||
2188
|
||||
|
||||
@@ -8,14 +8,53 @@ let {
|
||||
else [first] ++ range (builtins.add first 1) last;
|
||||
|
||||
/* Supposedly tail recursive version:
|
||||
|
||||
range_ = accum: first: last:
|
||||
|
||||
range_ = accum: first: last:
|
||||
if first == last then ([first] ++ accum)
|
||||
else range_ ([first] ++ accum) (builtins.add first 1) last;
|
||||
|
||||
range = range_ [];
|
||||
*/
|
||||
|
||||
body = sum (range 1 50);
|
||||
x = 12;
|
||||
|
||||
err = abort "urgh";
|
||||
|
||||
body = sum
|
||||
[ (sum (range 1 50))
|
||||
(123 + 456)
|
||||
(0 + -10 + -(-11) + -x)
|
||||
(10 - 7 - -2)
|
||||
(10 - (6 - -1))
|
||||
(10 - 1 + 2)
|
||||
(3 * 4 * 5)
|
||||
(56088 / 123 / 2)
|
||||
(3 + 4 * const 5 0 - 6 / id 2)
|
||||
|
||||
(if 3 < 7 then 1 else err)
|
||||
(if 7 < 3 then err else 1)
|
||||
(if 3 < 3 then err else 1)
|
||||
|
||||
(if 3 <= 7 then 1 else err)
|
||||
(if 7 <= 3 then err else 1)
|
||||
(if 3 <= 3 then 1 else err)
|
||||
|
||||
(if 3 > 7 then err else 1)
|
||||
(if 7 > 3 then 1 else err)
|
||||
(if 3 > 3 then err else 1)
|
||||
|
||||
(if 3 >= 7 then err else 1)
|
||||
(if 7 >= 3 then 1 else err)
|
||||
(if 3 >= 3 then 1 else err)
|
||||
|
||||
(if 2 > 1 == 1 < 2 then 1 else err)
|
||||
(if 1 + 2 * 3 >= 7 then 1 else err)
|
||||
(if 1 + 2 * 3 < 7 then err else 1)
|
||||
|
||||
# Not integer, but so what.
|
||||
(if "aa" < "ab" then 1 else err)
|
||||
(if "aa" < "aa" then err else 1)
|
||||
(if "foo" < "foobar" then 1 else err)
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ let
|
||||
in
|
||||
|
||||
{ xyzzy2 ? xyzzy # mutually recursive args
|
||||
, xyzzy ? "blaat" # will be overriden by --argstr
|
||||
, xyzzy ? "blaat" # will be overridden by --argstr
|
||||
, fb ? foobar
|
||||
, lib # will be set by --arg
|
||||
}:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"b-overridden"
|
||||
@@ -0,0 +1,24 @@
|
||||
let
|
||||
pkgs_ = with pkgs; {
|
||||
a = derivation {
|
||||
name = "a";
|
||||
system = builtins.currentSystem;
|
||||
builder = "/bin/sh";
|
||||
args = [ "-c" "touch $out" ];
|
||||
inherit b;
|
||||
};
|
||||
|
||||
inherit b;
|
||||
};
|
||||
|
||||
packageOverrides = p: {
|
||||
b = derivation {
|
||||
name = "b-overridden";
|
||||
system = builtins.currentSystem;
|
||||
builder = "/bin/sh";
|
||||
args = [ "-c" "touch $out" ];
|
||||
};
|
||||
};
|
||||
|
||||
pkgs = pkgs_ // (packageOverrides pkgs_);
|
||||
in pkgs.a.b.name
|
||||
@@ -0,0 +1 @@
|
||||
"b-overridden b-overridden a"
|
||||
@@ -0,0 +1,29 @@
|
||||
let
|
||||
|
||||
pkgs_ = with pkgs; {
|
||||
a = derivation {
|
||||
name = "a";
|
||||
system = builtins.currentSystem;
|
||||
builder = "/bin/sh";
|
||||
args = [ "-c" "touch $out" ];
|
||||
inherit b;
|
||||
};
|
||||
|
||||
b = derivation {
|
||||
name = "b";
|
||||
system = builtins.currentSystem;
|
||||
builder = "/bin/sh";
|
||||
args = [ "-c" "touch $out" ];
|
||||
inherit a;
|
||||
};
|
||||
|
||||
c = b;
|
||||
};
|
||||
|
||||
packageOverrides = pkgs: with pkgs; {
|
||||
b = derivation (b.drvAttrs // { name = "${b.name}-overridden"; });
|
||||
};
|
||||
|
||||
pkgs = pkgs_ // (packageOverrides pkgs_);
|
||||
|
||||
in "${pkgs.a.b.name} ${pkgs.c.name} ${pkgs.b.a.name}"
|
||||
@@ -0,0 +1 @@
|
||||
[ true false true false true false true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
|
||||
@@ -0,0 +1,23 @@
|
||||
with builtins;
|
||||
|
||||
[ (isNull null)
|
||||
(isNull (x: x))
|
||||
(isFunction (x: x))
|
||||
(isFunction "fnord")
|
||||
(isString ("foo" + "bar"))
|
||||
(isString [ "x" ])
|
||||
(isInt (1 + 2))
|
||||
(isInt { x = 123; })
|
||||
(isBool (true && false))
|
||||
(isBool null)
|
||||
(typeOf (3 * 4))
|
||||
(typeOf true)
|
||||
(typeOf "xyzzy")
|
||||
(typeOf null)
|
||||
(typeOf { x = 456; })
|
||||
(typeOf [ 1 2 3 ])
|
||||
(typeOf (x: x))
|
||||
(typeOf ((x: y: x) 1))
|
||||
(typeOf map)
|
||||
(typeOf (map (x: x)))
|
||||
]
|
||||
@@ -49,4 +49,8 @@ rec {
|
||||
if comp (head list2) (head list1) then [(head list2)] ++ mergeLists comp list1 (tail list2) else
|
||||
[(head list1)] ++ mergeLists comp (tail list1) list2;
|
||||
|
||||
id = x: x;
|
||||
|
||||
const = x: y: x;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{ system, nix }:
|
||||
|
||||
with import <nixos/lib/testing.nix> { inherit system; };
|
||||
with import <nixpkgs/nixos/lib/testing.nix> { inherit system; };
|
||||
|
||||
makeTest ({ pkgs, ... }: let pkgA = pkgs.aterm; pkgB = pkgs.wget; in {
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{ system, nix }:
|
||||
|
||||
with import <nixos/lib/testing.nix> { inherit system; };
|
||||
with import <nixpkgs/nixos/lib/testing.nix> { inherit system; };
|
||||
|
||||
makeTest ({ pkgs, ... }:
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#! /bin/sh -e
|
||||
echo
|
||||
echo substituter args: $* >&2
|
||||
|
||||
if test $1 = "--query"; then
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#! /bin/sh -e
|
||||
echo
|
||||
echo substituter2 args: $* >&2
|
||||
|
||||
if test $1 = "--query"; then
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
echo "\`timeout' builder entering an infinite loop"
|
||||
while true ; do echo foo; sleep 1; done
|
||||
while true ; do echo -n .; done
|
||||
|
||||
+10
-13
@@ -2,23 +2,20 @@
|
||||
|
||||
source common.sh
|
||||
|
||||
drvPath=$(nix-instantiate timeout.nix)
|
||||
|
||||
test "$(nix-store -q --binding system "$drvPath")" = "$system"
|
||||
|
||||
echo "derivation is $drvPath"
|
||||
|
||||
failed=0
|
||||
messages="`nix-store -r --timeout 2 $drvPath 2>&1 || failed=1`"
|
||||
if test $failed -ne 0; then
|
||||
echo "error: \`nix-store' succeeded; should have timed out" >&2
|
||||
messages="`nix-build timeout.nix --timeout 2 2>&1 || failed=1`"
|
||||
if [ $failed -ne 0 ]; then
|
||||
echo "error: \`nix-store' succeeded; should have timed out"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! echo "$messages" | grep "timed out"; then
|
||||
echo "error: \`nix-store' may have failed for reasons other than timeout" >&2
|
||||
echo >&2
|
||||
echo "output of \`nix-store' follows:" >&2
|
||||
if ! echo "$messages" | grep -q "timed out"; then
|
||||
echo "error: build may have failed for reasons other than timeout; output:"
|
||||
echo "$messages" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if nix-build timeout.nix --option build-max-log-size 100; then
|
||||
echo "build should have failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user