Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
6016bcd30e | ||
|
|
1b6ee8f4c7 | ||
|
|
cd49ee0897 | ||
|
|
1e2c7c04b1 | ||
|
|
6cc2a8f8ed | ||
|
|
bfee9a2581 | ||
|
|
f9ff67e948 | ||
|
|
6b05f688ee | ||
|
|
5c06e5297d | ||
|
|
24a356bf71 | ||
|
|
24e063efdc | ||
|
|
ca70fba0bf | ||
|
|
5959c591a0 | ||
|
|
c5f9d0d080 | ||
|
|
75e12b8e66 | ||
|
|
f0576d6775 | ||
|
|
ff08306746 | ||
|
|
bc2e43f3c8 | ||
|
|
039d5a023f | ||
|
|
b09b87321c | ||
|
|
107505e13a | ||
|
|
72d8209548 | ||
|
|
18a48d80a0 | ||
|
|
1b3a03f161 | ||
|
|
229567293c | ||
|
|
a4cb62ac25 | ||
|
|
31a551a60f |
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
+29
-13
@@ -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
|
||||
@@ -325,7 +340,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
<listitem><para>A list of URLs of binary caches, separated by
|
||||
whitespace. The default is
|
||||
<literal>http://nixos.org/binary-cache</literal>.</para></listitem>
|
||||
<literal>http://cache.nixos.org</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -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
|
||||
|
||||
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>
|
||||
@@ -538,7 +538,7 @@ a symbolic link to the current <emphasis>user environment</emphasis>
|
||||
installed packages). The simplest way to set the required environment
|
||||
variables is to include the file
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
in your <filename>~/.bashrc</filename> (or similar), like this:</para>
|
||||
in your <filename>~/.profile</filename> (or similar), like this:</para>
|
||||
|
||||
<screen>
|
||||
source <replaceable>prefix</replaceable>/etc/profile.d/nix.sh</screen>
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
</author>
|
||||
|
||||
<copyright>
|
||||
<year>2004-2012</year>
|
||||
<year>2004-2013</year>
|
||||
<holder>Eelco Dolstra</holder>
|
||||
</copyright>
|
||||
|
||||
<date>May 2012</date>
|
||||
<date>July 2013</date>
|
||||
|
||||
</info>
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<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" />
|
||||
@@ -72,6 +73,7 @@
|
||||
<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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
+17
-3
@@ -209,6 +209,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 +276,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 +322,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>
|
||||
@@ -692,7 +706,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 +1258,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>
|
||||
|
||||
+11
-4
@@ -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
|
||||
@@ -252,7 +259,7 @@ The properties that are currently supported are:
|
||||
<listitem><para>Each binary cache has a priority (defaulting to
|
||||
50). Binary caches are checked for binaries in order of ascending
|
||||
priority; thus a higher number denotes a lower priority. The
|
||||
binary cache <uri>http://nixos.org/binary-cache</uri> has priority
|
||||
binary cache <uri>http://cache.nixos.org</uri> has priority
|
||||
40.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -270,14 +277,14 @@ URL <replaceable>url</replaceable> has a binary for
|
||||
<replaceable>p</replaceable>, Nix fetches
|
||||
<replaceable>url/h</replaceable>, where <replaceable>h</replaceable>
|
||||
is the hash part of <replaceable>p</replaceable>. Thus, if we have a
|
||||
cache <uri>http://nixos.org/binary-cache</uri> and we want to obtain
|
||||
cache <uri>http://cache.nixos.org</uri> and we want to obtain
|
||||
the store path
|
||||
<screen>
|
||||
/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
|
||||
</screen>
|
||||
then Nix will attempt to fetch
|
||||
<screen>
|
||||
http://nixos.org/binary-cache/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
|
||||
http://cache.nixos.org/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
|
||||
</screen>
|
||||
(Commands such as <command>nix-env -qas</command> will issue an HTTP
|
||||
HEAD request, since it only needs to know if the
|
||||
@@ -381,7 +388,7 @@ The fields are as follows:
|
||||
references exist (e.g.,
|
||||
<filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>),
|
||||
Nix will fetch <screen>
|
||||
http://nixos.org/binary-cache/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
http://cache.nixos.org/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
</screen> and decompress and unpack it to
|
||||
<filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -852,7 +852,7 @@ in Nix itself.</para>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--verify-paths</option> compares the
|
||||
<para>The operation <option>--verify-path</option> compares the
|
||||
contents of the given store paths to their cryptographic hashes stored
|
||||
in Nix’s database. For every changed path, it prints a warning
|
||||
message. The exit status is 0 if no path has changed, and 1
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ changed using
|
||||
|
||||
<listitem><para>You should add
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
to your <filename>~/.bashrc</filename> (or some other login
|
||||
to your <filename>~/.profile</filename> (or some other login
|
||||
file).</para></listitem>
|
||||
|
||||
<listitem><para>Subscribe to the Nix Packages channel.
|
||||
|
||||
@@ -6,6 +6,179 @@
|
||||
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.6.0"><title>Release 1.6.0 (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>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.3"><title>Release 1.5.3 (June 17, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. The following changes are
|
||||
noteworthy:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Yet another security bug involving hard links to files
|
||||
outside the store was fixed. This bug only affected multi-user
|
||||
installations that do not have hard link restrictions
|
||||
enabled. (NixOS is thus not vulnerable.)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The default binary cache URL has changed from
|
||||
<uri>http://nixos.org/binary-cache</uri> to
|
||||
<uri>http://cache.nixos.org</uri>. The latter is hosted on Amazon
|
||||
CloudFront (courtesy of <link
|
||||
xlink:href="http://www.logicblox.com/">LogicBlox</link>) and
|
||||
should provide better performance for users in both Europe and
|
||||
North America.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The binary cache substituter now prints a warning message if
|
||||
fetching information from the cache takes more than five seconds.
|
||||
Thus network or server problems no longer make Nix appear to just
|
||||
hang.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Stack traces now show function names, e.g.
|
||||
<screen>
|
||||
while evaluating `concatMapStrings' at `<replaceable>...</replaceable>/nixpkgs/pkgs/lib/strings.nix:18:25':
|
||||
</screen>
|
||||
Also, if a function is called with an unexpected argument, Nix
|
||||
now shows the name of the argument.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.2"><title>Release 1.5.2 (May 13, 2013)</title>
|
||||
@@ -107,10 +280,10 @@ Pernsteiner.</para>
|
||||
configuration setting <option>binary-caches</option> contains a
|
||||
list of URLs of binary caches. For instance, doing
|
||||
<screen>
|
||||
$ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
|
||||
$ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
|
||||
</screen>
|
||||
will install Thunderbird and its dependencies, using the available
|
||||
pre-built binaries in <uri>http://nixos.org/binary-cache</uri>.
|
||||
pre-built binaries in <uri>http://cache.nixos.org</uri>.
|
||||
The main advantage over the old “manifest”-based method of getting
|
||||
pre-built binaries is that you don’t have to worry about your
|
||||
manifest being in sync with the Nix expressions you’re installing
|
||||
@@ -123,7 +296,7 @@ $ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
|
||||
used automatically if you subscribe to that channel. If you use
|
||||
the Nixpkgs or NixOS channels
|
||||
(<uri>http://nixos.org/channels</uri>) you automatically get the
|
||||
cache <uri>http://nixos.org/binary-cache</uri>.</para>
|
||||
cache <uri>http://cache.nixos.org</uri>.</para>
|
||||
|
||||
<para>Binary caches are created using <command>nix-push</command>.
|
||||
For details on the operation and format of binary caches, see the
|
||||
@@ -1273,7 +1446,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
|
||||
@@ -1413,7 +1586,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
|
||||
@@ -1768,7 +1941,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>
|
||||
|
||||
@@ -1932,7 +2105,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -227,6 +227,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 +314,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());
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+12
-12
@@ -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"};
|
||||
@@ -197,10 +197,11 @@ REQ: while (1) {
|
||||
$hostName = $machine->{hostName};
|
||||
if (openSSHConnection($hostName)) {
|
||||
last REQ if system("ssh $hostName @sshOpts nix-builds-inhibited < /dev/null > /dev/null 2>&1") != 0;
|
||||
warn "machine `$hostName' is refusing builds, trying other available machines...\n";
|
||||
closeSSHConnection;
|
||||
} else {
|
||||
warn "unable to open SSH connection to `$hostName', trying other available machines...\n";
|
||||
}
|
||||
|
||||
warn "unable to open SSH connection to $hostName, trying other available machines...\n";
|
||||
$machine->{enabled} = 0;
|
||||
}
|
||||
}
|
||||
@@ -212,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;
|
||||
|
||||
|
||||
@@ -261,7 +261,8 @@ close UPLOADLOCK;
|
||||
|
||||
# Perform the build.
|
||||
my $buildFlags =
|
||||
" --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
|
||||
. " --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
@@ -271,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
|
||||
@@ -288,13 +290,11 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & exec nix-
|
||||
|
||||
|
||||
# Copy the output from the build machine.
|
||||
foreach my $output (@outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
next if isValidPath($output);
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" .
|
||||
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die "cannot copy $output from $hostName: $?";
|
||||
my @outputs2 = grep { !isValidPath($_) } @outputs;
|
||||
if (scalar @outputs2 > 0) {
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export @outputs2'" .
|
||||
"| NIX_HELD_LOCKS='@outputs2' @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die("cannot copy paths " . join(", ", @outputs) . " from `$hostName': $?");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,9 @@ my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
|
||||
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
|
||||
my $didExpiration = 0;
|
||||
|
||||
my $debug = ($ENV{"NIX_DEBUG_SUBST"} // "") eq 1;
|
||||
open(STDERR, ">>/dev/tty") if $debug;
|
||||
my $showAfter = 5; # show that we're waiting for a request after this many seconds
|
||||
|
||||
my $debug = ($Nix::Config::config{"debug-subst"} // "") eq 1 || ($Nix::Config::config{"untrusted-debug-subst"} // "") eq 1;
|
||||
|
||||
my $cacheFileURLs = ($ENV{"_NIX_CACHE_FILE_URLS"} // "") eq 1; # for testing
|
||||
|
||||
@@ -46,7 +47,8 @@ sub addRequest {
|
||||
|
||||
my $curl = WWW::Curl::Easy->new;
|
||||
my $curlId = $curlIdCount++;
|
||||
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET" };
|
||||
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET"
|
||||
, shown => 0, started => time() };
|
||||
|
||||
$curl->setopt(CURLOPT_PRIVATE, $curlId);
|
||||
$curl->setopt(CURLOPT_URL, $url);
|
||||
@@ -57,6 +59,7 @@ sub addRequest {
|
||||
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
|
||||
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
||||
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
||||
$curl->setopt(CURLOPT_TIMEOUT, int($ENV{"NIX_CONNECT_TIMEOUT"} // 0));
|
||||
|
||||
if ($activeRequests >= $maxParallelRequests) {
|
||||
$scheduled{$curlId} = 1;
|
||||
@@ -76,7 +79,7 @@ sub processRequests {
|
||||
|
||||
# Sleep until we can read or write some data.
|
||||
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
|
||||
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 0.1);
|
||||
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 1.0);
|
||||
}
|
||||
|
||||
if ($curlm->perform() != $activeRequests) {
|
||||
@@ -101,6 +104,16 @@ sub processRequests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $time = time();
|
||||
while (my ($key, $request) = each %requests) {
|
||||
next unless defined $request->{handle};
|
||||
next if $request->{shown};
|
||||
if ($time > $request->{started} + $showAfter) {
|
||||
print STDERR "still waiting for ‘$request->{url}’ after $showAfter seconds...\n";
|
||||
$request->{shown} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,17 +199,13 @@ 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);
|
||||
}
|
||||
|
||||
my @urls = strToList($Nix::Config::config{"binary-caches"} //
|
||||
($Nix::Config::storeDir eq "/nix/store" ? "http://nixos.org/binary-cache" : ""));
|
||||
($Nix::Config::storeDir eq "/nix/store" ? "http://cache.nixos.org" : ""));
|
||||
|
||||
my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
|
||||
// "$Nix::Config::stateDir/profiles/per-user/$userName/channels/binary-caches/*";
|
||||
@@ -225,7 +234,7 @@ sub getAvailableCaches {
|
||||
}
|
||||
}
|
||||
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-extra-binary-caches"};
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-extra-binary-caches"} // "";
|
||||
foreach my $url (@untrustedUrls) {
|
||||
next unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
|
||||
push @urls, $url;
|
||||
@@ -265,11 +274,12 @@ sub getAvailableCaches {
|
||||
elsif ($1 eq "Priority") { $priority = int($2); }
|
||||
}
|
||||
|
||||
$dbh->do("insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
||||
$dbh->do("insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
||||
{}, $url, time(), $storeDir, $wantMassQuery, $priority);
|
||||
my $id = $dbh->last_insert_id("", "", "", "");
|
||||
$queryCache->execute($url);
|
||||
$res = $queryCache->fetchrow_hashref() or die;
|
||||
next if $storeDir ne $Nix::Config::storeDir;
|
||||
push @caches, { id => $id, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
||||
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
||||
}
|
||||
|
||||
@caches = sort { $a->{priority} <=> $b->{priority} } @caches;
|
||||
@@ -288,7 +298,7 @@ sub processNARInfo {
|
||||
my ($storePath, $cache, $request) = @_;
|
||||
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
||||
print STDERR "could not download ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
@@ -467,7 +477,7 @@ sub printSubstitutablePaths {
|
||||
|
||||
foreach my $request (values %requests) {
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
||||
print STDERR "could not check ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
@@ -503,8 +513,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;
|
||||
@@ -512,7 +523,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;
|
||||
}
|
||||
@@ -529,6 +540,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";
|
||||
}
|
||||
|
||||
|
||||
+50
-16
@@ -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,6 +157,7 @@ foreach my $expr (@exprs) {
|
||||
|
||||
# Instantiate.
|
||||
my @drvPaths;
|
||||
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, $_;}
|
||||
@@ -156,10 +165,14 @@ foreach my $expr (@exprs) {
|
||||
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,37 @@ 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,
|
||||
'[ -e ~/.bashrc ] && source ~/.bashrc; ' .
|
||||
($pure ? '' : 'p=$PATH; ' ) .
|
||||
'dontAddDisableDepTrack=1; ' .
|
||||
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
||||
($pure ? '' : 'PATH=$PATH:$p; ') .
|
||||
'set +e; ' .
|
||||
'PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
||||
'unset NIX_ENFORCE_PURITY; ' .
|
||||
$envCommand);
|
||||
exec($ENV{NIX_BUILD_SHELL} // "bash", "--rcfile", $rcfile);
|
||||
die;
|
||||
}
|
||||
|
||||
@@ -206,7 +240,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";
|
||||
|
||||
+19
-18
@@ -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,7 +16,7 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
string curPath;
|
||||
|
||||
state.mkThunk_(v, e);
|
||||
Value * v = &vIn;
|
||||
|
||||
foreach (Strings::iterator, i, tokens) {
|
||||
|
||||
@@ -27,14 +26,14 @@ 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
|
||||
@@ -42,31 +41,33 @@ void findAlongAttrPath(EvalState & state, const string & 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));
|
||||
% 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);
|
||||
|
||||
}
|
||||
|
||||
+91
-64
@@ -141,10 +141,10 @@ EvalState::EvalState()
|
||||
, sOutputs(symbols.create("outputs"))
|
||||
, sOutputName(symbols.create("outputName"))
|
||||
, sIgnoreNulls(symbols.create("__ignoreNulls"))
|
||||
, baseEnv(allocEnv(128))
|
||||
, baseEnvDispl(0)
|
||||
, staticBaseEnv(false, 0)
|
||||
, repair(false)
|
||||
, baseEnv(allocEnv(128))
|
||||
, staticBaseEnv(false, 0)
|
||||
, baseEnvDispl(0)
|
||||
{
|
||||
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
||||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
@@ -158,7 +158,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 +222,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
|
||||
@@ -247,6 +253,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const
|
||||
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);
|
||||
@@ -299,23 +310,29 @@ void mkPath(Value & v, const char * s)
|
||||
}
|
||||
|
||||
|
||||
inline Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
||||
inline Value * EvalState::lookupVar(Env * env, const VarRef & var, bool noEval)
|
||||
{
|
||||
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
||||
|
||||
if (var.fromWith) {
|
||||
if (!var.fromWith) return env->values[var.displ];
|
||||
|
||||
while (1) {
|
||||
if (!env->haveWithAttrs) {
|
||||
if (noEval) return 0;
|
||||
Expr * attrs = (Expr *) env->values[0];
|
||||
env->values[0] = allocValue();
|
||||
evalAttrs(*env->up, attrs, *env->values[0]);
|
||||
env->haveWithAttrs = true;
|
||||
}
|
||||
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)
|
||||
if (!env->prevWith)
|
||||
throwEvalError("undefined variable `%1%'", var.name);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
} else
|
||||
return env->values[var.displ];
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +349,7 @@ 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;
|
||||
|
||||
@@ -400,7 +417,7 @@ unsigned long nrAvoided = 0;
|
||||
|
||||
Value * ExprVar::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
Value * v = state.lookupVar(&env, info);
|
||||
Value * v = state.lookupVar(&env, info, true);
|
||||
/* The value might not be initialised in the environment yet.
|
||||
In that case, ignore it. */
|
||||
if (v) { nrAvoided++; return v; }
|
||||
@@ -429,19 +446,30 @@ 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);
|
||||
Path path2 = resolveExprPath(path);
|
||||
|
||||
FileEvalCache::iterator i = fileEvalCache.find(path2);
|
||||
if (i != 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", path);
|
||||
addErrorPrefix(e, "while evaluating the file `%1%':\n", path2);
|
||||
throw;
|
||||
}
|
||||
fileEvalCache[path] = v;
|
||||
} else
|
||||
v = i->second;
|
||||
fileEvalCache[path2] = v;
|
||||
//if (path != path2) fileEvalCache[path2] = v;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::resetFileCache()
|
||||
{
|
||||
fileEvalCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -510,19 +538,13 @@ 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 {
|
||||
foreach (AttrDefs::iterator, i, attrs) {
|
||||
Value * vAttr;
|
||||
if (hasOverrides) {
|
||||
if (hasOverrides && !i->second.inherited) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i->second.e);
|
||||
} else
|
||||
vAttr = i->second.e->maybeThunk(state, env2);
|
||||
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));
|
||||
}
|
||||
@@ -552,9 +574,6 @@ 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));
|
||||
}
|
||||
}
|
||||
@@ -572,10 +591,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);
|
||||
}
|
||||
@@ -591,7 +607,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, info, false);
|
||||
state.forceValue(*v2);
|
||||
v = *v2;
|
||||
}
|
||||
@@ -755,8 +771,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i->name);
|
||||
if (j == arg.attrs->end()) {
|
||||
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
|
||||
fun.lambda.fun->pos, i->name);
|
||||
if (!i->def) throwTypeError("%1% called without required argument `%2%'",
|
||||
fun.lambda.fun->showNamePos(), i->name);
|
||||
env2.values[displ++] = i->def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
@@ -765,20 +781,24 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument (unless the attribute match specifies a `...').
|
||||
TODO: show the names of the expected/unexpected
|
||||
arguments. */
|
||||
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
|
||||
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
|
||||
argument (unless the attribute match specifies a `...'). */
|
||||
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
foreach (Bindings::iterator, i, *arg.attrs)
|
||||
if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end())
|
||||
throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
|
||||
nrFunctionCalls++;
|
||||
if (countCalls) functionCalls[fun.lambda.fun->pos]++;
|
||||
if (countCalls) functionCalls[fun.lambda.fun]++;
|
||||
|
||||
try {
|
||||
fun.lambda.fun->body->eval(*this, env2, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
|
||||
addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -815,9 +835,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);
|
||||
}
|
||||
@@ -954,31 +973,39 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
std::ostringstream s;
|
||||
NixInt n = 0;
|
||||
|
||||
bool first = true, isPath = false;
|
||||
Value vStr;
|
||||
bool first = true;
|
||||
ValueType firstType;
|
||||
|
||||
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 && !forceString) {
|
||||
if (vTmp.type != tInt)
|
||||
throwEvalError("cannot add %1% to an integer", showType(vTmp));
|
||||
n += vTmp.integer;
|
||||
} else
|
||||
s << state.coerceToString(vTmp, context, false, firstType != tPath);
|
||||
}
|
||||
|
||||
if (isPath && !context.empty())
|
||||
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());
|
||||
|
||||
if (isPath)
|
||||
mkPath(v, s.str().c_str());
|
||||
else
|
||||
} else
|
||||
mkString(v, s.str(), context);
|
||||
}
|
||||
|
||||
@@ -999,7 +1026,7 @@ void EvalState::strictForceValue(Value & v)
|
||||
}
|
||||
|
||||
|
||||
int EvalState::forceInt(Value & v)
|
||||
NixInt EvalState::forceInt(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tInt)
|
||||
@@ -1261,23 +1288,23 @@ void EvalState::printStats()
|
||||
|
||||
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_)
|
||||
|
||||
+26
-18
@@ -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];
|
||||
};
|
||||
|
||||
@@ -102,9 +103,6 @@ public:
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
|
||||
/* A cache from path names to parse trees. */
|
||||
std::map<Path, Expr *> parseTrees;
|
||||
|
||||
/* A cache from path names to values. */
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::map<Path, Value, std::less<Path>, gc_allocator<std::pair<const Path, Value> > > FileEvalCache;
|
||||
@@ -124,17 +122,19 @@ public:
|
||||
|
||||
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 +158,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 +174,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,21 +183,19 @@ 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);
|
||||
@@ -205,14 +203,20 @@ private:
|
||||
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 VarRef & 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:
|
||||
|
||||
@@ -259,7 +263,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 +280,8 @@ private:
|
||||
string showType(const Value & v);
|
||||
|
||||
|
||||
/* If `path' refers to a directory, then append "/default.nix". */
|
||||
Path resolveExprPath(Path path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+6
-2
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,7 @@ 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;
|
||||
}
|
||||
|
||||
+23
-6
@@ -230,14 +230,12 @@ void ExprAttrs::bindVars(const StaticEnv & env)
|
||||
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)
|
||||
@@ -274,8 +272,7 @@ void ExprLet::bindVars(const StaticEnv & env)
|
||||
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);
|
||||
}
|
||||
@@ -324,4 +321,24 @@ void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
}
|
||||
|
||||
|
||||
/* Storing function names. */
|
||||
|
||||
void Expr::setName(Symbol & name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ExprLambda::setName(Symbol & name)
|
||||
{
|
||||
this->name = name;
|
||||
body->setName(name);
|
||||
}
|
||||
|
||||
|
||||
string ExprLambda::showNamePos()
|
||||
{
|
||||
return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ struct Expr
|
||||
virtual void bindVars(const StaticEnv & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol & name);
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, Expr & e);
|
||||
@@ -74,9 +75,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);
|
||||
};
|
||||
@@ -158,12 +159,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;
|
||||
@@ -197,6 +196,7 @@ struct Formals
|
||||
struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
Symbol arg;
|
||||
bool matchAttrs;
|
||||
Formals * formals;
|
||||
@@ -208,6 +208,8 @@ struct ExprLambda : Expr
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% arg % pos);
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
string showNamePos();
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
||||
+33
-17
@@ -104,6 +104,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
}
|
||||
}
|
||||
}
|
||||
e->setName(attrPath.back());
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +238,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
nix::ExprAttrs * attrs;
|
||||
nix::Formals * formals;
|
||||
nix::Formal * formal;
|
||||
int n;
|
||||
nix::NixInt n;
|
||||
char * id; // !!! -> Symbol
|
||||
char * path;
|
||||
char * uri;
|
||||
@@ -268,12 +269,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
|
||||
|
||||
%%
|
||||
|
||||
@@ -305,9 +309,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(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(data->symbols.create("__lessThan")), $1), $3); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1)); }
|
||||
| expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(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); }
|
||||
@@ -319,6 +328,9 @@ expr_op
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(false, l);
|
||||
}
|
||||
| expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__sub")), $1), $3); }
|
||||
| expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__mul")), $1), $3); }
|
||||
| expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__div")), $1), $3); }
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
@@ -401,7 +413,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(*i), pos, true);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
@@ -469,7 +481,7 @@ 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);
|
||||
@@ -484,7 +496,7 @@ Expr * EvalState::parse(const char * text,
|
||||
if (res) throw ParseError(data.error);
|
||||
|
||||
try {
|
||||
data.result->bindVars(staticBaseEnv);
|
||||
data.result->bindVars(staticEnv);
|
||||
} catch (Error & e) {
|
||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
}
|
||||
@@ -493,7 +505,7 @@ Expr * EvalState::parse(const char * text,
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromFile(Path path)
|
||||
Path resolveExprPath(Path path)
|
||||
{
|
||||
assert(path[0] == '/');
|
||||
|
||||
@@ -511,21 +523,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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -1024,7 +1023,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 +1031,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]));
|
||||
}
|
||||
|
||||
|
||||
@@ -1260,7 +1262,7 @@ 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")));
|
||||
evalFile(findFile("nix/derivation.nix"), v);
|
||||
addConstant("derivation", v);
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' attribute
|
||||
|
||||
@@ -49,6 +49,11 @@ public:
|
||||
return *s;
|
||||
}
|
||||
|
||||
bool set() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return s->empty();
|
||||
|
||||
@@ -31,17 +31,20 @@ struct PrimOp;
|
||||
struct Symbol;
|
||||
|
||||
|
||||
typedef long NixInt;
|
||||
|
||||
|
||||
struct Value
|
||||
{
|
||||
ValueType type;
|
||||
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"
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+96
-29
@@ -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);
|
||||
@@ -786,6 +793,12 @@ private:
|
||||
/* Outputs that are already valid. */
|
||||
PathSet validPaths;
|
||||
|
||||
/* Outputs that are corrupt or not valid. */
|
||||
PathSet missingPaths;
|
||||
|
||||
/* Paths that have been subject to hash rewriting. */
|
||||
PathSet rewrittenPaths;
|
||||
|
||||
/* User selected for running the builder. */
|
||||
UserLock buildUser;
|
||||
|
||||
@@ -800,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;
|
||||
|
||||
@@ -838,6 +854,11 @@ private:
|
||||
bool repair;
|
||||
map<Path, Path> redirectedBadOutputs;
|
||||
|
||||
/* Set of inodes seen during calls to canonicalisePathMetaData()
|
||||
for this build's outputs. This needs to be shared between
|
||||
outputs to allow hard links between outputs. */
|
||||
InodesSeen inodesSeen;
|
||||
|
||||
/* Magic exit code denoting that setting up the child environment
|
||||
failed. (It's possible that the child actually returns the
|
||||
exit code, but ah well.) */
|
||||
@@ -975,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);
|
||||
@@ -1061,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));
|
||||
|
||||
@@ -1252,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
|
||||
@@ -1262,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");
|
||||
@@ -1305,6 +1339,9 @@ void DerivationGoal::tryToBuild()
|
||||
return;
|
||||
}
|
||||
|
||||
missingPaths = outputPaths(drv.outputs);
|
||||
foreach (PathSet::iterator, i, validPaths) missingPaths.erase(*i);
|
||||
|
||||
/* If any of the outputs already exist but are not valid, delete
|
||||
them. */
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
@@ -1323,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
|
||||
@@ -1350,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;
|
||||
@@ -1442,8 +1478,8 @@ void DerivationGoal::buildDone()
|
||||
/* Some cleanup per path. We do this here and not in
|
||||
computeClosure() for convenience when the build has
|
||||
failed. */
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
Path path = i->second.path;
|
||||
foreach (PathSet::iterator, i, missingPaths) {
|
||||
Path path = *i;
|
||||
|
||||
/* If the output was already valid, just skip (discard) it. */
|
||||
if (validPaths.find(path) != validPaths.end()) continue;
|
||||
@@ -1480,6 +1516,12 @@ void DerivationGoal::buildDone()
|
||||
/* Apply hash rewriting if necessary. */
|
||||
if (!rewritesFromTmp.empty()) {
|
||||
printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
|
||||
|
||||
/* Canonicalise first. This ensures that the path
|
||||
we're rewriting doesn't contain a hard link to
|
||||
/etc/shadow or something like that. */
|
||||
canonicalisePathMetaData(path, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen);
|
||||
|
||||
/* FIXME: this is in-memory. */
|
||||
StringSink sink;
|
||||
dumpPath(path, sink);
|
||||
@@ -1487,6 +1529,8 @@ void DerivationGoal::buildDone()
|
||||
sink.s = rewriteHashes(sink.s, rewritesFromTmp);
|
||||
StringSource source(sink.s);
|
||||
restorePath(path, source);
|
||||
|
||||
rewrittenPaths.insert(path);
|
||||
}
|
||||
|
||||
/* Gain ownership of the build result using the setuid
|
||||
@@ -1565,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);
|
||||
}
|
||||
@@ -1583,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. */
|
||||
@@ -1645,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%")
|
||||
@@ -1671,9 +1714,7 @@ int childEntry(void * arg)
|
||||
|
||||
void DerivationGoal::startBuilder()
|
||||
{
|
||||
PathSet missing = outputPaths(drv.outputs);
|
||||
foreach (PathSet::iterator, i, validPaths) missing.erase(*i);
|
||||
startNest(nest, lvlInfo, format("building path(s) %1%") % showPaths(missing));
|
||||
startNest(nest, lvlInfo, format(repair ? "repairing path(s) %1%" : "building path(s) %1%") % showPaths(missingPaths));
|
||||
|
||||
/* Right platform? */
|
||||
if (!canBuildLocally(drv.platform))
|
||||
@@ -1743,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);
|
||||
}
|
||||
|
||||
@@ -1754,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);
|
||||
@@ -1840,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
|
||||
@@ -1964,7 +2005,7 @@ void DerivationGoal::startBuilder()
|
||||
/* If we're repairing, then we don't want to delete the
|
||||
corrupt outputs in advance. So rewrite them as well. */
|
||||
if (repair)
|
||||
foreach (PathSet::iterator, i, missing)
|
||||
foreach (PathSet::iterator, i, missingPaths)
|
||||
if (worker.store.isValidPath(*i) && pathExists(*i))
|
||||
redirectedBadOutputs[*i] = addHashRewrite(*i);
|
||||
}
|
||||
@@ -2085,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);
|
||||
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);
|
||||
}
|
||||
@@ -2127,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");
|
||||
}
|
||||
@@ -2249,6 +2302,8 @@ void DerivationGoal::computeClosure()
|
||||
output paths read-only. */
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
Path path = i->second.path;
|
||||
if (missingPaths.find(path) == missingPaths.end()) continue;
|
||||
|
||||
if (!pathExists(path)) {
|
||||
throw BuildError(
|
||||
format("builder for `%1%' failed to produce output path `%2%'")
|
||||
@@ -2287,8 +2342,10 @@ void DerivationGoal::computeClosure()
|
||||
% path % i->second.hashAlgo % printHash16or32(h) % printHash16or32(h2));
|
||||
}
|
||||
|
||||
/* Get rid of all weird permissions. */
|
||||
canonicalisePathMetaData(path, buildUser.enabled() ? buildUser.getUID() : -1);
|
||||
/* Get rid of all weird permissions. This also checks that
|
||||
all files are owned by the build user, if applicable. */
|
||||
canonicalisePathMetaData(path,
|
||||
buildUser.enabled() && rewrittenPaths.find(path) == rewrittenPaths.end() ? buildUser.getUID() : -1, inodesSeen);
|
||||
|
||||
/* For this output path, find the references to other paths
|
||||
contained in it. Compute the SHA-256 NAR hash at the same
|
||||
@@ -2315,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);
|
||||
@@ -2330,12 +2387,12 @@ void DerivationGoal::computeClosure()
|
||||
paths referenced by each of them. If there are cycles in the
|
||||
outputs, this will fail. */
|
||||
ValidPathInfos infos;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
foreach (PathSet::iterator, i, missingPaths) {
|
||||
ValidPathInfo info;
|
||||
info.path = i->second.path;
|
||||
info.hash = contentHashes[i->second.path].first;
|
||||
info.narSize = contentHashes[i->second.path].second;
|
||||
info.references = allReferences[i->second.path];
|
||||
info.path = *i;
|
||||
info.hash = contentHashes[*i].first;
|
||||
info.narSize = contentHashes[*i].second;
|
||||
info.references = allReferences[*i];
|
||||
info.deriver = drvPath;
|
||||
infos.push_back(info);
|
||||
}
|
||||
@@ -2348,6 +2405,8 @@ string drvsLogDir = "drvs";
|
||||
|
||||
Path DerivationGoal::openLogFile()
|
||||
{
|
||||
logSize = 0;
|
||||
|
||||
if (!settings.keepLog) return "";
|
||||
|
||||
string baseName = baseNameOf(drvPath);
|
||||
@@ -2423,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) {
|
||||
@@ -2581,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;
|
||||
|
||||
+42
-44
@@ -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,47 +289,48 @@ 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)
|
||||
{
|
||||
try {
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % path);
|
||||
|
||||
printMsg(lvlVomit, format("looking at `%1%'") % path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
findRoots(store, path + "/" + *i, recurseSymlinks, deleteStale, roots);
|
||||
}
|
||||
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
Path target = absPath(readLink(path), dirOf(path));
|
||||
|
||||
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);
|
||||
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 = lstat(path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
findRoots(store, path + "/" + *i, roots);
|
||||
}
|
||||
|
||||
else if (recurseSymlinks) {
|
||||
if (pathExists(target))
|
||||
findRoots(store, target, false, deleteStale, roots);
|
||||
else if (deleteStale) {
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
Path target = readLink(path);
|
||||
if (isInStore(target))
|
||||
foundRoot(store, path, target, roots);
|
||||
|
||||
/* 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);
|
||||
/* 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());
|
||||
}
|
||||
} else {
|
||||
struct stat st2 = lstat(target);
|
||||
if (!S_ISLNK(st2.st_mode)) return;
|
||||
Path target2 = readLink(target);
|
||||
if (isInStore(target2)) foundRoot(store, path, 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;
|
||||
|
||||
|
||||
+108
-36
@@ -5,6 +5,7 @@
|
||||
#include "pathlocks.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "derivations.hh"
|
||||
#include "affinity.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
@@ -317,8 +318,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 +381,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. */
|
||||
@@ -500,10 +503,6 @@ void canonicaliseTimestampAndPermissions(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
typedef std::pair<dev_t, ino_t> Inode;
|
||||
typedef set<Inode> InodesSeen;
|
||||
|
||||
|
||||
static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSeen & inodesSeen)
|
||||
{
|
||||
checkInterrupt();
|
||||
@@ -561,10 +560,8 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
}
|
||||
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid)
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen)
|
||||
{
|
||||
InodesSeen inodesSeen;
|
||||
|
||||
canonicalisePathMetaData_(path, fromUid, inodesSeen);
|
||||
|
||||
/* On platforms that don't have lchown(), the top-level path can't
|
||||
@@ -580,6 +577,13 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid)
|
||||
}
|
||||
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid)
|
||||
{
|
||||
InodesSeen inodesSeen;
|
||||
canonicalisePathMetaData(path, fromUid, inodesSeen);
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation & drv)
|
||||
{
|
||||
string drvName = storePathToName(drvPath);
|
||||
@@ -987,7 +991,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);
|
||||
|
||||
@@ -997,7 +1001,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);
|
||||
|
||||
@@ -1007,10 +1011,6 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||
fromPipe.create();
|
||||
errorPipe.create();
|
||||
|
||||
/* Hack: prevent substituters that write too much to stderr from
|
||||
deadlocking our read() from stdout. */
|
||||
fcntl(errorPipe.writeSide, F_SETFL, O_NONBLOCK);
|
||||
|
||||
setSubstituterEnv();
|
||||
|
||||
run.pid = maybeVfork();
|
||||
@@ -1022,6 +1022,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)
|
||||
@@ -1038,15 +1039,91 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||
|
||||
/* Parent. */
|
||||
|
||||
run.program = baseNameOf(substituter);
|
||||
run.to = toPipe.writeSide.borrow();
|
||||
run.from = fromPipe.readSide.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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T> T getIntLine(int fd)
|
||||
/* Read a line from the substituter's stdout, while also processing
|
||||
its stderr. */
|
||||
string LocalStore::getLineFromSubstituter(RunningSubstituter & run)
|
||||
{
|
||||
string s = readLine(fd);
|
||||
string res, err;
|
||||
|
||||
/* We might have stdout data left over from the last time. */
|
||||
if (run.fromBuf.hasData()) goto haveData;
|
||||
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(run.from, &fds);
|
||||
FD_SET(run.error, &fds);
|
||||
|
||||
/* Wait for data to appear on the substituter's stdout or
|
||||
stderr. */
|
||||
if (select(run.from > run.error ? run.from + 1 : run.error + 1, &fds, 0, 0, 0) == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("waiting for input from the substituter");
|
||||
}
|
||||
|
||||
/* Completely drain stderr before dealing with stdout. */
|
||||
if (FD_ISSET(run.error, &fds)) {
|
||||
char buf[4096];
|
||||
ssize_t n = read(run.error, (unsigned char *) buf, sizeof(buf));
|
||||
if (n == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("reading from substituter's stderr");
|
||||
}
|
||||
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) {
|
||||
printMsg(lvlError, run.program + ": " + string(err, 0, p));
|
||||
err = string(err, p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read from stdout until we get a newline or the buffer is empty. */
|
||||
else if (run.fromBuf.hasData() || FD_ISSET(run.from, &fds)) {
|
||||
haveData:
|
||||
do {
|
||||
unsigned char c;
|
||||
run.fromBuf(&c, 1);
|
||||
if (c == '\n') {
|
||||
if (!err.empty()) printMsg(lvlError, run.program + ": " + err);
|
||||
return res;
|
||||
}
|
||||
res += c;
|
||||
} while (run.fromBuf.hasData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T> T LocalStore::getIntLineFromSubstituter(RunningSubstituter & run)
|
||||
{
|
||||
string s = getLineFromSubstituter(run);
|
||||
T res;
|
||||
if (!string2Int(s, res)) throw Error("integer expected from stream");
|
||||
return res;
|
||||
@@ -1060,6 +1137,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 += " "; }
|
||||
@@ -1069,13 +1147,9 @@ PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||
substituters should only write (short) messages to
|
||||
stderr when they fail. I.e. they shouldn't write debug
|
||||
output. */
|
||||
try {
|
||||
Path path = readLine(run.from);
|
||||
Path path = getLineFromSubstituter(run);
|
||||
if (path == "") break;
|
||||
res.insert(path);
|
||||
} catch (EndOfFile e) {
|
||||
throw Error(format("substituter `%1%' failed: %2%") % *i % chomp(drainFD(run.error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -1087,6 +1161,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)
|
||||
@@ -1094,26 +1169,22 @@ void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
||||
writeLine(run.to, s);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
Path path = readLine(run.from);
|
||||
Path path = getLineFromSubstituter(run);
|
||||
if (path == "") break;
|
||||
if (paths.find(path) == paths.end())
|
||||
throw Error(format("got unexpected path `%1%' from substituter") % path);
|
||||
paths.erase(path);
|
||||
SubstitutablePathInfo & info(infos[path]);
|
||||
info.deriver = readLine(run.from);
|
||||
info.deriver = getLineFromSubstituter(run);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
int nrRefs = getIntLine<int>(run.from);
|
||||
int nrRefs = getIntLineFromSubstituter<int>(run);
|
||||
while (nrRefs--) {
|
||||
Path p = readLine(run.from);
|
||||
Path p = getLineFromSubstituter(run);
|
||||
assertStorePath(p);
|
||||
info.references.insert(p);
|
||||
}
|
||||
info.downloadSize = getIntLine<long long>(run.from);
|
||||
info.narSize = getIntLine<long long>(run.from);
|
||||
} catch (EndOfFile e) {
|
||||
throw Error(format("substituter `%1%' failed: %2%") % substituter % chomp(drainFD(run.error)));
|
||||
}
|
||||
info.downloadSize = getIntLineFromSubstituter<long long>(run);
|
||||
info.narSize = getIntLineFromSubstituter<long long>(run);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1157,9 +1228,10 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
/* !!! Maybe the registration info should be updated if the
|
||||
path is already valid. */
|
||||
if (!isValidPath(i->path)) addValidPath(*i);
|
||||
if (isValidPath(i->path))
|
||||
updatePathInfo(*i);
|
||||
else
|
||||
addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,12 @@ struct OptimiseStats
|
||||
|
||||
struct RunningSubstituter
|
||||
{
|
||||
Path program;
|
||||
Pid pid;
|
||||
AutoCloseFD to, from, error;
|
||||
FdSource fromBuf;
|
||||
bool disabled;
|
||||
RunningSubstituter() : disabled(false) { };
|
||||
};
|
||||
|
||||
|
||||
@@ -173,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. */
|
||||
@@ -289,6 +293,10 @@ private:
|
||||
void startSubstituter(const Path & substituter,
|
||||
RunningSubstituter & runningSubstituter);
|
||||
|
||||
string getLineFromSubstituter(RunningSubstituter & run);
|
||||
|
||||
template<class T> T getIntLineFromSubstituter(RunningSubstituter & run);
|
||||
|
||||
Path createTempDirInStore();
|
||||
|
||||
Path importPath(bool requireSignature, Source & source);
|
||||
@@ -299,6 +307,10 @@ private:
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<dev_t, ino_t> Inode;
|
||||
typedef set<Inode> InodesSeen;
|
||||
|
||||
|
||||
/* "Fix", or canonicalise, the meta-data of the files in a store path
|
||||
after it has been built. In particular:
|
||||
- the last modification date on each file is set to 1 (i.e.,
|
||||
@@ -307,6 +319,7 @@ private:
|
||||
without execute permission; setuid bits etc. are cleared)
|
||||
- the owner and group are set to the Nix user and group, if we're
|
||||
in a setuid Nix installation. */
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen);
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid);
|
||||
|
||||
void canonicaliseTimestampAndPermissions(const Path & path);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@ Path addPermRoot(StoreAPI & store, const Path & storePath,
|
||||
|
||||
|
||||
/* Sort a set of paths topologically under the references relation.
|
||||
If p refers to q, then p follows q in this list. */
|
||||
If p refers to q, then p preceeds q in this list. */
|
||||
Paths topoSortPaths(StoreAPI & store, const PathSet & paths);
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -90,6 +90,12 @@ size_t BufferedSource::read(unsigned char * data, size_t len)
|
||||
}
|
||||
|
||||
|
||||
bool BufferedSource::hasData()
|
||||
{
|
||||
return bufPosOut < bufPosIn;
|
||||
}
|
||||
|
||||
|
||||
size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
|
||||
{
|
||||
ssize_t n;
|
||||
|
||||
@@ -61,8 +61,10 @@ 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
|
||||
@@ -273,7 +279,7 @@ struct SavingSourceAdapter : Source
|
||||
};
|
||||
|
||||
|
||||
static void performOp(unsigned int clientVersion,
|
||||
static void performOp(bool trusted, unsigned int clientVersion,
|
||||
Source & from, Sink & to, unsigned int op)
|
||||
{
|
||||
switch (op) {
|
||||
@@ -554,7 +560,7 @@ static void performOp(unsigned int clientVersion,
|
||||
if (name == "build-timeout")
|
||||
string2Int(value, settings.buildTimeout);
|
||||
else
|
||||
settings.set("untrusted-" + name, value);
|
||||
settings.set(trusted ? name : "untrusted-" + name, value);
|
||||
}
|
||||
}
|
||||
startWork();
|
||||
@@ -643,7 +649,7 @@ static void performOp(unsigned int clientVersion,
|
||||
}
|
||||
|
||||
|
||||
static void processConnection()
|
||||
static void processConnection(bool trusted)
|
||||
{
|
||||
canSendStderr = false;
|
||||
myPid = getpid();
|
||||
@@ -666,6 +672,9 @@ static void processConnection()
|
||||
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;
|
||||
@@ -711,7 +720,7 @@ static void processConnection()
|
||||
opCount++;
|
||||
|
||||
try {
|
||||
performOp(clientVersion, from, to, op);
|
||||
performOp(trusted, clientVersion, from, to, op);
|
||||
} catch (Error & e) {
|
||||
/* If we're not in a state were we can send replies, then
|
||||
something went wrong processing the input of the
|
||||
@@ -839,6 +848,7 @@ static void daemonLoop()
|
||||
/* Get the identity of the caller, if possible. */
|
||||
uid_t clientUid = -1;
|
||||
pid_t clientPid = -1;
|
||||
bool trusted = false;
|
||||
|
||||
#if defined(SO_PEERCRED)
|
||||
ucred cred;
|
||||
@@ -846,6 +856,7 @@ static void daemonLoop()
|
||||
if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) != -1) {
|
||||
clientPid = cred.pid;
|
||||
clientUid = cred.uid;
|
||||
if (clientUid == 0) trusted = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -879,7 +890,7 @@ static void daemonLoop()
|
||||
/* Handle the connection. */
|
||||
from.fd = remote;
|
||||
to.fd = remote;
|
||||
processConnection();
|
||||
processConnection(trusted);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
writeToStderr("child error: " + string(e.what()) + "\n");
|
||||
|
||||
+63
-43
@@ -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,20 +127,36 @@ 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;
|
||||
}
|
||||
else
|
||||
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 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
|
||||
@@ -151,11 +164,13 @@ static Expr * loadSourceExpr(EvalState & state, const Path & path)
|
||||
(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;
|
||||
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 allElems(newElems);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
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,12 +39,16 @@ 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
|
||||
return;
|
||||
}
|
||||
|
||||
Value vRoot;
|
||||
state.eval(e, vRoot);
|
||||
|
||||
foreach (Strings::const_iterator, i, attrPaths) {
|
||||
Value v;
|
||||
findAlongAttrPath(state, *i, autoArgs, e, v);
|
||||
Value & v(*findAlongAttrPath(state, *i, autoArgs, vRoot));
|
||||
state.forceValue(v);
|
||||
|
||||
PathSet context;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -694,7 +694,9 @@ static void opExport(Strings opFlags, Strings opArgs)
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
FdSink sink(STDOUT_FILENO);
|
||||
exportPaths(*store, opArgs, sign, sink);
|
||||
Paths sorted = topoSortPaths(*store, PathSet(opArgs.begin(), opArgs.end()));
|
||||
reverse(sorted.begin(), sorted.end());
|
||||
exportPaths(*store, sorted, sign, sink);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+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
|
||||
|
||||
@@ -1 +1 @@
|
||||
1275
|
||||
2188
|
||||
|
||||
@@ -16,6 +16,45 @@ let {
|
||||
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}"
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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