Compare commits
146 Commits
0.12
...
reduce-cac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6468f63994 | ||
|
|
9416202465 | ||
|
|
4941ba3413 | ||
|
|
7244b9b861 | ||
|
|
c6890d6b5c | ||
|
|
749dd97a54 | ||
|
|
f2c3fc5191 | ||
|
|
a2fc3a53ba | ||
|
|
d53603c928 | ||
|
|
14bc3ce3d6 | ||
|
|
f24cf5d303 | ||
|
|
c6cb792d48 | ||
|
|
a64bbe049e | ||
|
|
d407d572fd | ||
|
|
e42975490f | ||
|
|
50d11b90ca | ||
|
|
c34e6d71bc | ||
|
|
9536ba19d4 | ||
|
|
83bd320b39 | ||
|
|
f751c2966b | ||
|
|
404996ca1f | ||
|
|
7660e2a068 | ||
|
|
52a9ba96f5 | ||
|
|
6a5599fd01 | ||
|
|
098cb9d233 | ||
|
|
c710fe540e | ||
|
|
d5eab2fc82 | ||
|
|
8f1bf28505 | ||
|
|
4e646b0ddb | ||
|
|
0460ea4c39 | ||
|
|
dfb863f333 | ||
|
|
435a93b5d8 | ||
|
|
8b2a01a8c2 | ||
|
|
50cb6f9782 | ||
|
|
351bf658f9 | ||
|
|
cff2b2a13a | ||
|
|
160a60d663 | ||
|
|
805144b705 | ||
|
|
096affb55b | ||
|
|
cbc1f57b48 | ||
|
|
7377195297 | ||
|
|
737423a89c | ||
|
|
47706e3924 | ||
|
|
6e946c8e72 | ||
|
|
c7152c8f97 | ||
|
|
3a2bbe7f8a | ||
|
|
7fb548aa26 | ||
|
|
f54e800366 | ||
|
|
38f98b3282 | ||
|
|
e23a7a8b7b | ||
|
|
cb85bc396e | ||
|
|
61686926d1 | ||
|
|
9856efc7b0 | ||
|
|
5dd8fb2069 | ||
|
|
92f525ecf4 | ||
|
|
7024a1ef07 | ||
|
|
8146a0c731 | ||
|
|
a046858a22 | ||
|
|
12c8c64efa | ||
|
|
7aedcf9460 | ||
|
|
3a87163b24 | ||
|
|
cacff1be88 | ||
|
|
58969fa2bf | ||
|
|
d7b2d11255 | ||
|
|
7a57b2920b | ||
|
|
77d272623f | ||
|
|
7e05b8b75e | ||
|
|
b88460bcbc | ||
|
|
2897286487 | ||
|
|
e530e0a350 | ||
|
|
9485ec31ea | ||
|
|
13df3915ef | ||
|
|
c183ee5c79 | ||
|
|
1dcf208f56 | ||
|
|
93b6926054 | ||
|
|
51e7e32c3b | ||
|
|
2d5114452d | ||
|
|
33ecb42991 | ||
|
|
be88248add | ||
|
|
a96cac0d18 | ||
|
|
d4753c944f | ||
|
|
0e6f604178 | ||
|
|
7f254706b0 | ||
|
|
1273d355ac | ||
|
|
d4ca5c3952 | ||
|
|
fd2e14b3c8 | ||
|
|
60cb7de336 | ||
|
|
8ab6bc5a49 | ||
|
|
f052c10eed | ||
|
|
a7cee528c5 | ||
|
|
041717eda3 | ||
|
|
621093cb1c | ||
|
|
824b154ce8 | ||
|
|
2ef579d1aa | ||
|
|
1407a1ec99 | ||
|
|
1bb0f1e84b | ||
|
|
b682fae9d9 | ||
|
|
6f8c96d123 | ||
|
|
110606d470 | ||
|
|
061141e632 | ||
|
|
019176137f | ||
|
|
4ce692df88 | ||
|
|
c504d90c11 | ||
|
|
8e39d9bdb3 | ||
|
|
28355dafcf | ||
|
|
8fce03e0ad | ||
|
|
60ec75048a | ||
|
|
6f6bb1fdea | ||
|
|
92cb7c4dfe | ||
|
|
0008b0006d | ||
|
|
ac36c6cd44 | ||
|
|
2b70a8e7c9 | ||
|
|
9122dcecbb | ||
|
|
6776a52bb3 | ||
|
|
d86bd22d24 | ||
|
|
ac5478eb52 | ||
|
|
652817046b | ||
|
|
5a569509b4 | ||
|
|
7c54f1603f | ||
|
|
07cdfb09fb | ||
|
|
a0766eca27 | ||
|
|
cd16d5dc3d | ||
|
|
5b949241a5 | ||
|
|
67958f21df | ||
|
|
d91dc086bb | ||
|
|
9ac3f5df9c | ||
|
|
bcfe98acff | ||
|
|
9850262a72 | ||
|
|
909fbb9de1 | ||
|
|
5dfba0b4db | ||
|
|
9ccdb80de3 | ||
|
|
9fd9c4c635 | ||
|
|
63b8f09d8d | ||
|
|
f8713e1287 | ||
|
|
82ae85de27 | ||
|
|
5eaf644c99 | ||
|
|
cdee317419 | ||
|
|
d95b68fde3 | ||
|
|
ff762fb499 | ||
|
|
1307b22223 | ||
|
|
64519cfd65 | ||
|
|
09bc0c502c | ||
|
|
5d4eb9dd07 | ||
|
|
0c478d2f4d | ||
|
|
5024bde8f4 | ||
|
|
2ab09a55cf |
18
Makefile.am
18
Makefile.am
@@ -1,17 +1,11 @@
|
||||
SUBDIRS = externals src scripts corepkgs doc misc tests
|
||||
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
|
||||
svn-revision nix.conf.example NEWS
|
||||
nix.conf.example NEWS version
|
||||
|
||||
include ./substitute.mk
|
||||
|
||||
nix.spec: nix.spec.in
|
||||
|
||||
rpm: nix.spec dist
|
||||
rpm $(EXTRA_RPM_FLAGS) -ta $(distdir).tar.gz
|
||||
|
||||
relname:
|
||||
echo -n $(distdir) > relname
|
||||
|
||||
install-data-local: init-state
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
$(INSTALL_DATA) $(srcdir)/nix.conf.example $(DESTDIR)$(sysconfdir)/nix
|
||||
@@ -44,13 +38,11 @@ init-state:
|
||||
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
|
||||
|
||||
else
|
||||
|
||||
init-state:
|
||||
|
||||
endif
|
||||
|
||||
svn-revision:
|
||||
svnversion . > svn-revision
|
||||
|
||||
all-local: NEWS
|
||||
|
||||
NEWS: doc/manual/NEWS.txt
|
||||
NEWS:
|
||||
$(MAKE) -C doc/manual NEWS.txt
|
||||
cp $(srcdir)/doc/manual/NEWS.txt NEWS
|
||||
|
||||
57
configure.ac
57
configure.ac
@@ -1,21 +1,8 @@
|
||||
AC_INIT(nix, 0.12)
|
||||
AC_INIT(nix, m4_esyscmd([echo -n $(cat ./version)$VERSION_SUFFIX]))
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
|
||||
|
||||
# Change to `1' to produce a `stable' release (i.e., the `preREVISION'
|
||||
# suffix is not added).
|
||||
STABLE=1
|
||||
|
||||
# Put the revision number in the version.
|
||||
if test "$STABLE" != "1"; then
|
||||
if REVISION=`test -d $srcdir/.svn && svnversion -n $srcdir 2> /dev/null`; then
|
||||
VERSION=${VERSION}pre${REVISION}
|
||||
elif REVISION=`cat $srcdir/svn-revision 2> /dev/null`; then
|
||||
VERSION=${VERSION}pre${REVISION}
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [Nix version.])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
@@ -63,14 +50,30 @@ AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
|
||||
test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
|
||||
|
||||
|
||||
# Whether to produce a statically linked binary. On Cygwin, this is
|
||||
# the default: dynamically linking against the ATerm DLL does work,
|
||||
# except that it requires the ATerm "lib" directory to be in $PATH, as
|
||||
# Windows doesn't have anything like an RPATH embedded in executable.
|
||||
# Since this is kind of annoying, we use static libraries for now.
|
||||
|
||||
AC_ARG_ENABLE(static-nix, AC_HELP_STRING([--enable-static-nix],
|
||||
[produce statically linked binaries]),
|
||||
static_nix=$enableval, static_nix=no)
|
||||
|
||||
if test "$sys_name" = cygwin; then
|
||||
static_nix=yes
|
||||
fi
|
||||
|
||||
if test "$static_nix" = yes; then
|
||||
AC_DISABLE_SHARED
|
||||
AC_ENABLE_STATIC
|
||||
fi
|
||||
|
||||
|
||||
# Windows-specific stuff.
|
||||
if test "$sys_name" = "cygwin"; then
|
||||
# We cannot delete open files.
|
||||
AC_DEFINE(CANNOT_DELETE_OPEN_FILES, 1, [Whether it is impossible to delete open files.])
|
||||
|
||||
# Shared libraries don't work, currently.
|
||||
AC_DISABLE_SHARED
|
||||
AC_ENABLE_STATIC
|
||||
fi
|
||||
|
||||
|
||||
@@ -103,6 +106,8 @@ AC_LANG_POP(C++)
|
||||
|
||||
# Check for chroot support (requires chroot() and bind mounts).
|
||||
AC_CHECK_FUNCS([chroot])
|
||||
AC_CHECK_FUNCS([unshare])
|
||||
AC_CHECK_HEADERS([sched.h], [], [], [])
|
||||
AC_CHECK_HEADERS([sys/param.h], [], [], [])
|
||||
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
[#ifdef HAVE_SYS_PARAM_H
|
||||
@@ -111,12 +116,17 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
])
|
||||
|
||||
|
||||
# Check for <locale>
|
||||
# Check for <locale>.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([locale], [], [], [])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
# Check whether we have the personality() syscall, which allows us to
|
||||
# do i686-linux builds on x86_64-linux machines.
|
||||
AC_CHECK_HEADERS([sys/personality.h])
|
||||
|
||||
|
||||
AC_DEFUN([NEED_PROG],
|
||||
[
|
||||
AC_PATH_PROG($1, $2)
|
||||
@@ -272,6 +282,7 @@ AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||
|
||||
# Nice to have, but not essential.
|
||||
AC_CHECK_FUNCS([strsignal])
|
||||
AC_CHECK_FUNCS([posix_fallocate])
|
||||
|
||||
|
||||
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
|
||||
@@ -281,6 +292,14 @@ if test "$(uname)" = "Darwin"; then
|
||||
fi
|
||||
|
||||
|
||||
if test "$static_nix" = yes; then
|
||||
# `-all-static' has to be added at the end of configure, because
|
||||
# the C compiler doesn't know about -all-static (it's filtered out
|
||||
# by libtool, but configure doesn't use libtool).
|
||||
LDFLAGS="-all-static $LDFLAGS"
|
||||
fi
|
||||
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
externals/Makefile
|
||||
|
||||
@@ -8,9 +8,15 @@ inputs=($inputs)
|
||||
for ((n = 0; n < ${#inputs[*]}; n += 2)); do
|
||||
channelName=${inputs[n]}
|
||||
channelTarball=${inputs[n+1]}
|
||||
|
||||
echo "unpacking channel $channelName"
|
||||
|
||||
@bunzip2@ < $channelTarball | @tar@ xf -
|
||||
|
||||
if test -e */channel-name; then
|
||||
channelName="$(@coreutils@/cat */channel-name)"
|
||||
fi
|
||||
|
||||
nr=1
|
||||
attrName=$(echo $channelName | @tr@ -- '- ' '__')
|
||||
dirName=$attrName
|
||||
|
||||
@@ -79,10 +79,14 @@ all-local: manual.html NEWS.html NEWS.txt
|
||||
install-data-local: manual.html
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)/manual
|
||||
$(INSTALL_DATA) manual.html $(DESTDIR)$(docdir)/manual
|
||||
ln -sf manual.html $(DESTDIR)$(docdir)/manual/index.html
|
||||
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/manual
|
||||
cp -r images $(DESTDIR)$(docdir)/manual/images
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)/manual/figures
|
||||
$(INSTALL_DATA) $(FIGURES) $(DESTDIR)$(docdir)/manual/figures
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)/release-notes
|
||||
$(INSTALL_DATA) NEWS.html $(DESTDIR)$(docdir)/release-notes/index.html
|
||||
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/release-notes/
|
||||
|
||||
images:
|
||||
mkdir images
|
||||
|
||||
@@ -77,18 +77,8 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
This allows a Nix expression to fall back gracefully on older Nix
|
||||
installations that don’t have the desired built-in function.
|
||||
However, in that case you should not write
|
||||
|
||||
<programlisting>
|
||||
if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
|
||||
|
||||
This Nix expression will trigger an “undefined variable” error on
|
||||
older Nix versions since <function>__getEnv</function> doesn’t
|
||||
exist. <literal>builtins.getEnv</literal>, on the other hand, is
|
||||
safe since <literal>builtins</literal> always exists and attribute
|
||||
selection is lazy, so it’s only performed if the test
|
||||
succeeds.</para></listitem>
|
||||
installations that don’t have the desired built-in
|
||||
function.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -364,6 +354,36 @@ x: x + 456</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isString</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a string, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isInt</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a int, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isBool</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a bool, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>isNull</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
|
||||
@@ -151,12 +151,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<para>On the basis of this information, and whatever persistent
|
||||
state the build hook keeps about other machines and their current
|
||||
load, it has to decide what to do with the build. It should print
|
||||
out on file descriptor 3 one of the following responses (terminated
|
||||
by a newline, <literal>"\n"</literal>):
|
||||
out on standard error one of the following responses (terminated by
|
||||
a newline, <literal>"\n"</literal>):
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>decline</literal></term>
|
||||
<varlistentry><term><literal># decline</literal></term>
|
||||
|
||||
<listitem><para>The build hook is not willing or able to perform
|
||||
the build; the calling Nix process should do the build itself,
|
||||
@@ -164,7 +164,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>postpone</literal></term>
|
||||
<varlistentry><term><literal># postpone</literal></term>
|
||||
|
||||
<listitem><para>The build hook cannot perform the build now, but
|
||||
can do so in the future (e.g., because all available build slots
|
||||
@@ -174,7 +174,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>accept</literal></term>
|
||||
<varlistentry><term><literal># accept</literal></term>
|
||||
|
||||
<listitem><para>The build hook has accepted the
|
||||
build.</para></listitem>
|
||||
@@ -185,37 +185,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If the build hook accepts the build, it is possible that it is
|
||||
no longer necessary to do the build because some other process has
|
||||
performed the build in the meantime. To prevent races, the hook
|
||||
must read from file descriptor 4 a single line that tells it whether
|
||||
to continue:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>cancel</literal></term>
|
||||
|
||||
<listitem><para>The build has already been done, so the hook
|
||||
should exit.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>okay</literal></term>
|
||||
|
||||
<listitem><para>The hook should proceed with the build. At this
|
||||
point, the calling Nix process has acquired locks on the output
|
||||
path, so no other Nix process will perform the
|
||||
build.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If the hook has been told to proceed, Nix will store in the
|
||||
hook’s current directory a number of text files that contain
|
||||
information about the derivation:
|
||||
<para>After sending <literal># accept</literal>, the hook should
|
||||
read one line from standard input, which will be the string
|
||||
<literal>okay</literal>. It can then proceed with the build.
|
||||
Before sending <literal>okay</literal>, Nix will store in the hook’s
|
||||
current directory a number of text files that contain information
|
||||
about the derivation:
|
||||
|
||||
<variablelist>
|
||||
|
||||
@@ -255,7 +230,9 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<para>The hook should copy the inputs to the remote machine,
|
||||
register the validity of the inputs, perform the remote build, and
|
||||
copy the outputs back to the local machine. An exit code other than
|
||||
<literal>0</literal> indicates that the hook has failed.</para>
|
||||
<literal>0</literal> indicates that the hook has failed. An exit
|
||||
code equal to 100 means that the remote build failed (as opposed to,
|
||||
e.g., a network error).</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
</appendix>
|
||||
|
||||
<xi:include href="troubleshooting.xml" />
|
||||
<xi:include href="bugs.xml" />
|
||||
<!-- <xi:include href="bugs.xml" /> -->
|
||||
<xi:include href="glossary.xml" />
|
||||
|
||||
<appendix>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<arg><option>--fallback</option></arg>
|
||||
<arg><option>--readonly-mode</option></arg>
|
||||
<arg><option>--log-type</option> <replaceable>type</replaceable></arg>
|
||||
<arg><option>--show-trace</option></arg>
|
||||
<sbr />
|
||||
|
||||
</nop>
|
||||
|
||||
@@ -251,14 +251,14 @@
|
||||
|
||||
<programlisting>
|
||||
{ # The system (e.g., `i686-linux') for which to build the packages.
|
||||
system ? __currentSystem
|
||||
system ? builtins.currentSystem
|
||||
<replaceable>...</replaceable>
|
||||
}: <replaceable>...</replaceable></programlisting>
|
||||
|
||||
So if you call this Nix expression (e.g., when you do
|
||||
<literal>nix-env -i <replaceable>pkgname</replaceable></literal>),
|
||||
the function will be called automatically using the value <link
|
||||
linkend='builtin-currentSystem'><literal>__currentSystem</literal></link>
|
||||
linkend='builtin-currentSystem'><literal>builtins.currentSystem</literal></link>
|
||||
for the <literal>system</literal> argument. You can override this
|
||||
using <option>--arg</option>, e.g., <literal>nix-env -i
|
||||
<replaceable>pkgname</replaceable> --arg system
|
||||
@@ -305,6 +305,14 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--show-trace</option></term>
|
||||
|
||||
<listitem><para>Causes Nix to print out a stack trace in case of Nix
|
||||
expression evaluation errors.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,93 @@
|
||||
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.13"><title>Release 0.13 (April NN,
|
||||
2009)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It has some new
|
||||
features:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Syntactic sugar for writing nested attribute sets. Instead of
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
foo = {
|
||||
bar = 123;
|
||||
xyzzy = true;
|
||||
};
|
||||
a = { b = { c = "d"; }; };
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
you can write
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
foo.bar = 123;
|
||||
foo.xyzzy = true;
|
||||
a.b.c = "d";
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
This is useful, for instance, in NixOS configuration files.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for Nix channels generated by Hydra, the Nix-based
|
||||
continuous build system. (Hydra generates NAR archives on the
|
||||
fly, so the size and hash of these archives isn’t known in
|
||||
advance.)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support <literal>i686-linux</literal> builds directly on
|
||||
<literal>x86_64-linux</literal> Nix installations. This is
|
||||
implemented using the <function>personality()</function> syscall,
|
||||
which causes <command>uname</command> to return
|
||||
<literal>i686</literal> in child processes.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Various improvements to the <literal>chroot</literal>
|
||||
support. Building in a <literal>chroot</literal> works quite well
|
||||
now.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix no longer blocks if it tries to build a path and another
|
||||
process is already building the same path. Instead it tries to
|
||||
build another buildable path first. This improves
|
||||
parallelism.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for large (> 4 GiB) files in NAR archives.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Various (performance) improvements to the remote build
|
||||
mechanism.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New primops: <varname>builtins.addErrorContext</varname> (to
|
||||
add a string to stack traces — useful for debugging),
|
||||
<varname>builtins.isBool</varname>,
|
||||
<varname>builtins.isString</varname>,
|
||||
<varname>builtins.isInt</varname>.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (November 20,
|
||||
|
||||
@@ -1432,7 +1432,7 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||
inputs.</para></listitem>
|
||||
|
||||
<listitem><para>After the build, Nix sets the last-modified
|
||||
timestamp on all files in the build result to 0 (00:00:00 1/1/1970
|
||||
timestamp on all files in the build result to 1 (00:00:01 1/1/1970
|
||||
UTC), sets the group to the default group, and sets the mode of the
|
||||
file to 0444 or 0555 (i.e., read-only, with execute permission
|
||||
enabled if the file was originally executable). Note that possible
|
||||
|
||||
2
externals/Makefile.am
vendored
2
externals/Makefile.am
vendored
@@ -48,7 +48,7 @@ ATERM = aterm-2.4.2-fixes-r2
|
||||
$(ATERM).tar.bz2:
|
||||
@echo "Nix requires the CWI ATerm library to build."
|
||||
@echo "Please download version 2.4.2-fixes-r2 from"
|
||||
@echo " http://losser.st-lab.cs.uu.nl/~eelco/dist/aterm-2.4.2-fixes-r2.tar.bz2"
|
||||
@echo " http://nixos.org/tarballs/aterm-2.4.2-fixes-r2.tar.bz2"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
|
||||
@@ -76,10 +76,10 @@ The hook `nix-mode-hook' is run when Nix mode is started.
|
||||
("\\<baseNameOf\\>" . font-lock-builtin-face)
|
||||
("\\<toString\\>" . font-lock-builtin-face)
|
||||
("\\<isNull\\>" . font-lock-builtin-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_']*\\)[ \t]*="
|
||||
(1 font-lock-variable-name-face nil nil))
|
||||
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
|
||||
. font-lock-constant-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\.]*\\)[ \t]*="
|
||||
(1 font-lock-variable-name-face nil nil))
|
||||
("[a-zA-Z0-9._\\+-]*\\(/[a-zA-Z0-9._\\+-]+\\)+"
|
||||
. font-lock-constant-face)
|
||||
))
|
||||
|
||||
@@ -21,12 +21,14 @@ syn match nixFuncArg "\zs\w\+\ze\s*:"
|
||||
syn region nixStringParam start=+\${+ end=+}+
|
||||
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
|
||||
syn match nixEndOfLineComment "#.*$"
|
||||
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
|
||||
syn region nixStringIndented start=+''+ skip=+'''\|''${\|"+ end=+''+ contains=nixStringParam
|
||||
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
|
||||
|
||||
hi def link nixKeyword Keyword
|
||||
hi def link nixConditional Conditional
|
||||
hi def link nixBrace Special
|
||||
hi def link nixString String
|
||||
hi def link nixStringIndented String
|
||||
hi def link nixBuiltin Special
|
||||
hi def link nixStringParam Macro
|
||||
hi def link nixMultiLineComment Comment
|
||||
|
||||
@@ -154,21 +154,17 @@
|
||||
#build-chroot-dirs = /dev /dev/pts /proc
|
||||
|
||||
|
||||
### Option `system'
|
||||
### Option `build-cache-failure'
|
||||
#
|
||||
# This option specifies the canonical Nix system name of the current
|
||||
# installation, such as `i686-linux' or `powerpc-darwin'. Nix can
|
||||
# only build derivations whose `system' attribute equals the value
|
||||
# specified here. In general, it never makes sense to modify this
|
||||
# value from its default, since you can use it to `lie' about the
|
||||
# platform you are building on (e.g., perform a Mac OS build on a
|
||||
# Linux machine; the result would obviously be wrong). It only makes
|
||||
# sense if the Nix binaries can run on multiple platforms, e.g.,
|
||||
# `universal binaries' that run on `powerpc-darwin' and `i686-darwin'.
|
||||
#
|
||||
# It defaults to the canonical Nix system name detected by `configure'
|
||||
# at build time.
|
||||
# If this option is enabled, Nix will do negative caching; that is, it
|
||||
# will remember failed builds, and won't attempt to try to build them
|
||||
# again if you ask for it. Negative caching is disabled by default
|
||||
# because Nix cannot distinguish between permanent build errors (e.g.,
|
||||
# a syntax error in a source file) and transient build errors (e.g., a
|
||||
# full disk), as they both cause the builder to return a non-zero exit
|
||||
# code. You can clear the cache by doing `rm -f
|
||||
# /nix/var/nix/db/failed/*'.
|
||||
#
|
||||
# Example:
|
||||
# system = i686-darwin
|
||||
#system =
|
||||
# build-cache-failure = true
|
||||
#build-cache-failure = false
|
||||
|
||||
196
release.nix
Normal file
196
release.nix
Normal file
@@ -0,0 +1,196 @@
|
||||
{ nixpkgs ? ../nixpkgs }:
|
||||
|
||||
let
|
||||
|
||||
jobs = rec {
|
||||
|
||||
|
||||
tarball =
|
||||
{ nix ? {outPath = ./.; rev = 1234;}
|
||||
, officialRelease ? false
|
||||
}:
|
||||
|
||||
with import nixpkgs {};
|
||||
|
||||
releaseTools.sourceTarball {
|
||||
name = "nix-tarball";
|
||||
version = builtins.readFile ./version;
|
||||
src = nix;
|
||||
inherit officialRelease;
|
||||
|
||||
buildInputs = [curl bison flex2533 perl libxml2 libxslt w3m bzip2 jing_tools tetex dblatex];
|
||||
|
||||
configureFlags = ''
|
||||
--with-docbook-rng=${docbook5}/xml/rng/docbook
|
||||
--with-docbook-xsl=${docbook5_xsl}/xml/xsl/docbook
|
||||
--with-xml-flags=--nonet
|
||||
'';
|
||||
|
||||
# Include the BDB, ATerm and Bzip2 tarballs in the distribution.
|
||||
preConfigure = ''
|
||||
stripHash ${db45.src}
|
||||
# Remove unnecessary stuff from the Berkeley DB tarball.
|
||||
( mkdir bdb-temp
|
||||
cd bdb-temp
|
||||
tar xfz ${db45.src}
|
||||
cd *
|
||||
rm -rf docs test tcl perl libdb_java java rpc_server build_vxworks \
|
||||
examples_java examples_c examples_cxx dist/tags
|
||||
mkdir test
|
||||
touch test/include.tcl
|
||||
cd ..
|
||||
tar cvfz ../externals/$strippedName *
|
||||
)
|
||||
|
||||
stripHash ${aterm242fixes.src}
|
||||
cp -pv ${aterm242fixes.src} externals/$strippedName
|
||||
|
||||
stripHash ${bzip2.src}
|
||||
cp -pv ${bzip2.src} externals/$strippedName
|
||||
'';
|
||||
|
||||
preDist = ''
|
||||
make -C doc/manual install prefix=$out
|
||||
|
||||
make -C doc/manual manual.pdf prefix=$out
|
||||
cp doc/manual/manual.pdf $out/manual.pdf
|
||||
|
||||
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
|
||||
echo "doc-pdf manual $out/manual.pdf" >> $out/nix-support/hydra-build-products
|
||||
echo "doc release-notes $out/share/doc/nix/release-notes" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
build =
|
||||
{ tarball ? jobs.tarball {}
|
||||
, system ? "i686-linux"
|
||||
}:
|
||||
|
||||
with import nixpkgs {inherit system;};
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix";
|
||||
src = tarball;
|
||||
|
||||
buildInputs = [curl perl bzip2 openssl];
|
||||
|
||||
configureFlags = ''
|
||||
--disable-init-state
|
||||
--with-bdb=${db45} --with-aterm=${aterm242fixes} --with-bzip2=${bzip2}
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
static =
|
||||
{ tarball ? jobs.tarball {}
|
||||
, system ? "i686-linux"
|
||||
}:
|
||||
|
||||
with import nixpkgs {inherit system;};
|
||||
|
||||
releaseTools.binaryTarball {
|
||||
name = "nix-static-tarball";
|
||||
src = tarball;
|
||||
|
||||
buildInputs = [curl perl bzip2];
|
||||
|
||||
configureFlags = ''
|
||||
--disable-init-state
|
||||
--disable-old-db-compat --with-aterm=${aterm242fixes} --with-bzip2=${bzip2}
|
||||
--enable-static-nix
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
coverage =
|
||||
{ tarball ? jobs.tarball {}
|
||||
}:
|
||||
|
||||
with import nixpkgs {};
|
||||
|
||||
releaseTools.coverageAnalysis {
|
||||
name = "nix-build";
|
||||
src = tarball;
|
||||
|
||||
buildInputs = [
|
||||
curl perl bzip2 openssl
|
||||
# These are for "make check" only:
|
||||
graphviz libxml2 libxslt
|
||||
];
|
||||
|
||||
configureFlags = ''
|
||||
--disable-init-state --disable-shared
|
||||
--with-bdb=${db45} --with-aterm=${aterm242fixes} --with-bzip2=${bzip2}
|
||||
'';
|
||||
|
||||
lcovFilter = ["*/boost/*" "*-tab.*"];
|
||||
|
||||
# We call `dot', and even though we just use it to
|
||||
# syntax-check generated dot files, it still requires some
|
||||
# fonts. So provide those.
|
||||
FONTCONFIG_FILE = texFunctions.fontsConf;
|
||||
};
|
||||
|
||||
|
||||
rpm_fedora5i386 = makeRPM_i686 (diskImages: diskImages.fedora5i386) 20;
|
||||
rpm_fedora9i386 = makeRPM_i686 (diskImages: diskImages.fedora9i386) 50;
|
||||
rpm_fedora9x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora9x86_64) 50;
|
||||
rpm_fedora10i386 = makeRPM_i686 (diskImages: diskImages.fedora10i386) 40;
|
||||
rpm_fedora10x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora10x86_64) 40;
|
||||
rpm_opensuse103i386 = makeRPM_i686 (diskImages: diskImages.opensuse103i386) 40;
|
||||
|
||||
|
||||
deb_debian40i386 = makeDeb_i686 (diskImages: diskImages.debian40i386) 40;
|
||||
deb_debian40x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian40x86_64) 40;
|
||||
deb_debian50i386 = makeDeb_i686 (diskImages: diskImages.debian50i386) 30;
|
||||
deb_debian50x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian50x86_64) 30;
|
||||
deb_ubuntu804i386 = makeDeb_i686 (diskImages: diskImages.ubuntu804i386) 50;
|
||||
deb_ubuntu804x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu804x86_64) 50;
|
||||
deb_ubuntu810i386 = makeDeb_i686 (diskImages: diskImages.ubuntu810i386) 40;
|
||||
deb_ubuntu810x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu810x86_64) 40;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
makeRPM_i686 = makeRPM "i686-linux";
|
||||
makeRPM_x86_64 = makeRPM "x86_64-linux";
|
||||
|
||||
makeRPM =
|
||||
system: diskImageFun: prio:
|
||||
{ tarball ? jobs.tarball {}
|
||||
}:
|
||||
|
||||
with import nixpkgs {inherit system;};
|
||||
|
||||
releaseTools.rpmBuild rec {
|
||||
name = "nix-rpm-${diskImage.name}";
|
||||
src = tarball;
|
||||
diskImage = diskImageFun vmTools.diskImages;
|
||||
memSize = 1024;
|
||||
meta = { schedulingPriority = toString prio; };
|
||||
};
|
||||
|
||||
|
||||
makeDeb_i686 = makeDeb "i686-linux";
|
||||
makeDeb_x86_64 = makeDeb "x86_64-linux";
|
||||
|
||||
makeDeb =
|
||||
system: diskImageFun: prio:
|
||||
{ tarball ? jobs.tarball {}
|
||||
}:
|
||||
|
||||
with import nixpkgs {inherit system;};
|
||||
|
||||
releaseTools.debBuild {
|
||||
name = "nix-deb";
|
||||
src = tarball;
|
||||
diskImage = diskImageFun vmTools.diskImages;
|
||||
memSize = 1024;
|
||||
meta = { schedulingPriority = toString prio; };
|
||||
configureFlags = "--sysconfdir=/etc";
|
||||
};
|
||||
|
||||
|
||||
in jobs
|
||||
@@ -3,6 +3,7 @@
|
||||
use strict;
|
||||
use Fcntl ':flock';
|
||||
use English '-no_match_vars';
|
||||
use IO::Handle;
|
||||
|
||||
# General operation:
|
||||
#
|
||||
@@ -23,16 +24,12 @@ use English '-no_match_vars';
|
||||
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my $amWilling = shift @ARGV;
|
||||
my $localSystem = shift @ARGV;
|
||||
my $neededSystem = shift @ARGV;
|
||||
my $drvPath = shift @ARGV;
|
||||
my ($amWilling, $localSystem, $neededSystem, $drvPath, $maxSilentTime) = @ARGV;
|
||||
$maxSilentTime = 0 unless defined $maxSilentTime;
|
||||
|
||||
sub sendReply {
|
||||
my $reply = shift;
|
||||
open OUT, ">&3" or die;
|
||||
print OUT "$reply\n";
|
||||
close OUT;
|
||||
print STDERR "# $reply\n";
|
||||
}
|
||||
|
||||
sub decline {
|
||||
@@ -47,15 +44,11 @@ mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
decline if !defined $conf || ! -e $conf;
|
||||
|
||||
# Decline if the local system can do the build.
|
||||
decline if $amWilling && ($localSystem eq $neededSystem);
|
||||
my $canBuildLocally = $amWilling && ($localSystem eq $neededSystem);
|
||||
|
||||
|
||||
# Otherwise find a willing remote machine.
|
||||
my %machines;
|
||||
my %systemTypes;
|
||||
my %sshKeys;
|
||||
my %maxJobs;
|
||||
my @machines;
|
||||
my %curJobs;
|
||||
|
||||
|
||||
@@ -67,10 +60,12 @@ while (<CONF>) {
|
||||
s/\#.*$//g;
|
||||
next if /^\s*$/;
|
||||
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s*$/ or die;
|
||||
$machines{$1} = "";
|
||||
$systemTypes{$1} = $2;
|
||||
$sshKeys{$1} = $3;
|
||||
$maxJobs{$1} = $4;
|
||||
push @machines,
|
||||
{ hostName => $1
|
||||
, systemType => $2
|
||||
, sshKeys => $3
|
||||
, maxJobs => $4
|
||||
};
|
||||
}
|
||||
|
||||
close CONF;
|
||||
@@ -85,21 +80,26 @@ flock(MAINLOCK, LOCK_EX) or die;
|
||||
# Find a suitable system.
|
||||
my $rightType = 0;
|
||||
my $machine;
|
||||
LOOP: foreach my $cur (keys %machines) {
|
||||
if ($neededSystem eq $systemTypes{$cur}) {
|
||||
my $slotLock;
|
||||
LOOP: foreach my $cur (@machines) {
|
||||
if ($neededSystem eq $cur->{systemType}
|
||||
|| ($neededSystem eq "i686-linux" && $cur->{systemType} eq "x86_64-linux"))
|
||||
{
|
||||
$rightType = 1;
|
||||
|
||||
# We have a machine of the right type. Try to get a lock on
|
||||
# one of the machine's lock files.
|
||||
my $slot = 0;
|
||||
while ($slot < $maxJobs{$cur}) {
|
||||
my $slotLock = "$currentLoad/$cur-$slot";
|
||||
open SLOTLOCK, ">>$slotLock" or die;
|
||||
if (flock(SLOTLOCK, LOCK_EX | LOCK_NB)) {
|
||||
while ($slot < $cur->{maxJobs}) {
|
||||
my $slotLockFn = "$currentLoad/" . $cur->{systemType} . "-" . $cur->{hostName} . "-$slot";
|
||||
$slotLock = new IO::Handle;
|
||||
open $slotLock, ">>$slotLockFn" or die;
|
||||
if (flock($slotLock, LOCK_EX | LOCK_NB)) {
|
||||
utime undef, undef, $slotLock;
|
||||
$machine = $cur;
|
||||
last LOOP;
|
||||
}
|
||||
close SLOTLOCK;
|
||||
}
|
||||
close $slotLock;
|
||||
$slot++;
|
||||
}
|
||||
}
|
||||
@@ -108,9 +108,11 @@ LOOP: foreach my $cur (keys %machines) {
|
||||
close MAINLOCK;
|
||||
|
||||
|
||||
# Didn't find one?
|
||||
# Didn't find one? Then decline or postpone.
|
||||
if (!defined $machine) {
|
||||
if ($rightType) {
|
||||
# Postpone if we have a machine of the right type, except if the
|
||||
# local system can and wants to do the build.
|
||||
if ($rightType && !$canBuildLocally) {
|
||||
sendReply "postpone";
|
||||
exit 0;
|
||||
} else {
|
||||
@@ -120,11 +122,8 @@ if (!defined $machine) {
|
||||
|
||||
# Yes we did, accept.
|
||||
sendReply "accept";
|
||||
open IN, "<&4" or die;
|
||||
my $x = <IN>;
|
||||
my $x = <STDIN>;
|
||||
chomp $x;
|
||||
#print "got $x\n";
|
||||
close IN;
|
||||
|
||||
if ($x ne "okay") {
|
||||
exit 0;
|
||||
@@ -132,7 +131,8 @@ if ($x ne "okay") {
|
||||
|
||||
|
||||
# Do the actual job.
|
||||
print "BUILDING REMOTE: $drvPath on $machine\n";
|
||||
my $hostName = $machine->{hostName};
|
||||
print STDERR "building `$drvPath' on `$hostName'\n";
|
||||
|
||||
# Make sure that we don't get any SSH passphrase or host key popups -
|
||||
# if there is any problem it should fail, not do something
|
||||
@@ -141,10 +141,10 @@ $ENV{"DISPLAY"} = "";
|
||||
$ENV{"SSH_PASSWORD_FILE="} = "";
|
||||
$ENV{"SSH_ASKPASS="} = "";
|
||||
|
||||
my $sshOpts = "-i $sshKeys{$machine} -x";
|
||||
my $sshOpts = "-i " . $machine->{sshKeys} . " -x";
|
||||
|
||||
# Hack to support Cygwin: if we login without a password, we don't
|
||||
# have exactly the same right as when we do. This causes the
|
||||
# have exactly the same rights as when we do. This causes the
|
||||
# Microsoft C compiler to fail with certain flags:
|
||||
#
|
||||
# http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99676
|
||||
@@ -154,8 +154,8 @@ my $sshOpts = "-i $sshKeys{$machine} -x";
|
||||
# password. (It only calls this command when there is no controlling
|
||||
# terminal, but Nix ensures that is is the case. When doing this
|
||||
# manually, use setsid(1).)
|
||||
if ($sshKeys{$machine} =~ /^password:/) {
|
||||
my $passwordFile = $sshKeys{$machine};
|
||||
if ($machine->{sshKeys} =~ /^password:/) {
|
||||
my $passwordFile = $machine->{sshKeys};
|
||||
$passwordFile =~ s/^password://;
|
||||
$sshOpts = "ssh -x";
|
||||
$ENV{"SSH_PASSWORD_FILE"} = $passwordFile;
|
||||
@@ -173,36 +173,40 @@ $inputs =~ s/\n/ /g;
|
||||
my $outputs = `cat outputs`; die if ($? != 0);
|
||||
$outputs =~ s/\n/ /g;
|
||||
|
||||
print "COPYING INPUTS...\n";
|
||||
print "copying inputs...\n";
|
||||
|
||||
my $maybeSign = "";
|
||||
$maybeSign = "--sign" if -e "/nix/etc/nix/signing-key.sec";
|
||||
|
||||
system("NIX_SSHOPTS=\"$sshOpts\" nix-copy-closure $machine $maybeSign $drvPath $inputs") == 0
|
||||
or die "cannot copy inputs to $machine: $?";
|
||||
system("NIX_SSHOPTS=\"$sshOpts\" @bindir@/nix-copy-closure --gzip $hostName $maybeSign $drvPath $inputs") == 0
|
||||
or die "cannot copy inputs to $hostName: $?";
|
||||
|
||||
print "BUILDING...\n";
|
||||
print "building...\n";
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store -rvvK $drvPath'") == 0
|
||||
or die "remote build on $machine failed: $?";
|
||||
my $buildFlags = "--max-silent-time $maxSilentTime";
|
||||
|
||||
print "REMOTE BUILD DONE: $drvPath on $machine\n";
|
||||
# `-tt' forces allocation of a pseudo-terminal. This is required to
|
||||
# make the remote nix-store process receive a signal when the
|
||||
# connection dies. Without it, the remote process might continue to
|
||||
# run indefinitely (that is, until it next tries to write to
|
||||
# stdout/stderr).
|
||||
if (system("ssh -tt $sshOpts $hostName 'nix-store --realise -K $buildFlags $drvPath > /dev/null'") != 0) {
|
||||
# If we couldn't run ssh or there was an ssh problem (indicated by
|
||||
# exit code 255), then we return exit code 1; otherwise we assume
|
||||
# that the builder failed, which we indicated to Nix using exit
|
||||
# code 100. It's important to distinguish between the two because
|
||||
# the first is a transient failure and the latter is permanent.
|
||||
my $res = $? == -1 || ($? >> 8) == 255 ? 1 : 100;
|
||||
print STDERR "build of `$drvPath' on `$hostName' failed with exit code $?\n";
|
||||
exit $res;
|
||||
}
|
||||
|
||||
print "build of `$drvPath' on `$hostName' succeeded\n";
|
||||
|
||||
foreach my $output (split '\n', $outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store --export $maybeSignRemote $output' > dump") == 0
|
||||
or die "cannot copy $output from $machine: $?";
|
||||
|
||||
# This doesn't work yet, since the caller has a lock on the output
|
||||
# path. We should move towards lock-free invocation of build
|
||||
# hooks and substitutes.
|
||||
#system("nix-store --import < dump") == 0
|
||||
# or die "cannot import $output: $?";
|
||||
|
||||
# Hack: skip the first 8 bytes (the nix-store --export next
|
||||
# archive marker). The archive follows.
|
||||
system("(dd bs=1 count=8 of=/dev/null && cat) < dump | nix-store --restore $output") == 0
|
||||
or die "cannot restore $output: $?";
|
||||
system("ssh $sshOpts $hostName 'nix-store --export $maybeSignRemote $output | gzip' | gunzip | @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die "cannot copy $output from $hostName: $?";
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
STDOUT->autoflush(1);
|
||||
|
||||
my $manifestDir = "@localstatedir@/nix/manifests";
|
||||
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "@localstatedir@/nix/manifests");
|
||||
my $logFile = "@localstatedir@/log/nix/downloads";
|
||||
|
||||
|
||||
@@ -19,10 +19,15 @@ my %localPaths;
|
||||
my %patches;
|
||||
|
||||
for my $manifest (glob "$manifestDir/*.nixmanifest") {
|
||||
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
|
||||
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
|
||||
if ($version < 3) {
|
||||
print STDERR "you have an old-style manifest `$manifest'; please delete it\n";
|
||||
exit 1;
|
||||
}
|
||||
if ($version >= 10) {
|
||||
print STDERR "manifest `$manifest' is too new; please delete it or upgrade Nix\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -185,13 +190,11 @@ while ($queueFront < scalar @queue) {
|
||||
$format = "" if $baseHashAlgo eq "md5";
|
||||
my $hash = `$binDir/nix-hash --type '$baseHashAlgo' $format "$patch->{basePath}"`;
|
||||
chomp $hash;
|
||||
# print " MY HASH is $hash\n";
|
||||
if ($hash ne $baseHash) {
|
||||
print LOGFILE "$$ rejecting $patch->{basePath}\n";
|
||||
next;
|
||||
}
|
||||
}
|
||||
# print " PATCH from $patch->{basePath}\n";
|
||||
addToQueue $patch->{basePath};
|
||||
addEdge $patch->{basePath}, $u, $patch->{size}, "patch", $patch;
|
||||
}
|
||||
@@ -199,10 +202,12 @@ while ($queueFront < scalar @queue) {
|
||||
# Add NAR file edges to the start node.
|
||||
my $narFileList = $narFiles{$u};
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
# print " NAR from $narFile->{url}\n";
|
||||
addEdge "start", $u, $narFile->{size}, "narfile", $narFile;
|
||||
# !!! how to handle files whose size is not known in advance?
|
||||
# For now, assume some arbitrary size (1 MB).
|
||||
addEdge "start", $u, ($narFile->{size} || 1000000), "narfile", $narFile;
|
||||
if ($u eq $targetPath) {
|
||||
print LOGFILE "$$ full-download-would-be $narFile->{size}\n";
|
||||
my $size = $narFile->{size} || -1;
|
||||
print LOGFILE "$$ full-download-would-be $size\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +233,6 @@ while (scalar @todo > 0) {
|
||||
|
||||
my $u_ = $graph{$u};
|
||||
|
||||
# print "IN $u $u_->{d}\n";
|
||||
|
||||
foreach my $edge (@{$u_->{edges}}) {
|
||||
my $v_ = $graph{$edge->{end}};
|
||||
if ($v_->{d} > $u_->{d} + $edge->{weight}) {
|
||||
@@ -237,7 +240,6 @@ while (scalar @todo > 0) {
|
||||
# Store the edge; to edge->start is actually the
|
||||
# predecessor.
|
||||
$v_->{pred} = $edge;
|
||||
# print " RELAX $edge->{end} $v_->{d}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,20 +261,18 @@ while ($cur ne "start") {
|
||||
my $curStep = 1;
|
||||
my $maxStep = scalar @path;
|
||||
|
||||
sub downloadFile {
|
||||
my $url = shift;
|
||||
my ($hashAlgo, $hash) = parseHash(shift);
|
||||
sub downloadFile {
|
||||
my $url = shift;
|
||||
$ENV{"PRINT_PATH"} = 1;
|
||||
$ENV{"QUIET"} = 1;
|
||||
$ENV{"NIX_HASH_ALGO"} = $hashAlgo;
|
||||
my ($hash2, $path) = `$binDir/nix-prefetch-url '$url' '$hash'`;
|
||||
my ($hash, $path) = `$binDir/nix-prefetch-url '$url'`;
|
||||
die "download of `$url' failed" unless $? == 0;
|
||||
chomp $hash2;
|
||||
chomp $path;
|
||||
die "hash mismatch, expected $hash, got $hash2" if $hash ne $hash2;
|
||||
return $path;
|
||||
}
|
||||
|
||||
my $finalNarHash;
|
||||
|
||||
while (scalar @path > 0) {
|
||||
my $edge = pop @path;
|
||||
my $u = $edge->{start};
|
||||
@@ -302,7 +302,7 @@ while (scalar @path > 0) {
|
||||
|
||||
# Download the patch.
|
||||
print " downloading patch...\n";
|
||||
my $patchPath = downloadFile "$patch->{url}", "$patch->{hash}";
|
||||
my $patchPath = downloadFile "$patch->{url}";
|
||||
|
||||
# Apply the patch to the NAR archive produced in step 1 (for
|
||||
# the already present path) or a later step (for patch sequences).
|
||||
@@ -320,17 +320,20 @@ while (scalar @path > 0) {
|
||||
system("$binDir/nix-store --restore $v < $tmpNar2") == 0
|
||||
or die "cannot unpack $tmpNar2 into `$v'";
|
||||
}
|
||||
|
||||
$finalNarHash = $patch->{narHash};
|
||||
}
|
||||
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
my $narFile = $edge->{info};
|
||||
print "downloading `$narFile->{url}' into `$v'\n";
|
||||
|
||||
print LOGFILE "$$ narfile $narFile->{url} $narFile->{size} $v\n";
|
||||
my $size = $narFile->{size} || -1;
|
||||
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
|
||||
|
||||
# Download the archive.
|
||||
print " downloading archive...\n";
|
||||
my $narFilePath = downloadFile "$narFile->{url}", "$narFile->{hash}";
|
||||
my $narFilePath = downloadFile "$narFile->{url}";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used a base to a patch.
|
||||
@@ -342,11 +345,36 @@ while (scalar @path > 0) {
|
||||
system("@bunzip2@ < '$narFilePath' | $binDir/nix-store --restore '$v'") == 0
|
||||
or die "cannot unpack `$narFilePath' into `$v'";
|
||||
}
|
||||
|
||||
$finalNarHash = $narFile->{narHash};
|
||||
}
|
||||
|
||||
$curStep++;
|
||||
}
|
||||
|
||||
|
||||
# Make sure that the hash declared in the manifest matches what we
|
||||
# downloaded and unpacked.
|
||||
|
||||
if (defined $finalNarHash) {
|
||||
my ($hashAlgo, $hash) = parseHash $finalNarHash;
|
||||
|
||||
# The hash in the manifest can be either in base-16 or base-32.
|
||||
# Handle both.
|
||||
my $extraFlag =
|
||||
($hashAlgo eq "sha256" && length($hash) != 64)
|
||||
? "--base32" : "";
|
||||
|
||||
my $hash2 = `@bindir@/nix-hash --type $hashAlgo $extraFlag $targetPath`
|
||||
or die "cannot compute hash of path `$targetPath'";
|
||||
chomp $hash2;
|
||||
|
||||
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2"
|
||||
if $hash ne $hash2;
|
||||
} else {
|
||||
die "cannot check integrity of the downloaded path since its hash is not known";
|
||||
}
|
||||
|
||||
|
||||
print LOGFILE "$$ success\n";
|
||||
close LOGFILE;
|
||||
|
||||
@@ -77,7 +77,7 @@ EOF
|
||||
|
||||
elsif ($arg eq "--attr" or $arg eq "-A") {
|
||||
$n++;
|
||||
die "$0: `--attr' requires an argument\n" unless $n < scalar @ARGV;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @instArgs, ("--attr", $ARGV[$n]);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,21 @@ EOF
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time") {
|
||||
elsif ($arg eq "--log-type") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @instArgs, ($arg, $ARGV[$n]);
|
||||
push @buildArgs, ($arg, $ARGV[$n]);
|
||||
}
|
||||
|
||||
elsif ($arg eq "--option") {
|
||||
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
||||
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
push @buildArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @buildArgs, ($arg, $ARGV[$n]);
|
||||
@@ -128,7 +142,10 @@ foreach my $expr (@exprs) {
|
||||
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
||||
my $pid = open(DRVPATHS, "-|") || exec "$binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||
close DRVPATHS or exit 1;
|
||||
if (!close DRVPATHS) {
|
||||
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
foreach my $drvPath (@drvPaths) {
|
||||
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
|
||||
@@ -140,7 +157,10 @@ foreach my $expr (@exprs) {
|
||||
$pid = open(OUTPATHS, "-|") || exec "$binDir/nix-store", "--add-root", $outLink, "--indirect", "-rv",
|
||||
@buildArgs, @drvPaths;
|
||||
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
|
||||
close OUTPATHS or exit 1;
|
||||
if (!close OUTPATHS) {
|
||||
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
next if $dryRun;
|
||||
|
||||
|
||||
@@ -125,15 +125,13 @@ sub update {
|
||||
|
||||
my $rootFile = "$rootsDir/per-user/$userName/channels";
|
||||
|
||||
# Instantiate the Nix expression.
|
||||
# Build the Nix expression.
|
||||
print "unpacking channel Nix expressions...\n";
|
||||
my $storeExpr = `@bindir@/nix-instantiate --add-root '$rootFile'.tmp @datadir@/nix/corepkgs/channels/unpack.nix --argstr system @system@ --arg inputs '$inputs'`
|
||||
or die "cannot instantiate Nix expression";
|
||||
chomp $storeExpr;
|
||||
|
||||
# Build the resulting derivation.
|
||||
my $outPath = `@bindir@/nix-store --add-root '$rootFile' -r '$storeExpr'`
|
||||
or die "cannot realise store expression";
|
||||
my $outPath = `\\
|
||||
@bindir@/nix-build --out-link '$rootFile' --drv-link '$rootFile'.tmp \\
|
||||
@datadir@/nix/corepkgs/channels/unpack.nix \\
|
||||
--argstr system @system@ --arg inputs '$inputs'`
|
||||
or die "cannot unpack the channels";
|
||||
chomp $outPath;
|
||||
|
||||
unlink "$rootFile.tmp";
|
||||
|
||||
@@ -55,30 +55,18 @@ while (@ARGV) {
|
||||
if ($toMode) { # Copy TO the remote machine.
|
||||
|
||||
my @allStorePaths;
|
||||
my %storePathsSeen;
|
||||
|
||||
foreach my $storePath (@storePaths) {
|
||||
# $arg might be a symlink to the store, so resolve it.
|
||||
my $storePath2 = (`$binDir/nix-store --query --resolve '$storePath'`
|
||||
or die "cannot resolve `$storePath'");
|
||||
chomp $storePath2;
|
||||
|
||||
# Get the closure of this path.
|
||||
my $pid = open(READ,
|
||||
"$binDir/nix-store --query --requisites '$storePath2'|") or die;
|
||||
# Get the closure of this path.
|
||||
my $pid = open(READ, "$binDir/nix-store --query --requisites @storePaths|") or die;
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
if (!defined $storePathsSeen{$_}) {
|
||||
push @allStorePaths, $_;
|
||||
$storePathsSeen{$_} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
close READ or die "nix-store failed: $?";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
push @allStorePaths, $_;
|
||||
}
|
||||
|
||||
close READ or die "nix-store failed: $?";
|
||||
|
||||
|
||||
# Ask the remote host which paths are invalid.
|
||||
open(READ, "ssh @sshOpts $sshHost nix-store --check-validity --print-invalid @allStorePaths|");
|
||||
@@ -111,15 +99,11 @@ else { # Copy FROM the remote machine.
|
||||
"ssh @sshOpts $sshHost nix-store --query --requisites @storePaths|") or die;
|
||||
|
||||
my @allStorePaths;
|
||||
my %storePathsSeen;
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
if (!defined $storePathsSeen{$_}) {
|
||||
push @allStorePaths, $_;
|
||||
$storePathsSeen{$_} = 1;
|
||||
}
|
||||
push @allStorePaths, $_;
|
||||
}
|
||||
|
||||
close READ or die "nix-store on remote machine `$sshHost' failed: $?";
|
||||
|
||||
@@ -12,7 +12,8 @@ needed_path="?$QUERY_STRING"
|
||||
needed_path="${needed_path#*[?&]needed_path=}"
|
||||
needed_path="${needed_path%%&*}"
|
||||
#needed_path="$(echo $needed_path | ./unhttp)"
|
||||
needed_path="$(echo $needed_path | sed -e 's/%2B/+/g; s/%3D/=/g')"
|
||||
needed_path="${needed_path//%2B/+}"
|
||||
needed_path="${needed_path//%3D/=}"
|
||||
|
||||
echo needed_path: "$needed_path" >&2
|
||||
|
||||
|
||||
@@ -123,6 +123,11 @@ if ($interactive) {
|
||||
}
|
||||
|
||||
|
||||
# Store the manifest in the temporary directory so that we don't
|
||||
# pollute /nix/var/nix/manifests.
|
||||
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
|
||||
|
||||
|
||||
print "\nPulling manifests...\n";
|
||||
system("$binDir/nix-pull", $manifestURL) == 0
|
||||
or barf "nix-pull failed: $?";
|
||||
|
||||
@@ -60,7 +60,7 @@ removeTempDir() {
|
||||
|
||||
|
||||
doDownload() {
|
||||
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
|
||||
@curl@ $cacheFlags --fail --location --max-redirs 20 --disable-epsv \
|
||||
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,10 @@ my $tmpDir = tempdir("nix-pull.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
my $libexecDir = $ENV{"NIX_LIBEXEC_DIR"};
|
||||
$libexecDir = "@libexecdir@" unless defined $libexecDir;
|
||||
|
||||
my $stateDir = $ENV{"NIX_STATE_DIR"};
|
||||
$stateDir = "@localstatedir@/nix" unless defined $stateDir;
|
||||
|
||||
my $storeDir = $ENV{"NIX_STORE_DIR"};
|
||||
$storeDir = "@storedir@" unless defined $storeDir;
|
||||
my $libexecDir = ($ENV{"NIX_LIBEXEC_DIR"} or "@libexecdir@");
|
||||
my $storeDir = ($ENV{"NIX_STORE_DIR"} or "@storedir@");
|
||||
my $stateDir = ($ENV{"NIX_STATE_DIR"} or "@localstatedir@/nix");
|
||||
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "$stateDir/manifests");
|
||||
|
||||
|
||||
# Prevent access problems in shared-stored installations.
|
||||
@@ -68,10 +63,11 @@ sub processURL {
|
||||
print "obtaining list of Nix archives at `$url'...\n";
|
||||
$manifest = downloadFile $url;
|
||||
}
|
||||
|
||||
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
|
||||
|
||||
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
|
||||
die "`$url' is not manifest or it is too old (i.e., for Nix <= 0.7)\n";
|
||||
}
|
||||
die "`$url' is not a manifest or it is too old (i.e., for Nix <= 0.7)\n" if $version < 3;
|
||||
die "manifest `$url' is too new\n" if $version >= 5;
|
||||
|
||||
if ($skipWrongStore) {
|
||||
foreach my $path (keys %narFiles) {
|
||||
@@ -91,7 +87,7 @@ sub processURL {
|
||||
or die "cannot hash `$manifest'";
|
||||
chomp $hash;
|
||||
|
||||
my $finalPath = "$stateDir/manifests/$baseName-$hash.nixmanifest";
|
||||
my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
|
||||
|
||||
system("@coreutils@/ln", "-sfn", "$manifest", "$finalPath") == 0
|
||||
or die "cannot link `$finalPath to `$manifest'";
|
||||
|
||||
@@ -128,7 +128,7 @@ while (<READ>) {
|
||||
close READ or die "nix-instantiate failed: $?";
|
||||
|
||||
|
||||
# Realise the store expressions.
|
||||
# Build the derivations.
|
||||
print STDERR "creating archives...\n";
|
||||
|
||||
my @narPaths;
|
||||
@@ -140,12 +140,7 @@ while (scalar @tmp > 0) {
|
||||
my @tmp2 = @tmp[0..$n - 1];
|
||||
@tmp = @tmp[$n..scalar @tmp - 1];
|
||||
|
||||
# Note: we disable build hooks because of the impure path
|
||||
# reference (see above). Even if that is fixed, using a hook
|
||||
# probably wouldn't make that much sense; pumping lots of data
|
||||
# around just to compress them won't gain that much.
|
||||
$ENV{"NIX_BUILD_HOOK"} = "";
|
||||
my $pid = open(READ, "$binDir/nix-store --no-build-hook --realise @tmp2|")
|
||||
my $pid = open(READ, "$binDir/nix-store --realise @tmp2|")
|
||||
or die "cannot run nix-store";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
|
||||
@@ -2,10 +2,7 @@ use strict;
|
||||
|
||||
|
||||
sub addPatch {
|
||||
my $patches = shift;
|
||||
my $storePath = shift;
|
||||
my $patch = shift;
|
||||
my $allowConflicts = shift;
|
||||
my ($patches, $storePath, $patch) = @_;
|
||||
|
||||
$$patches{$storePath} = []
|
||||
unless defined $$patches{$storePath};
|
||||
@@ -14,15 +11,9 @@ sub addPatch {
|
||||
|
||||
my $found = 0;
|
||||
foreach my $patch2 (@{$patchList}) {
|
||||
if ($patch2->{url} eq $patch->{url}) {
|
||||
if ($patch2->{hash} eq $patch->{hash}) {
|
||||
$found = 1 if ($patch2->{basePath} eq $patch->{basePath});
|
||||
} else {
|
||||
die "conflicting hashes for URL $patch->{url}, " .
|
||||
"namely $patch2->{hash} and $patch->{hash}"
|
||||
unless $allowConflicts;
|
||||
}
|
||||
}
|
||||
$found = 1 if
|
||||
$patch2->{url} eq $patch->{url} &&
|
||||
$patch2->{basePath} eq $patch->{basePath};
|
||||
}
|
||||
|
||||
push @{$patchList}, $patch if !$found;
|
||||
@@ -32,12 +23,7 @@ sub addPatch {
|
||||
|
||||
|
||||
sub readManifest {
|
||||
my $manifest = shift;
|
||||
my $narFiles = shift;
|
||||
my $localPaths = shift;
|
||||
my $patches = shift;
|
||||
my $allowConflicts = shift;
|
||||
$allowConflicts = 0 unless defined $allowConflicts;
|
||||
my ($manifest, $narFiles, $localPaths, $patches) = @_;
|
||||
|
||||
open MANIFEST, "<$manifest"
|
||||
or die "cannot open `$manifest': $!";
|
||||
@@ -98,15 +84,7 @@ sub readManifest {
|
||||
|
||||
my $found = 0;
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
if ($narFile->{url} eq $url) {
|
||||
if ($narFile->{hash} eq $hash) {
|
||||
$found = 1;
|
||||
} else {
|
||||
die "conflicting hashes for URL $url, " .
|
||||
"namely $narFile->{hash} and $hash"
|
||||
unless $allowConflicts;
|
||||
}
|
||||
}
|
||||
$found = 1 if $narFile->{url} eq $url;
|
||||
}
|
||||
if (!$found) {
|
||||
push @{$narFileList},
|
||||
@@ -124,7 +102,7 @@ sub readManifest {
|
||||
, basePath => $basePath, baseHash => $baseHash
|
||||
, narHash => $narHash, patchType => $patchType
|
||||
, hashAlgo => $hashAlgo
|
||||
}, $allowConflicts;
|
||||
};
|
||||
}
|
||||
|
||||
elsif ($type eq "localPath") {
|
||||
@@ -171,12 +149,8 @@ sub readManifest {
|
||||
}
|
||||
|
||||
|
||||
sub writeManifest
|
||||
{
|
||||
my $manifest = shift;
|
||||
my $narFiles = shift;
|
||||
my $patches = shift;
|
||||
my $copySources = shift;
|
||||
sub writeManifest {
|
||||
my ($manifest, $narFiles, $patches) = @_;
|
||||
|
||||
open MANIFEST, ">$manifest.tmp"; # !!! check exclusive
|
||||
|
||||
@@ -190,9 +164,9 @@ sub writeManifest
|
||||
print MANIFEST "{\n";
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $narFile->{url}\n";
|
||||
print MANIFEST " Hash: $narFile->{hash}\n";
|
||||
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
|
||||
print MANIFEST " NarHash: $narFile->{narHash}\n";
|
||||
print MANIFEST " Size: $narFile->{size}\n";
|
||||
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
|
||||
print MANIFEST " References: $narFile->{references}\n"
|
||||
if defined $narFile->{references} && $narFile->{references} ne "";
|
||||
print MANIFEST " Deriver: $narFile->{deriver}\n"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SUBDIRS = format
|
||||
|
||||
pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
|
||||
nobase_pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
|
||||
shared_ptr.hpp weak_ptr.hpp throw_exception.hpp \
|
||||
enable_shared_from_this.hpp \
|
||||
detail/shared_count.hpp detail/workaround.hpp
|
||||
|
||||
@@ -3,12 +3,12 @@ pkglib_LTLIBRARIES = libexpr.la
|
||||
libexpr_la_SOURCES = \
|
||||
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
|
||||
get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \
|
||||
names.cc
|
||||
names.cc normal-forms.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
|
||||
get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh \
|
||||
names.hh
|
||||
names.hh nixexpr-ast.hh
|
||||
|
||||
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
|
||||
../boost/format/libformat.la
|
||||
|
||||
@@ -16,13 +16,23 @@ namespace nix {
|
||||
|
||||
|
||||
EvalState::EvalState()
|
||||
: normalForms(32768), primOps(128)
|
||||
: sessionNormalForms(32768), normalForms(32768), primOps(128)
|
||||
{
|
||||
nrEvaluated = nrCached = 0;
|
||||
nrEvaluated = nrCached = nrDepthAfterReset = 0;
|
||||
|
||||
initNixExprHelpers();
|
||||
|
||||
addPrimOps();
|
||||
|
||||
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
|
||||
safeCache = true;
|
||||
|
||||
loadNormalForms();
|
||||
}
|
||||
|
||||
EvalState::~EvalState()
|
||||
{
|
||||
saveNormalForms();
|
||||
}
|
||||
|
||||
|
||||
@@ -466,8 +476,10 @@ LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
|
||||
if (arity == 0)
|
||||
/* !!! backtrace for primop call */
|
||||
return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
|
||||
else
|
||||
else {
|
||||
state.safeCache = false;
|
||||
return makePrimOp(arity, fun, ATempty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -493,6 +505,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
||||
for (ATermIterator i(args); i; ++i)
|
||||
args2[--arity] = *i;
|
||||
/* !!! backtrace for primop call */
|
||||
state.safeCache = true;
|
||||
return ((PrimOp) ATgetBlobData(funBlob))
|
||||
(state, args2);
|
||||
} else
|
||||
@@ -644,6 +657,48 @@ LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
|
||||
}
|
||||
|
||||
|
||||
/* Implementation of the `==' and `!=' operators. */
|
||||
LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2))
|
||||
{
|
||||
e1 = evalExpr(state, e1);
|
||||
e2 = evalExpr(state, e2);
|
||||
|
||||
/* We cannot test functions/primops for equality, and we currently
|
||||
don't support testing equality between attribute sets or lists
|
||||
- that would have to be a deep equality test to be sound. */
|
||||
AFun sym1 = ATgetAFun(e1);
|
||||
AFun sym2 = ATgetAFun(e2);
|
||||
|
||||
if (sym1 != sym2) return false;
|
||||
|
||||
/* Functions are incomparable. */
|
||||
if (sym1 == symFunction || sym1 == symPrimOp) return false;
|
||||
|
||||
if (!state.allowUnsafeEquality && sym1 == symAttrs)
|
||||
throw EvalError("comparison of attribute sets is not implemented");
|
||||
|
||||
/* !!! This allows comparisons of infinite data structures to
|
||||
succeed, such as `let x = [x]; in x == x'. This is
|
||||
undesirable, since equivalent (?) terms such as `let x = [x]; y
|
||||
= [y]; in x == y' don't terminate. */
|
||||
if (e1 == e2) return true;
|
||||
|
||||
if (sym1 == symList) {
|
||||
ATermList es1; matchList(e1, es1);
|
||||
ATermList es2; matchList(e2, es2);
|
||||
if (ATgetLength(es1) != ATgetLength(es2)) return false;
|
||||
ATermIterator i(es1), j(es2);
|
||||
while (*i) {
|
||||
if (!areEqual(state, *i, *j)) return false;
|
||||
++i; ++j;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static char * deepestStack = (char *) -1; /* for measuring stack usage */
|
||||
|
||||
|
||||
@@ -707,12 +762,10 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||
However, we don't want to make (==) strict, because that would
|
||||
make operations like `big_derivation == null' very slow (unless
|
||||
we were to evaluate them side-by-side). */
|
||||
if (matchOpEq(e, e1, e2))
|
||||
return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
|
||||
|
||||
if (matchOpNEq(e, e1, e2))
|
||||
return makeBool(evalExpr(state, e1) != evalExpr(state, e2));
|
||||
|
||||
if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2));
|
||||
|
||||
if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2));
|
||||
|
||||
/* Negation. */
|
||||
if (matchOpNot(e, e1))
|
||||
return makeBool(!evalBool(state, e1));
|
||||
@@ -760,11 +813,19 @@ Expr evalExpr(EvalState & state, Expr e)
|
||||
format("evaluating expression: %1%") % e);
|
||||
#endif
|
||||
|
||||
state.nrEvaluated++;
|
||||
const unsigned int hugeEvalExpr = 100;
|
||||
unsigned int nrEvaluated = state.nrEvaluated++;
|
||||
state.nrDepthAfterReset++;
|
||||
|
||||
/* Consult the memo table to quickly get the normal form of
|
||||
previously evaluated expressions. */
|
||||
Expr nf = state.normalForms.get(e);
|
||||
if (nf) {
|
||||
state.nrCached++;
|
||||
return nf;
|
||||
}
|
||||
|
||||
nf = state.sessionNormalForms.get(e);
|
||||
if (nf) {
|
||||
if (nf == makeBlackHole())
|
||||
throwEvalError("infinite recursion encountered");
|
||||
@@ -773,14 +834,25 @@ Expr evalExpr(EvalState & state, Expr e)
|
||||
}
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
state.normalForms.set(e, makeBlackHole());
|
||||
state.sessionNormalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
} catch (Error & err) {
|
||||
state.normalForms.remove(e);
|
||||
state.sessionNormalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
state.normalForms.set(e, nf);
|
||||
|
||||
// session independent condition
|
||||
if (state.nrDepthAfterReset && state.safeCache &&
|
||||
// heuristic condition
|
||||
state.nrEvaluated - nrEvaluated > hugeEvalExpr) {
|
||||
state.sessionNormalForms.remove(e);
|
||||
state.normalForms.set(e, nf);
|
||||
state.nrDepthAfterReset--;
|
||||
} else {
|
||||
state.sessionNormalForms.set(e, nf);
|
||||
}
|
||||
|
||||
return nf;
|
||||
}
|
||||
|
||||
@@ -788,6 +860,7 @@ Expr evalExpr(EvalState & state, Expr e)
|
||||
Expr evalFile(EvalState & state, const Path & path)
|
||||
{
|
||||
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
|
||||
state.nrDepthAfterReset = 0;
|
||||
Expr e = parseExprFromFile(state, path);
|
||||
try {
|
||||
return evalExpr(state, e);
|
||||
@@ -859,11 +932,13 @@ void printEvalStats(EvalState & state)
|
||||
char x;
|
||||
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
||||
printMsg(showStats ? lvlInfo : lvlDebug,
|
||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
|
||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space, %6% cached normal reduction, %7% session dependent reduction.")
|
||||
% state.nrEvaluated % state.nrCached
|
||||
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
|
||||
% AT_calcAllocatedSize()
|
||||
% (&x - deepestStack));
|
||||
% (&x - deepestStack)
|
||||
% state.normalForms.size()
|
||||
% state.sessionNormalForms.size());
|
||||
if (showStats)
|
||||
printATermMapStats();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
||||
struct EvalState
|
||||
{
|
||||
ATermMap normalForms;
|
||||
ATermMap sessionNormalForms;
|
||||
ATermMap primOps;
|
||||
DrvRoots drvRoots;
|
||||
DrvHashes drvHashes; /* normalised derivation hashes */
|
||||
@@ -37,12 +38,19 @@ struct EvalState
|
||||
|
||||
unsigned int nrEvaluated;
|
||||
unsigned int nrCached;
|
||||
unsigned int nrDepthAfterReset;
|
||||
|
||||
bool allowUnsafeEquality;
|
||||
bool safeCache;
|
||||
|
||||
EvalState();
|
||||
~EvalState();
|
||||
|
||||
void addPrimOps();
|
||||
void addPrimOp(const string & name,
|
||||
unsigned int arity, PrimOp primOp);
|
||||
void loadNormalForms();
|
||||
void saveNormalForms();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -50,31 +50,55 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
||||
Expr e = evalExpr(state, i->value);
|
||||
string s;
|
||||
PathSet context;
|
||||
if (matchStr(e, s, context))
|
||||
meta[aterm2String(i->key)] = s;
|
||||
/* For future compatibility, ignore attribute values that are
|
||||
not strings. */
|
||||
MetaValue value;
|
||||
int n;
|
||||
ATermList es;
|
||||
if (matchStr(e, s, context)) {
|
||||
value.type = MetaValue::tpString;
|
||||
value.stringValue = s;
|
||||
meta[aterm2String(i->key)] = value;
|
||||
} else if (matchInt(e, n)) {
|
||||
value.type = MetaValue::tpInt;
|
||||
value.intValue = n;
|
||||
meta[aterm2String(i->key)] = value;
|
||||
} else if (matchList(e, es)) {
|
||||
value.type = MetaValue::tpStrings;
|
||||
for (ATermIterator j(es); j; ++j)
|
||||
value.stringValues.push_back(evalStringNoCtx(state, *j));
|
||||
meta[aterm2String(i->key)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
|
||||
MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
|
||||
{
|
||||
/* !!! evaluates all meta attributes => inefficient */
|
||||
MetaInfo meta = queryMetaInfo(state);
|
||||
MetaInfo::iterator i = meta.find(name);
|
||||
return i == meta.end() ? "" : i->second;
|
||||
return queryMetaInfo(state)[name];
|
||||
}
|
||||
|
||||
|
||||
void DrvInfo::setMetaInfo(const MetaInfo & meta)
|
||||
{
|
||||
ATermMap metaAttrs;
|
||||
for (MetaInfo::const_iterator i = meta.begin(); i != meta.end(); ++i)
|
||||
metaAttrs.set(toATerm(i->first),
|
||||
makeAttrRHS(makeStr(i->second), makeNoPos()));
|
||||
foreach (MetaInfo::const_iterator, i, meta) {
|
||||
Expr e;
|
||||
switch (i->second.type) {
|
||||
case MetaValue::tpInt: e = makeInt(i->second.intValue); break;
|
||||
case MetaValue::tpString: e = makeStr(i->second.stringValue); break;
|
||||
case MetaValue::tpStrings: {
|
||||
ATermList es = ATempty;
|
||||
foreach (Strings::const_iterator, j, i->second.stringValues)
|
||||
es = ATinsert(es, makeStr(*j));
|
||||
e = makeList(ATreverse(es));
|
||||
break;
|
||||
}
|
||||
default: abort();
|
||||
}
|
||||
metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos()));
|
||||
}
|
||||
attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,16 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
typedef std::map<string, string> MetaInfo;
|
||||
struct MetaValue
|
||||
{
|
||||
enum { tpNone, tpString, tpStrings, tpInt } type;
|
||||
string stringValue;
|
||||
Strings stringValues;
|
||||
int intValue;
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<string, MetaValue> MetaInfo;
|
||||
|
||||
|
||||
struct DrvInfo
|
||||
@@ -34,7 +43,7 @@ public:
|
||||
string queryDrvPath(EvalState & state) const;
|
||||
string queryOutPath(EvalState & state) const;
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
string queryMetaInfo(EvalState & state, const string & name) const;
|
||||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
|
||||
void setDrvPath(const string & s)
|
||||
{
|
||||
|
||||
@@ -70,6 +70,7 @@ Bool | ATermBool | Expr |
|
||||
Null | | Expr |
|
||||
|
||||
Bind | string Expr Pos | ATerm |
|
||||
BindAttrPath | ATermList Expr Pos | ATerm | # desugared during parsing
|
||||
Bind | string Expr | ATerm | ObsoleteBind
|
||||
Inherit | Expr ATermList Pos | ATerm |
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ string showPos(ATerm pos)
|
||||
if (matchNoPos(pos)) return "undefined position";
|
||||
if (!matchPos(pos, path, line, column))
|
||||
throw badTerm("position expected", pos);
|
||||
return (format("`%1%', line %2%") % aterm2String(path) % line).str();
|
||||
return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace nix {
|
||||
|
||||
|
||||
MakeError(EvalError, Error)
|
||||
MakeError(ParseError, Error)
|
||||
MakeError(AssertionError, EvalError)
|
||||
MakeError(ThrownError, AssertionError)
|
||||
MakeError(Abort, EvalError)
|
||||
|
||||
64
src/libexpr/normal-forms.cc
Normal file
64
src/libexpr/normal-forms.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "eval.hh"
|
||||
#include "util.hh"
|
||||
#include "globals.hh"
|
||||
#include "serialise.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
void EvalState::loadNormalForms()
|
||||
{
|
||||
// nixCacheFile = getEnv("NIX_CACHE_FILE", nixStateDir + "/reduce-cache");
|
||||
nixCacheFile = getEnv("NIX_CACHE_FILE");
|
||||
string loadFlag = getEnv("NIX_CACHE_FILE_LOAD", "1");
|
||||
|
||||
if(nixCacheFile == "" || loadFlag == "")
|
||||
return;
|
||||
if(!pathExists(nixCacheFile))
|
||||
return;
|
||||
|
||||
printMsg(lvlInfo, format("Load cache: ..."));
|
||||
try {
|
||||
int fd = open(nixCacheFile.c_str(), O_RDONLY);
|
||||
if (fd == -1)
|
||||
throw SysError(format("opening file `%1%'") % nixCacheFile);
|
||||
AutoCloseFD auto_fd = fd;
|
||||
|
||||
FdSource source = fd;
|
||||
normalForms = readATermMap(source);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("Cannot load cached reduce operations from %1%:\n") % nixCacheFile);
|
||||
throw;
|
||||
}
|
||||
printMsg(lvlInfo, format("Load cache: end"));
|
||||
}
|
||||
|
||||
void EvalState::saveNormalForms()
|
||||
{
|
||||
string saveFlag = getEnv("NIX_CACHE_FILE_SAVE", "");
|
||||
|
||||
if(nixCacheFile == "" || saveFlag == "")
|
||||
return;
|
||||
|
||||
printMsg(lvlInfo, format("Save cache: ..."));
|
||||
try {
|
||||
int fd = open(nixCacheFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
|
||||
if (fd == -1)
|
||||
throw SysError(format("opening file `%1%'") % nixCacheFile);
|
||||
AutoCloseFD auto_fd = fd;
|
||||
|
||||
FdSink sink = fd;
|
||||
writeATermMap(normalForms, sink);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("Cannot save cached reduce operations to %1%:\n") % nixCacheFile);
|
||||
throw;
|
||||
}
|
||||
printMsg(lvlInfo, format("Save cache: end"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "parser-tab.hh"
|
||||
#include "lexer-tab.hh"
|
||||
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
@@ -43,28 +44,128 @@ struct ParseData
|
||||
Path path;
|
||||
string error;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static Expr fixAttrs(int recursive, ATermList as)
|
||||
static string showAttrPath(ATermList attrPath)
|
||||
{
|
||||
ATermList bs = ATempty, cs = ATempty;
|
||||
ATermList * is = recursive ? &cs : &bs;
|
||||
string s;
|
||||
for (ATermIterator i(attrPath); i; ++i) {
|
||||
if (!s.empty()) s += '.';
|
||||
s += aterm2String(*i);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
struct Tree
|
||||
{
|
||||
Expr leaf; ATerm pos; bool recursive;
|
||||
typedef std::map<ATerm, Tree> Children;
|
||||
Children children;
|
||||
Tree() { leaf = 0; recursive = true; }
|
||||
};
|
||||
|
||||
|
||||
static ATermList buildAttrs(const Tree & t, ATermList & nonrec)
|
||||
{
|
||||
ATermList res = ATempty;
|
||||
for (Tree::Children::const_reverse_iterator i = t.children.rbegin();
|
||||
i != t.children.rend(); ++i)
|
||||
if (!i->second.recursive)
|
||||
nonrec = ATinsert(nonrec, makeBind(i->first, i->second.leaf, i->second.pos));
|
||||
else
|
||||
res = ATinsert(res, i->second.leaf
|
||||
? makeBind(i->first, i->second.leaf, i->second.pos)
|
||||
: makeBind(i->first, makeAttrs(buildAttrs(i->second, nonrec)), makeNoPos()));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static Expr fixAttrs(bool recursive, ATermList as)
|
||||
{
|
||||
Tree attrs;
|
||||
|
||||
for (ATermIterator i(as); i; ++i) {
|
||||
ATermList names;
|
||||
Expr src;
|
||||
ATerm pos;
|
||||
ATermList names, attrPath; Expr src, e; ATerm name, pos;
|
||||
|
||||
if (matchInherit(*i, src, names, pos)) {
|
||||
bool fromScope = matchScope(src);
|
||||
for (ATermIterator j(names); j; ++j) {
|
||||
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
||||
*is = ATinsert(*is, makeBind(*j, rhs, pos));
|
||||
if (attrs.children.find(*j) != attrs.children.end())
|
||||
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
|
||||
% showAttrPath(ATmakeList1(*j)) % showPos(pos));
|
||||
Tree & t(attrs.children[*j]);
|
||||
t.leaf = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
||||
t.pos = pos;
|
||||
if (recursive && fromScope) t.recursive = false;
|
||||
}
|
||||
} else bs = ATinsert(bs, *i);
|
||||
}
|
||||
|
||||
else if (matchBindAttrPath(*i, attrPath, e, pos)) {
|
||||
|
||||
Tree * t(&attrs);
|
||||
|
||||
for (ATermIterator j(attrPath); j; ) {
|
||||
name = *j; ++j;
|
||||
if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
|
||||
t = &(t->children[name]);
|
||||
}
|
||||
|
||||
if (t->leaf)
|
||||
throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%")
|
||||
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
|
||||
if (!t->children.empty())
|
||||
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
|
||||
% showAttrPath(attrPath) % showPos(pos));
|
||||
|
||||
t->leaf = e; t->pos = pos;
|
||||
}
|
||||
|
||||
else abort(); /* can't happen */
|
||||
}
|
||||
if (recursive)
|
||||
return makeRec(bs, cs);
|
||||
else
|
||||
return makeAttrs(bs);
|
||||
|
||||
ATermList nonrec = ATempty;
|
||||
ATermList rec = buildAttrs(attrs, nonrec);
|
||||
|
||||
return recursive ? makeRec(rec, nonrec) : makeAttrs(rec);
|
||||
}
|
||||
|
||||
|
||||
static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
|
||||
{
|
||||
ATerm name;
|
||||
ATermList formals;
|
||||
Pattern pat1, pat2;
|
||||
ATermBool ellipsis;
|
||||
if (matchVarPat(pat, name)) {
|
||||
if (map.get(name))
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
map.set(name, name);
|
||||
}
|
||||
else if (matchAttrsPat(pat, formals, ellipsis)) {
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm d1;
|
||||
if (!matchFormal(*i, name, d1)) abort();
|
||||
if (map.get(name))
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
map.set(name, name);
|
||||
}
|
||||
}
|
||||
else if (matchAtPat(pat, pat1, pat2)) {
|
||||
checkPatternVars(pos, map, pat1);
|
||||
checkPatternVars(pos, map, pat2);
|
||||
}
|
||||
else abort();
|
||||
}
|
||||
|
||||
|
||||
static void checkPatternVars(ATerm pos, Pattern pat)
|
||||
{
|
||||
ATermMap map;
|
||||
checkPatternVars(pos, map, pat);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +247,7 @@ static Expr stripIndentation(ATermList es)
|
||||
/* Remove the last line if it is empty and consists only of
|
||||
spaces. */
|
||||
if (n == 1) {
|
||||
unsigned int p = s2.find_last_of('\n');
|
||||
string::size_type p = s2.find_last_of('\n');
|
||||
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
|
||||
s2 = string(s2, 0, p + 1);
|
||||
}
|
||||
@@ -217,7 +318,7 @@ static void freeAndUnprotect(void * p)
|
||||
%type <t> start expr expr_function expr_if expr_op
|
||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
|
||||
%type <t> pattern pattern2
|
||||
%type <ts> binds ids expr_list string_parts ind_string_parts
|
||||
%type <ts> binds ids attrpath expr_list string_parts ind_string_parts
|
||||
%type <formals> formals
|
||||
%token <t> ID INT STR IND_STR PATH URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||
@@ -244,13 +345,13 @@ expr: expr_function;
|
||||
|
||||
expr_function
|
||||
: pattern ':' expr_function
|
||||
{ $$ = makeFunction($1, $3, CUR_POS); }
|
||||
{ checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); }
|
||||
| ASSERT expr ';' expr_function
|
||||
{ $$ = makeAssert($2, $4, CUR_POS); }
|
||||
| WITH expr ';' expr_function
|
||||
{ $$ = makeWith($2, $4, CUR_POS); }
|
||||
| LET binds IN expr_function
|
||||
{ $$ = makeSelect(fixAttrs(1, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); }
|
||||
{ $$ = makeSelect(fixAttrs(true, ATinsert($2, makeBindAttrPath(ATmakeList1(toATerm("<let-body>")), $4, CUR_POS))), toATerm("<let-body>")); }
|
||||
| expr_if
|
||||
;
|
||||
|
||||
@@ -305,12 +406,12 @@ expr_simple
|
||||
/* Let expressions `let {..., body = ...}' are just desugared
|
||||
into `(rec {..., body = ...}).body'. */
|
||||
| LET '{' binds '}'
|
||||
{ $$ = makeSelect(fixAttrs(1, $3), toATerm("body")); }
|
||||
{ $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); }
|
||||
| REC '{' binds '}'
|
||||
{ $$ = fixAttrs(1, $3); }
|
||||
{ $$ = fixAttrs(true, $3); }
|
||||
| '{' binds '}'
|
||||
{ $$ = fixAttrs(0, $2); }
|
||||
| '[' expr_list ']' { $$ = makeList($2); }
|
||||
{ $$ = fixAttrs(false, $2); }
|
||||
| '[' expr_list ']' { $$ = makeList(ATreverse($2)); }
|
||||
;
|
||||
|
||||
string_parts
|
||||
@@ -341,8 +442,8 @@ binds
|
||||
;
|
||||
|
||||
bind
|
||||
: ID '=' expr ';'
|
||||
{ $$ = makeBind($1, $3, CUR_POS); }
|
||||
: attrpath '=' expr ';'
|
||||
{ $$ = makeBindAttrPath(ATreverse($1), $3, CUR_POS); }
|
||||
| INHERIT inheritsrc ids ';'
|
||||
{ $$ = makeInherit($2, $3, CUR_POS); }
|
||||
;
|
||||
@@ -354,16 +455,18 @@ inheritsrc
|
||||
|
||||
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
|
||||
|
||||
attrpath
|
||||
: attrpath '.' ID { $$ = ATinsert($1, $3); }
|
||||
| ID { $$ = ATmakeList1($1); }
|
||||
;
|
||||
|
||||
expr_list
|
||||
: expr_select expr_list { $$ = ATinsert($2, $1); }
|
||||
/* yes, this is right-recursive, but it doesn't matter since
|
||||
otherwise we would need ATreverse which requires unbounded
|
||||
stack space */
|
||||
: expr_list expr_select { $$ = ATinsert($1, $2); }
|
||||
| { $$ = ATempty; }
|
||||
;
|
||||
|
||||
formals
|
||||
: formal ',' formals /* idem - right recursive */
|
||||
: formal ',' formals /* !!! right recursive */
|
||||
{ $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; }
|
||||
| formal
|
||||
{ $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; }
|
||||
@@ -392,84 +495,6 @@ formal
|
||||
namespace nix {
|
||||
|
||||
|
||||
static void checkAttrs(ATermMap & names, ATermList bnds)
|
||||
{
|
||||
for (ATermIterator i(bnds); i; ++i) {
|
||||
ATerm name;
|
||||
Expr e;
|
||||
ATerm pos;
|
||||
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
|
||||
if (names.get(name))
|
||||
throw EvalError(format("duplicate attribute `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
names.set(name, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
|
||||
{
|
||||
ATerm name;
|
||||
ATermList formals;
|
||||
Pattern pat1, pat2;
|
||||
ATermBool ellipsis;
|
||||
if (matchVarPat(pat, name)) {
|
||||
if (map.get(name))
|
||||
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
map.set(name, name);
|
||||
}
|
||||
else if (matchAttrsPat(pat, formals, ellipsis)) {
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm d1;
|
||||
if (!matchFormal(*i, name, d1)) abort();
|
||||
if (map.get(name))
|
||||
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
map.set(name, name);
|
||||
}
|
||||
}
|
||||
else if (matchAtPat(pat, pat1, pat2)) {
|
||||
checkPatternVars(pos, map, pat1);
|
||||
checkPatternVars(pos, map, pat2);
|
||||
}
|
||||
else abort();
|
||||
}
|
||||
|
||||
|
||||
static void checkAttrSets(ATerm e)
|
||||
{
|
||||
ATerm pat, body, pos;
|
||||
if (matchFunction(e, pat, body, pos)) {
|
||||
ATermMap map(16);
|
||||
checkPatternVars(pos, map, pat);
|
||||
}
|
||||
|
||||
ATermList bnds;
|
||||
if (matchAttrs(e, bnds)) {
|
||||
ATermMap names(ATgetLength(bnds));
|
||||
checkAttrs(names, bnds);
|
||||
}
|
||||
|
||||
ATermList rbnds, nrbnds;
|
||||
if (matchRec(e, rbnds, nrbnds)) {
|
||||
ATermMap names(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
||||
checkAttrs(names, rbnds);
|
||||
checkAttrs(names, nrbnds);
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
int arity = ATgetArity(ATgetAFun(e));
|
||||
for (int i = 0; i < arity; ++i)
|
||||
checkAttrSets(ATgetArgument(e, i));
|
||||
}
|
||||
|
||||
else if (ATgetType(e) == AT_LIST)
|
||||
for (ATermIterator i((ATermList) e); i; ++i)
|
||||
checkAttrSets(*i);
|
||||
}
|
||||
|
||||
|
||||
static Expr parse(EvalState & state,
|
||||
const char * text, const Path & path,
|
||||
const Path & basePath)
|
||||
@@ -484,16 +509,14 @@ static Expr parse(EvalState & state,
|
||||
int res = yyparse(scanner, &data);
|
||||
yylex_destroy(scanner);
|
||||
|
||||
if (res) throw EvalError(data.error);
|
||||
if (res) throw ParseError(data.error);
|
||||
|
||||
try {
|
||||
checkVarDefs(state.primOps, data.result);
|
||||
} catch (Error & e) {
|
||||
throw EvalError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
}
|
||||
|
||||
checkAttrSets(data.result);
|
||||
|
||||
return data.result;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,12 +74,14 @@ static Expr prim_null(EvalState & state, const ATermVector & args)
|
||||
platforms. */
|
||||
static Expr prim_currentSystem(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
state.nrDepthAfterReset = 0;
|
||||
return makeStr(thisSystem);
|
||||
}
|
||||
|
||||
|
||||
static Expr prim_currentTime(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
state.nrDepthAfterReset = 0;
|
||||
return ATmake("Int(<int>)", time(0));
|
||||
}
|
||||
|
||||
@@ -125,6 +127,27 @@ static Expr prim_isFunction(EvalState & state, const ATermVector & args)
|
||||
return makeBool(matchFunction(e, pat, body, pos));
|
||||
}
|
||||
|
||||
/* Determine whether the argument is an Int. */
|
||||
static Expr prim_isInt(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
int i;
|
||||
return makeBool(matchInt(evalExpr(state, args[0]), i));
|
||||
}
|
||||
|
||||
/* Determine whether the argument is an String. */
|
||||
static Expr prim_isString(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
string s;
|
||||
PathSet l;
|
||||
return makeBool(matchStr(evalExpr(state, args[0]), s, l));
|
||||
}
|
||||
|
||||
/* Determine whether the argument is an Bool. */
|
||||
static Expr prim_isBool(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
ATermBool b;
|
||||
return makeBool(matchBool(evalExpr(state, args[0]), b));
|
||||
}
|
||||
|
||||
static Expr prim_genericClosure(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
@@ -185,15 +208,29 @@ static Expr prim_abort(EvalState & state, const ATermVector & args)
|
||||
static Expr prim_throw(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context;
|
||||
throw ThrownError(format("user-thrown exception: `%1%'") %
|
||||
throw ThrownError(format("user-thrown exception: %1%") %
|
||||
evalString(state, args[0], context));
|
||||
}
|
||||
|
||||
|
||||
static Expr prim_addErrorContext(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context;
|
||||
try {
|
||||
return evalExpr(state, args[1]);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("%1%\n") %
|
||||
evalString(state, args[0], context));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return an environment variable. Use with care. */
|
||||
static Expr prim_getEnv(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
string name = evalStringNoCtx(state, args[0]);
|
||||
state.nrDepthAfterReset = 0;
|
||||
return makeStr(getEnv(name));
|
||||
}
|
||||
|
||||
@@ -215,6 +252,14 @@ static Expr prim_trace(EvalState & state, const ATermVector & args)
|
||||
*************************************************************/
|
||||
|
||||
|
||||
static bool isFixedOutput(const Derivation & drv)
|
||||
{
|
||||
return drv.outputs.size() == 1 &&
|
||||
drv.outputs.begin()->first == "out" &&
|
||||
drv.outputs.begin()->second.hash != "";
|
||||
}
|
||||
|
||||
|
||||
/* Returns the hash of a derivation modulo fixed-output
|
||||
subderivations. A fixed-output derivation is a derivation with one
|
||||
output (`out') for which an expected hash and hash algorithm are
|
||||
@@ -227,35 +272,29 @@ static Expr prim_trace(EvalState & state, const ATermVector & args)
|
||||
function, we do not want to rebuild everything depending on it
|
||||
(after all, (the hash of) the file being downloaded is unchanged).
|
||||
So the *output paths* should not change. On the other hand, the
|
||||
*derivation store expression paths* should change to reflect the
|
||||
new dependency graph.
|
||||
*derivation paths* should change to reflect the new dependency
|
||||
graph.
|
||||
|
||||
That's what this function does: it returns a hash which is just the
|
||||
of the derivation ATerm, except that any input store expression
|
||||
hash of the derivation ATerm, except that any input derivation
|
||||
paths have been replaced by the result of a recursive call to this
|
||||
function, and that for fixed-output derivations we return
|
||||
(basically) its outputHash. */
|
||||
function, and that for fixed-output derivations we return a hash of
|
||||
its output path. */
|
||||
static Hash hashDerivationModulo(EvalState & state, Derivation drv)
|
||||
{
|
||||
/* Return a fixed hash for fixed-output derivations. */
|
||||
if (drv.outputs.size() == 1) {
|
||||
if (isFixedOutput(drv)) {
|
||||
DerivationOutputs::const_iterator i = drv.outputs.begin();
|
||||
if (i->first == "out" &&
|
||||
i->second.hash != "")
|
||||
{
|
||||
return hashString(htSHA256, "fixed:out:"
|
||||
+ i->second.hashAlgo + ":"
|
||||
+ i->second.hash + ":"
|
||||
+ i->second.path);
|
||||
}
|
||||
return hashString(htSHA256, "fixed:out:"
|
||||
+ i->second.hashAlgo + ":"
|
||||
+ i->second.hash + ":"
|
||||
+ i->second.path);
|
||||
}
|
||||
|
||||
/* For other derivations, replace the inputs paths with recursive
|
||||
calls to this function.*/
|
||||
DerivationInputs inputs2;
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
{
|
||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
|
||||
Hash h = state.drvHashes[i->first];
|
||||
if (h.type == htUnknown) {
|
||||
Derivation drv2 = derivationFromPath(i->first);
|
||||
@@ -280,6 +319,7 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv)
|
||||
static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
startNest(nest, lvlVomit, "evaluating derivation");
|
||||
state.nrDepthAfterReset = 0;
|
||||
|
||||
ATermMap attrs;
|
||||
queryAllAttrs(evalExpr(state, args[0]), attrs, true);
|
||||
@@ -304,8 +344,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
|
||||
PathSet context;
|
||||
|
||||
string outputHash;
|
||||
string outputHashAlgo;
|
||||
string outputHash, outputHashAlgo;
|
||||
bool outputHashRecursive = false;
|
||||
|
||||
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
|
||||
@@ -364,13 +403,32 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
/* Everything in the context of the strings in the derivation
|
||||
attributes should be added as dependencies of the resulting
|
||||
derivation. */
|
||||
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
|
||||
debug(format("derivation uses `%1%'") % *i);
|
||||
assert(isStorePath(*i));
|
||||
if (isDerivation(*i))
|
||||
drv.inputDrvs[*i] = singleton<StringSet>("out");
|
||||
foreach (PathSet::iterator, i, context) {
|
||||
Path path = *i;
|
||||
|
||||
/* Paths marked with `=' denote that the path of a derivation
|
||||
is explicitly passed to the builder. Since that allows the
|
||||
builder to gain access to every path in the dependency
|
||||
graph of the derivation (including all outputs), all paths
|
||||
in the graph must be added to this derivation's list of
|
||||
inputs to ensure that they are available when the builder
|
||||
runs. */
|
||||
if (path.at(0) == '=') {
|
||||
path = string(path, 1);
|
||||
PathSet refs; computeFSClosure(path, refs);
|
||||
foreach (PathSet::iterator, j, refs) {
|
||||
drv.inputSrcs.insert(*j);
|
||||
if (isDerivation(*j))
|
||||
drv.inputDrvs[*j] = singleton<StringSet>("out");
|
||||
}
|
||||
}
|
||||
|
||||
debug(format("derivation uses `%1%'") % path);
|
||||
assert(isStorePath(path));
|
||||
if (isDerivation(path))
|
||||
drv.inputDrvs[path] = singleton<StringSet>("out");
|
||||
else
|
||||
drv.inputSrcs.insert(*i);
|
||||
drv.inputSrcs.insert(path);
|
||||
}
|
||||
|
||||
/* Do we have all required attributes? */
|
||||
@@ -380,6 +438,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
throw EvalError("required attribute `system' missing");
|
||||
|
||||
/* If an output hash was given, check it. */
|
||||
Path outPath;
|
||||
if (outputHash == "")
|
||||
outputHashAlgo = "";
|
||||
else {
|
||||
@@ -398,6 +457,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
% outputHash % outputHashAlgo);
|
||||
string s = outputHash;
|
||||
outputHash = printHash(h);
|
||||
outPath = makeFixedOutputPath(outputHashRecursive, ht, h, drvName);
|
||||
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
|
||||
}
|
||||
|
||||
@@ -413,13 +473,12 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
have an empty value. This ensures that changes in the set of
|
||||
output names do get reflected in the hash. */
|
||||
drv.env["out"] = "";
|
||||
drv.outputs["out"] =
|
||||
DerivationOutput("", outputHashAlgo, outputHash);
|
||||
drv.outputs["out"] = DerivationOutput("", outputHashAlgo, outputHash);
|
||||
|
||||
/* Use the masked derivation expression to compute the output
|
||||
path. */
|
||||
Path outPath = makeStorePath("output:out",
|
||||
hashDerivationModulo(state, drv), drvName);
|
||||
if (outPath == "")
|
||||
outPath = makeStorePath("output:out", hashDerivationModulo(state, drv), drvName);
|
||||
|
||||
/* Construct the final derivation store expression. */
|
||||
drv.env["out"] = outPath;
|
||||
@@ -442,7 +501,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
outAttrs.set(toATerm("outPath"),
|
||||
makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
outAttrs.set(toATerm("drvPath"),
|
||||
makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
makeAttrRHS(makeStr(drvPath, singleton<PathSet>("=" + drvPath)), makeNoPos()));
|
||||
|
||||
return makeAttrs(outAttrs);
|
||||
}
|
||||
@@ -494,11 +553,13 @@ static Expr prim_storePath(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context;
|
||||
Path path = canonPath(coerceToPath(state, args[0], context));
|
||||
state.nrDepthAfterReset = 0;
|
||||
if (!isInStore(path))
|
||||
throw EvalError(format("path `%1%' is not in the Nix store") % path);
|
||||
if (!store->isValidPath(path))
|
||||
throw EvalError(format("store path `%1%' is not valid") % path);
|
||||
context.insert(toStorePath(path));
|
||||
Path path2 = toStorePath(path);
|
||||
if (!store->isValidPath(path2))
|
||||
throw EvalError(format("store path `%1%' is not valid") % path2);
|
||||
context.insert(path2);
|
||||
return makeStr(path, context);
|
||||
}
|
||||
|
||||
@@ -509,6 +570,7 @@ static Expr prim_pathExists(EvalState & state, const ATermVector & args)
|
||||
Path path = coerceToPath(state, args[0], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
|
||||
state.nrDepthAfterReset = 0;
|
||||
return makeBool(pathExists(path));
|
||||
}
|
||||
|
||||
@@ -542,6 +604,7 @@ static Expr prim_readFile(EvalState & state, const ATermVector & args)
|
||||
Path path = coerceToPath(state, args[0], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
|
||||
state.nrDepthAfterReset = 0;
|
||||
return makeStr(readFile(path));
|
||||
}
|
||||
|
||||
@@ -574,11 +637,14 @@ static Expr prim_toFile(EvalState & state, const ATermVector & args)
|
||||
PathSet refs;
|
||||
|
||||
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
|
||||
if (isDerivation(*i))
|
||||
Path path = *i;
|
||||
if (path.at(0) == '=') path = string(path, 1);
|
||||
if (isDerivation(path))
|
||||
throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name);
|
||||
refs.insert(*i);
|
||||
refs.insert(path);
|
||||
}
|
||||
|
||||
state.nrDepthAfterReset = 0;
|
||||
Path storePath = readOnlyMode
|
||||
? computeStorePathForText(name, contents, refs)
|
||||
: store->addTextToStore(name, contents, refs);
|
||||
@@ -631,9 +697,10 @@ static Expr prim_filterSource(EvalState & state, const ATermVector & args)
|
||||
|
||||
FilterFromExpr filter(state, args[0]);
|
||||
|
||||
state.nrDepthAfterReset = 0;
|
||||
Path dstPath = readOnlyMode
|
||||
? computeStorePathForPath(path, false, false, "", filter).first
|
||||
: store->addToStore(path, false, false, "", filter);
|
||||
? computeStorePathForPath(path, true, htSHA256, filter).first
|
||||
: store->addToStore(path, true, htSHA256, filter);
|
||||
|
||||
return makeStr(dstPath, singleton<PathSet>(dstPath));
|
||||
}
|
||||
@@ -955,11 +1022,16 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("import", 1, prim_import);
|
||||
addPrimOp("isNull", 1, prim_isNull);
|
||||
addPrimOp("__isFunction", 1, prim_isFunction);
|
||||
addPrimOp("__isString", 1, prim_isString);
|
||||
addPrimOp("__isInt", 1, prim_isInt);
|
||||
addPrimOp("__isBool", 1, prim_isBool);
|
||||
addPrimOp("__genericClosure", 1, prim_genericClosure);
|
||||
addPrimOp("abort", 1, prim_abort);
|
||||
addPrimOp("throw", 1, prim_throw);
|
||||
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
|
||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||
addPrimOp("__trace", 2, prim_trace);
|
||||
|
||||
|
||||
// Expr <-> String
|
||||
addPrimOp("__exprToString", 1, prim_exprToString);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
pkglib_LTLIBRARIES = libmain.la
|
||||
|
||||
libmain_la_SOURCES = shared.cc shared.hh
|
||||
libmain_la_SOURCES = shared.cc
|
||||
|
||||
libmain_la_LIBADD = ../libstore/libstore.la
|
||||
|
||||
pkginclude_HEADERS = shared.hh
|
||||
|
||||
AM_CXXFLAGS = \
|
||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||
|
||||
@@ -125,6 +125,9 @@ RemoveTempRoots::~RemoveTempRoots()
|
||||
}
|
||||
|
||||
|
||||
static bool showTrace = false;
|
||||
|
||||
|
||||
/* Initialize and reorder arguments, then call the actual argument
|
||||
processor. */
|
||||
static void initAndRun(int argc, char * * argv)
|
||||
@@ -138,7 +141,6 @@ static void initAndRun(int argc, char * * argv)
|
||||
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
nixChrootsDir = canonPath(getEnv("NIX_CHROOTS_DIR", nixStateDir + "/chroots"));
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
@@ -244,6 +246,15 @@ static void initAndRun(int argc, char * * argv)
|
||||
maxSilentTime = getIntArg(arg, i, args.end());
|
||||
else if (arg == "--no-build-hook")
|
||||
useBuildHook = false;
|
||||
else if (arg == "--show-trace")
|
||||
showTrace = true;
|
||||
else if (arg == "--option") {
|
||||
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
|
||||
string name = *i;
|
||||
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
|
||||
string value = *i;
|
||||
overrideSetting(name, tokenizeString(value));
|
||||
}
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
@@ -359,7 +370,9 @@ int main(int argc, char * * argv)
|
||||
% e.what() % programId);
|
||||
return 1;
|
||||
} catch (BaseError & e) {
|
||||
printMsg(lvlError, format("error: %1%") % e.msg());
|
||||
printMsg(lvlError, format("error: %1%%2%") % (showTrace ? e.prefix() : "") % e.msg());
|
||||
if (e.prefix() != "" && !showTrace)
|
||||
printMsg(lvlError, "(use `--show-trace' to show detailed location information)");
|
||||
return 1;
|
||||
} catch (std::exception & e) {
|
||||
printMsg(lvlError, format("error: %1%") % e.what());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,8 +21,7 @@ Path writeDerivation(const Derivation & drv, const string & name)
|
||||
{
|
||||
PathSet references;
|
||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
for (DerivationInputs::const_iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
|
||||
references.insert(i->first);
|
||||
/* Note that the outputs of a derivation are *not* references
|
||||
(that can be missing (of course) and should not necessarily be
|
||||
|
||||
@@ -247,9 +247,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
Strings tempRootFiles = readDirectory(
|
||||
(format("%1%/%2%") % nixStateDir % tempRootsDir).str());
|
||||
|
||||
for (Strings::iterator i = tempRootFiles.begin();
|
||||
i != tempRootFiles.end(); ++i)
|
||||
{
|
||||
foreach (Strings::iterator, i, tempRootFiles) {
|
||||
Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
|
||||
|
||||
debug(format("reading temporary root file `%1%'") % path);
|
||||
@@ -328,7 +326,7 @@ static void findRoots(const Path & path, bool recurseSymlinks,
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
foreach (Strings::iterator, i, names)
|
||||
findRoots(path + "/" + *i, recurseSymlinks, deleteStale, roots);
|
||||
}
|
||||
|
||||
@@ -399,7 +397,7 @@ static void addAdditionalRoots(PathSet & roots)
|
||||
|
||||
Strings paths = tokenizeString(result, "\n");
|
||||
|
||||
for (Strings::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
foreach (Strings::iterator, i, paths) {
|
||||
if (isInStore(*i)) {
|
||||
Path path = toStorePath(*i);
|
||||
if (roots.find(path) == roots.end() && store->isValidPath(path)) {
|
||||
@@ -421,8 +419,7 @@ static void dfsVisit(const PathSet & paths, const Path & path,
|
||||
if (store->isValidPath(path))
|
||||
store->queryReferences(path, references);
|
||||
|
||||
for (PathSet::iterator i = references.begin();
|
||||
i != references.end(); ++i)
|
||||
foreach (PathSet::iterator, i, references)
|
||||
/* Don't traverse into paths that don't exist. That can
|
||||
happen due to substitutes for non-existent paths. */
|
||||
if (*i != path && paths.find(*i) != paths.end())
|
||||
@@ -436,7 +433,7 @@ Paths topoSortPaths(const PathSet & paths)
|
||||
{
|
||||
Paths sorted;
|
||||
PathSet visited;
|
||||
for (PathSet::const_iterator i = paths.begin(); i != paths.end(); ++i)
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
dfsVisit(paths, *i, visited, sorted);
|
||||
return sorted;
|
||||
}
|
||||
@@ -453,7 +450,7 @@ static time_t lastFileAccessTime(const Path & path)
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
time_t last = 0;
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i) {
|
||||
foreach (Strings::iterator, i, names) {
|
||||
time_t t = lastFileAccessTime(path + "/" + *i);
|
||||
if (t > last) last = t;
|
||||
}
|
||||
@@ -476,30 +473,13 @@ void LocalStore::gcPath(const GCOptions & options, GCResults & results,
|
||||
|
||||
if (!pathExists(path)) return;
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
AutoCloseFD fdLock;
|
||||
|
||||
/* Only delete a lock file if we can acquire a write lock on it.
|
||||
That means that it's either stale, or the process that created
|
||||
it hasn't locked it yet. In the latter case the other process
|
||||
will detect that we deleted the lock, and retry (see
|
||||
pathlocks.cc). */
|
||||
if (path.size() >= 5 && string(path, path.size() - 5) == ".lock") {
|
||||
fdLock = openLockFile(path, false);
|
||||
if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
|
||||
debug(format("skipping active lock `%1%'") % path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Okay, it's safe to delete. */
|
||||
unsigned long long bytesFreed, blocksFreed;
|
||||
deleteFromStore(path, bytesFreed, blocksFreed);
|
||||
results.bytesFreed += bytesFreed;
|
||||
results.blocksFreed += blocksFreed;
|
||||
|
||||
if (results.bytesFreed > options.maxFreed) {
|
||||
if (options.maxFreed && results.bytesFreed > options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted more than %1% bytes; stopping") % options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
}
|
||||
@@ -513,12 +493,6 @@ void LocalStore::gcPath(const GCOptions & options, GCResults & results,
|
||||
throw GCLimitReached();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
if (fdLock != -1)
|
||||
/* Write token to stale (deleted) lock file. */
|
||||
writeFull(fdLock, (const unsigned char *) "d", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -569,7 +543,7 @@ struct CachingAtimeComparator : public std::binary_function<Path, Path, bool>
|
||||
};
|
||||
|
||||
|
||||
string showTime(const string & format, time_t t)
|
||||
static string showTime(const string & format, time_t t)
|
||||
{
|
||||
char s[128];
|
||||
strftime(s, sizeof s, format.c_str(), localtime(&t));
|
||||
@@ -577,6 +551,26 @@ string showTime(const string & format, time_t t)
|
||||
}
|
||||
|
||||
|
||||
static bool isLive(const Path & path, const PathSet & livePaths,
|
||||
const PathSet & tempRoots, const PathSet & tempRootsClosed)
|
||||
{
|
||||
if (livePaths.find(path) != livePaths.end() ||
|
||||
tempRootsClosed.find(path) != tempRootsClosed.end()) return true;
|
||||
|
||||
/* A lock file belonging to a path that we're building right
|
||||
now isn't garbage. */
|
||||
if (hasSuffix(path, ".lock") && tempRoots.find(string(path, 0, path.size() - 5)) != tempRoots.end())
|
||||
return true;
|
||||
|
||||
/* Don't delete .chroot directories for derivations that are
|
||||
currently being built. */
|
||||
if (hasSuffix(path, ".chroot") && tempRoots.find(string(path, 0, path.size() - 7)) != tempRoots.end())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
{
|
||||
bool gcKeepOutputs =
|
||||
@@ -597,8 +591,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(true);
|
||||
|
||||
PathSet roots;
|
||||
for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i)
|
||||
roots.insert(i->second);
|
||||
foreach (Roots::iterator, i, rootMap) roots.insert(i->second);
|
||||
|
||||
/* Add additional roots returned by the program specified by the
|
||||
NIX_ROOT_FINDER environment variable. This is typically used
|
||||
@@ -616,13 +609,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
roots under the `references' relation. */
|
||||
printMsg(lvlError, format("computing live paths..."));
|
||||
PathSet livePaths;
|
||||
for (PathSet::const_iterator i = roots.begin(); i != roots.end(); ++i)
|
||||
foreach (PathSet::const_iterator, i, roots)
|
||||
computeFSClosure(canonPath(*i), livePaths);
|
||||
|
||||
if (gcKeepDerivations) {
|
||||
for (PathSet::iterator i = livePaths.begin();
|
||||
i != livePaths.end(); ++i)
|
||||
{
|
||||
foreach (PathSet::iterator, i, livePaths) {
|
||||
/* Note that the deriver need not be valid (e.g., if we
|
||||
previously ran the collector with `gcKeepDerivations'
|
||||
turned off). */
|
||||
@@ -634,8 +625,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
if (gcKeepOutputs) {
|
||||
/* Hmz, identical to storePathRequisites in nix-store. */
|
||||
for (PathSet::iterator i = livePaths.begin();
|
||||
i != livePaths.end(); ++i)
|
||||
foreach (PathSet::iterator, i, livePaths)
|
||||
if (isDerivation(*i)) {
|
||||
Derivation drv = derivationFromPath(*i);
|
||||
|
||||
@@ -645,8 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
gcLevel = defaultGcLevel;
|
||||
|
||||
if (gcLevel >= gcKeepOutputsThreshold)
|
||||
for (DerivationOutputs::iterator j = drv.outputs.begin();
|
||||
j != drv.outputs.end(); ++j)
|
||||
foreach (DerivationOutputs::iterator, j, drv.outputs)
|
||||
if (isValidPath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths);
|
||||
}
|
||||
@@ -671,7 +660,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
(and computeFSClosure() assumes that the presence of a path
|
||||
means that it has already been closed). */
|
||||
PathSet tempRootsClosed;
|
||||
for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i)
|
||||
foreach (PathSet::iterator, i, tempRoots)
|
||||
if (isValidPath(*i))
|
||||
computeFSClosure(*i, tempRootsClosed);
|
||||
else
|
||||
@@ -691,9 +680,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
Paths entries = readDirectory(nixStore);
|
||||
foreach (Paths::iterator, i, entries) {
|
||||
Path path = canonPath(nixStore + "/" + *i);
|
||||
if (livePaths.find(path) == livePaths.end() &&
|
||||
tempRootsClosed.find(path) == tempRootsClosed.end())
|
||||
storePaths.insert(path);
|
||||
if (!isLive(path, livePaths, tempRoots, tempRootsClosed)) storePaths.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,10 +688,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
foreach (PathSet::iterator, i, options.pathsToDelete) {
|
||||
assertStorePath(*i);
|
||||
storePaths.insert(*i);
|
||||
if (livePaths.find(*i) != livePaths.end())
|
||||
if (isLive(*i, livePaths, tempRoots, tempRootsClosed))
|
||||
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
|
||||
if (tempRootsClosed.find(*i) != tempRootsClosed.end())
|
||||
throw Error(format("cannot delete path `%1%' since it is temporarily in use") % *i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ string nixDataDir = "/UNINIT";
|
||||
string nixLogDir = "/UNINIT";
|
||||
string nixStateDir = "/UNINIT";
|
||||
string nixDBPath = "/UNINIT";
|
||||
string nixCacheFile = "/UNINIT";
|
||||
string nixConfDir = "/UNINIT";
|
||||
string nixLibexecDir = "/UNINIT";
|
||||
string nixBinDir = "/UNINIT";
|
||||
string nixChrootsDir = "/UNINIT";
|
||||
|
||||
bool keepFailed = false;
|
||||
bool keepGoing = false;
|
||||
@@ -25,7 +25,7 @@ Verbosity buildVerbosity = lvlInfo;
|
||||
unsigned int maxBuildJobs = 1;
|
||||
bool readOnlyMode = false;
|
||||
string thisSystem = "unset";
|
||||
unsigned int maxSilentTime = 0;
|
||||
time_t maxSilentTime = 0;
|
||||
Paths substituters;
|
||||
bool useBuildHook = true;
|
||||
bool printBuildTrace = false;
|
||||
@@ -35,6 +35,9 @@ static bool settingsRead = false;
|
||||
|
||||
static std::map<string, Strings> settings;
|
||||
|
||||
/* Overriden settings. */
|
||||
std::map<string, Strings> settingsCmdline;
|
||||
|
||||
|
||||
string & at(Strings & ss, unsigned int n)
|
||||
{
|
||||
@@ -74,6 +77,8 @@ static void readSettings()
|
||||
advance(i, 2);
|
||||
settings[name] = Strings(i, tokens.end());
|
||||
};
|
||||
|
||||
settings.insert(settingsCmdline.begin(), settingsCmdline.end());
|
||||
|
||||
settingsRead = true;
|
||||
}
|
||||
@@ -119,6 +124,13 @@ unsigned int queryIntSetting(const string & name, unsigned int def)
|
||||
}
|
||||
|
||||
|
||||
void overrideSetting(const string & name, const Strings & value)
|
||||
{
|
||||
if (settingsRead) settings[name] = value;
|
||||
settingsCmdline[name] = value;
|
||||
}
|
||||
|
||||
|
||||
void reloadSettings()
|
||||
{
|
||||
settingsRead = false;
|
||||
|
||||
@@ -24,6 +24,9 @@ extern string nixStateDir;
|
||||
/* nixDBPath is the path name of our Berkeley DB environment. */
|
||||
extern string nixDBPath;
|
||||
|
||||
/* nixCacheFile is the path name of the normal form cache. */
|
||||
extern string nixCacheFile;
|
||||
|
||||
/* nixConfDir is the directory where configuration files are
|
||||
stored. */
|
||||
extern string nixConfDir;
|
||||
@@ -35,12 +38,6 @@ extern string nixLibexecDir;
|
||||
/* nixBinDir is the directory where the main programs are stored. */
|
||||
extern string nixBinDir;
|
||||
|
||||
/* nixChrootsDir is the directory where we create chroot environments
|
||||
(when chroot builds are enabled). We don't put these under /tmp to
|
||||
prevent "rm -rf /tmp" from recursing into /nix/store via the bind
|
||||
mounts in the chroots. */
|
||||
extern string nixChrootsDir;
|
||||
|
||||
|
||||
/* Misc. global flags. */
|
||||
|
||||
@@ -71,7 +68,7 @@ extern string thisSystem;
|
||||
/* The maximum time in seconds that a builer can go without producing
|
||||
any output on stdout/stderr before it is killed. 0 means
|
||||
infinity. */
|
||||
extern unsigned int maxSilentTime;
|
||||
extern time_t maxSilentTime;
|
||||
|
||||
/* The substituters. There are programs that can somehow realise a
|
||||
store path without building, e.g., by downloading it or copying it
|
||||
@@ -107,6 +104,8 @@ bool queryBoolSetting(const string & name, bool def);
|
||||
|
||||
unsigned int queryIntSetting(const string & name, unsigned int def);
|
||||
|
||||
void overrideSetting(const string & name, const Strings & value);
|
||||
|
||||
void reloadSettings();
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ LocalStore::LocalStore()
|
||||
|
||||
if (readOnlyMode) return;
|
||||
|
||||
createDirs(nixStore);
|
||||
|
||||
checkStoreNotSymlink();
|
||||
|
||||
try {
|
||||
@@ -65,6 +67,7 @@ LocalStore::LocalStore()
|
||||
|
||||
createDirs(nixDBPath + "/info");
|
||||
createDirs(nixDBPath + "/referrer");
|
||||
createDirs(nixDBPath + "/failed");
|
||||
|
||||
int curSchema = getSchema();
|
||||
if (curSchema > nixSchemaVersion)
|
||||
@@ -85,8 +88,8 @@ LocalStore::~LocalStore()
|
||||
flushDelayedUpdates();
|
||||
|
||||
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
|
||||
i->second.toBuf.reset();
|
||||
i->second.to.reset();
|
||||
i->second.to.close();
|
||||
i->second.from.close();
|
||||
i->second.pid.wait(true);
|
||||
}
|
||||
|
||||
@@ -108,23 +111,6 @@ int LocalStore::getSchema()
|
||||
}
|
||||
|
||||
|
||||
void copyPath(const Path & src, const Path & dst, PathFilter & filter)
|
||||
{
|
||||
debug(format("copying `%1%' to `%2%'") % src % dst);
|
||||
|
||||
/* Dump an archive of the path `src' into a string buffer, then
|
||||
restore the archive to `dst'. This is not a very good method
|
||||
for very large paths, but `copyPath' is mainly used for small
|
||||
files. */
|
||||
|
||||
StringSink sink;
|
||||
dumpPath(src, sink, filter);
|
||||
|
||||
StringSource source(sink.s);
|
||||
restorePath(dst, source);
|
||||
}
|
||||
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, bool recurse)
|
||||
{
|
||||
checkInterrupt();
|
||||
@@ -167,7 +153,7 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
|
||||
if (st.st_mtime != 0) {
|
||||
struct utimbuf utimbuf;
|
||||
utimbuf.actime = st.st_atime;
|
||||
utimbuf.modtime = 0;
|
||||
utimbuf.modtime = 1; /* 1 second into the epoch */
|
||||
if (utime(path.c_str(), &utimbuf) == -1)
|
||||
throw SysError(format("changing modification time of `%1%'") % path);
|
||||
}
|
||||
@@ -176,7 +162,7 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
|
||||
|
||||
if (recurse && S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
foreach (Strings::iterator, i, names)
|
||||
canonicalisePathMetaData(path + "/" + *i, true);
|
||||
}
|
||||
}
|
||||
@@ -213,6 +199,13 @@ static Path referrersFileFor(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
static Path failedFileFor(const Path & path)
|
||||
{
|
||||
string baseName = baseNameOf(path);
|
||||
return (format("%1%/failed/%2%") % nixDBPath % baseName).str();
|
||||
}
|
||||
|
||||
|
||||
static Path tmpFileForAtomicUpdate(const Path & path)
|
||||
{
|
||||
return (format("%1%/.%2%.%3%") % dirOf(path) % getpid() % baseNameOf(path)).str();
|
||||
@@ -332,6 +325,8 @@ void LocalStore::registerValidPath(const ValidPathInfo & info, bool ignoreValidi
|
||||
appendReferrer(*i, info.path, false);
|
||||
}
|
||||
|
||||
assert(info.hash.type == htSHA256);
|
||||
|
||||
string s = (format(
|
||||
"Hash: sha256:%1%\n"
|
||||
"References: %2%\n"
|
||||
@@ -350,6 +345,20 @@ void LocalStore::registerValidPath(const ValidPathInfo & info, bool ignoreValidi
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::registerFailedPath(const Path & path)
|
||||
{
|
||||
/* Write an empty file in the .../failed directory to denote the
|
||||
failure of the builder for `path'. */
|
||||
writeFile(failedFileFor(path), "");
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::hasPathFailed(const Path & path)
|
||||
{
|
||||
return pathExists(failedFileFor(path));
|
||||
}
|
||||
|
||||
|
||||
Hash parseHashField(const Path & path, const string & s)
|
||||
{
|
||||
string::size_type colon = s.find(':');
|
||||
@@ -364,7 +373,7 @@ Hash parseHashField(const Path & path, const string & s)
|
||||
}
|
||||
|
||||
|
||||
ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||
ValidPathInfo LocalStore::queryPathInfo(const Path & path, bool ignoreErrors)
|
||||
{
|
||||
ValidPathInfo res;
|
||||
res.path = path;
|
||||
@@ -383,8 +392,8 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||
/* Parse it. */
|
||||
Strings lines = tokenizeString(info, "\n");
|
||||
|
||||
for (Strings::iterator i = lines.begin(); i != lines.end(); ++i) {
|
||||
unsigned int p = i->find(':');
|
||||
foreach (Strings::iterator, i, lines) {
|
||||
string::size_type p = i->find(':');
|
||||
if (p == string::npos) continue; /* bad line */
|
||||
string name(*i, 0, p);
|
||||
string value(*i, p + 2);
|
||||
@@ -394,7 +403,12 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||
} else if (name == "Deriver") {
|
||||
res.deriver = value;
|
||||
} else if (name == "Hash") {
|
||||
res.hash = parseHashField(path, value);
|
||||
try {
|
||||
res.hash = parseHashField(path, value);
|
||||
} catch (Error & e) {
|
||||
if (!ignoreErrors) throw;
|
||||
printMsg(lvlError, format("cannot parse hash field in `%1%': %2%") % infoFile % e.msg());
|
||||
}
|
||||
} else if (name == "Registered-At") {
|
||||
int n = 0;
|
||||
string2Int(value, n);
|
||||
@@ -419,7 +433,7 @@ PathSet LocalStore::queryValidPaths()
|
||||
{
|
||||
PathSet paths;
|
||||
Strings entries = readDirectory(nixDBPath + "/info");
|
||||
for (Strings::iterator i = entries.begin(); i != entries.end(); ++i)
|
||||
foreach (Strings::iterator, i, entries)
|
||||
if (i->at(0) != '.') paths.insert(nixStore + "/" + *i);
|
||||
return paths;
|
||||
}
|
||||
@@ -454,7 +468,7 @@ bool LocalStore::queryReferrersInternal(const Path & path, PathSet & referrers)
|
||||
|
||||
Paths refs = tokenizeString(readFile(fd), " ");
|
||||
|
||||
for (Paths::iterator i = refs.begin(); i != refs.end(); ++i)
|
||||
foreach (Paths::iterator, i, refs)
|
||||
/* Referrers can be invalid (see registerValidPath() for the
|
||||
invariant), so we only return one if it is valid. */
|
||||
if (isStorePath(*i) && isValidPath(*i)) referrers.insert(*i); else allValid = false;
|
||||
@@ -512,23 +526,16 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||
|
||||
/* Parent. */
|
||||
|
||||
toPipe.readSide.close();
|
||||
fromPipe.writeSide.close();
|
||||
|
||||
run.toBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(toPipe.writeSide.borrow(), std::ios_base::out));
|
||||
run.to = boost::shared_ptr<std::ostream>(new std::ostream(&*run.toBuf));
|
||||
|
||||
run.fromBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(fromPipe.readSide.borrow(), std::ios_base::in));
|
||||
run.from = boost::shared_ptr<std::istream>(new std::istream(&*run.fromBuf));
|
||||
run.to = toPipe.writeSide.borrow();
|
||||
run.from = fromPipe.readSide.borrow();
|
||||
}
|
||||
|
||||
|
||||
template<class T> T getIntLine(std::istream & str)
|
||||
template<class T> T getIntLine(int fd)
|
||||
{
|
||||
string s;
|
||||
string s = readLine(fd);
|
||||
T res;
|
||||
getline(str, s);
|
||||
if (!str || !string2Int(s, res)) throw Error("integer expected from stream");
|
||||
if (!string2Int(s, res)) throw Error("integer expected from stream");
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -538,10 +545,8 @@ bool LocalStore::hasSubstitutes(const Path & path)
|
||||
foreach (Paths::iterator, i, substituters) {
|
||||
RunningSubstituter & run(runningSubstituters[*i]);
|
||||
startSubstituter(*i, run);
|
||||
|
||||
*run.to << "have\n" << path << "\n" << std::flush;
|
||||
|
||||
if (getIntLine<int>(*run.from)) return true;
|
||||
writeLine(run.to, "have\n" + path);
|
||||
if (getIntLine<int>(run.from)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -554,19 +559,19 @@ bool LocalStore::querySubstitutablePathInfo(const Path & substituter,
|
||||
RunningSubstituter & run(runningSubstituters[substituter]);
|
||||
startSubstituter(substituter, run);
|
||||
|
||||
*run.to << "info\n" << path << "\n" << std::flush;
|
||||
|
||||
if (!getIntLine<int>(*run.from)) return false;
|
||||
writeLine(run.to, "info\n" + path);
|
||||
|
||||
if (!getIntLine<int>(run.from)) return false;
|
||||
|
||||
getline(*run.from, info.deriver);
|
||||
info.deriver = readLine(run.from);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
int nrRefs = getIntLine<int>(*run.from);
|
||||
int nrRefs = getIntLine<int>(run.from);
|
||||
while (nrRefs--) {
|
||||
Path p; getline(*run.from, p);
|
||||
Path p = readLine(run.from);
|
||||
assertStorePath(p);
|
||||
info.references.insert(p);
|
||||
}
|
||||
info.downloadSize = getIntLine<long long>(*run.from);
|
||||
info.downloadSize = getIntLine<long long>(run.from);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -595,8 +600,7 @@ static void dfsVisit(std::map<Path, ValidPathInfo> & infos,
|
||||
|
||||
ValidPathInfo & info(infos[path]);
|
||||
|
||||
for (PathSet::iterator i = info.references.begin();
|
||||
i != info.references.end(); ++i)
|
||||
foreach (PathSet::iterator, i, info.references)
|
||||
if (infos.find(*i) != infos.end())
|
||||
dfsVisit(infos, *i, visited, sorted);
|
||||
|
||||
@@ -611,15 +615,15 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
/* Sort the paths topologically under the references relation, so
|
||||
that if path A is referenced by B, then A is registered before
|
||||
B. */
|
||||
for (ValidPathInfos::const_iterator i = infos.begin(); i != infos.end(); ++i)
|
||||
foreach (ValidPathInfos::const_iterator, i, infos)
|
||||
infosMap[i->path] = *i;
|
||||
|
||||
PathSet visited;
|
||||
Paths sorted;
|
||||
for (ValidPathInfos::const_iterator i = infos.begin(); i != infos.end(); ++i)
|
||||
foreach (ValidPathInfos::const_iterator, i, infos)
|
||||
dfsVisit(infosMap, i->path, visited, sorted);
|
||||
|
||||
for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i)
|
||||
foreach (Paths::iterator, i, sorted)
|
||||
registerValidPath(infosMap[*i]);
|
||||
}
|
||||
|
||||
@@ -670,16 +674,12 @@ void LocalStore::invalidatePath(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
|
||||
bool recursive, string hashAlgo, PathFilter & filter)
|
||||
Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||
bool recursive, HashType hashAlgo)
|
||||
{
|
||||
Path srcPath(absPath(_srcPath));
|
||||
debug(format("adding `%1%' to the store") % srcPath);
|
||||
Hash h = hashString(hashAlgo, dump);
|
||||
|
||||
std::pair<Path, Hash> pr =
|
||||
computeStorePathForPath(srcPath, fixed, recursive, hashAlgo, filter);
|
||||
Path & dstPath(pr.first);
|
||||
Hash & h(pr.second);
|
||||
Path dstPath = makeFixedOutputPath(recursive, hashAlgo, h, name);
|
||||
|
||||
addTempRoot(dstPath);
|
||||
|
||||
@@ -694,16 +694,22 @@ Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
|
||||
|
||||
if (pathExists(dstPath)) deletePathWrapped(dstPath);
|
||||
|
||||
copyPath(srcPath, dstPath, filter);
|
||||
|
||||
Hash h2 = hashPath(htSHA256, dstPath, filter);
|
||||
if (h != h2)
|
||||
throw Error(format("contents of `%1%' changed while copying it to `%2%' (%3% -> %4%)")
|
||||
% srcPath % dstPath % printHash(h) % printHash(h2));
|
||||
if (recursive) {
|
||||
StringSource source(dump);
|
||||
restorePath(dstPath, source);
|
||||
} else
|
||||
writeStringToFile(dstPath, dump);
|
||||
|
||||
canonicalisePathMetaData(dstPath);
|
||||
|
||||
registerValidPath(dstPath, h, PathSet(), "");
|
||||
|
||||
/* Register the SHA-256 hash of the NAR serialisation of
|
||||
the path in the database. We may just have computed it
|
||||
above (if called with recursive == true and hashAlgo ==
|
||||
sha256); otherwise, compute it here. */
|
||||
registerValidPath(dstPath,
|
||||
(recursive && hashAlgo == htSHA256) ? h :
|
||||
(recursive ? hashString(htSHA256, dump) : hashPath(htSHA256, dstPath)),
|
||||
PathSet(), "");
|
||||
}
|
||||
|
||||
outputLock.setDeletion(true);
|
||||
@@ -713,10 +719,29 @@ Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
|
||||
}
|
||||
|
||||
|
||||
Path LocalStore::addTextToStore(const string & suffix, const string & s,
|
||||
Path LocalStore::addToStore(const Path & _srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter)
|
||||
{
|
||||
Path srcPath(absPath(_srcPath));
|
||||
debug(format("adding `%1%' to the store") % srcPath);
|
||||
|
||||
/* Read the whole path into memory. This is not a very scalable
|
||||
method for very large paths, but `copyPath' is mainly used for
|
||||
small files. */
|
||||
StringSink sink;
|
||||
if (recursive)
|
||||
dumpPath(srcPath, sink, filter);
|
||||
else
|
||||
sink.s = readFile(srcPath);
|
||||
|
||||
return addToStoreFromDump(sink.s, baseNameOf(srcPath), recursive, hashAlgo);
|
||||
}
|
||||
|
||||
|
||||
Path LocalStore::addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references)
|
||||
{
|
||||
Path dstPath = computeStorePathForText(suffix, s, references);
|
||||
Path dstPath = computeStorePathForText(name, s, references);
|
||||
|
||||
addTempRoot(dstPath);
|
||||
|
||||
@@ -854,7 +879,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
|
||||
store path follows the archive data proper), and besides, we
|
||||
don't know yet whether the signature is valid. */
|
||||
Path tmpDir = createTempDir(nixStore);
|
||||
AutoDelete delTmp(tmpDir);
|
||||
AutoDelete delTmp(tmpDir); /* !!! could be GC'ed! */
|
||||
Path unpacked = tmpDir + "/unpacked";
|
||||
|
||||
restorePath(unpacked, hashAndReadSource);
|
||||
@@ -913,7 +938,14 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
|
||||
|
||||
if (!isValidPath(dstPath)) {
|
||||
|
||||
PathLocks outputLock(singleton<PathSet, Path>(dstPath));
|
||||
PathLocks outputLock;
|
||||
|
||||
/* Lock the output path. But don't lock if we're being called
|
||||
from a build hook (whose parent process already acquired a
|
||||
lock on this path). */
|
||||
Strings locksHeld = tokenizeString(getEnv("NIX_HELD_LOCKS"));
|
||||
if (find(locksHeld.begin(), locksHeld.end(), dstPath) == locksHeld.end())
|
||||
outputLock.lockPaths(singleton<PathSet, Path>(dstPath));
|
||||
|
||||
if (!isValidPath(dstPath)) {
|
||||
|
||||
@@ -971,7 +1003,7 @@ void LocalStore::verifyStore(bool checkContents)
|
||||
|
||||
PathSet validPaths2 = queryValidPaths(), validPaths;
|
||||
|
||||
for (PathSet::iterator i = validPaths2.begin(); i != validPaths2.end(); ++i) {
|
||||
foreach (PathSet::iterator, i, validPaths2) {
|
||||
checkInterrupt();
|
||||
if (!isStorePath(*i)) {
|
||||
printMsg(lvlError, format("path `%1%' is not in the Nix store") % *i);
|
||||
@@ -989,26 +1021,25 @@ void LocalStore::verifyStore(bool checkContents)
|
||||
|
||||
std::map<Path, PathSet> referrersCache;
|
||||
|
||||
for (PathSet::iterator i = validPaths.begin(); i != validPaths.end(); ++i) {
|
||||
foreach (PathSet::iterator, i, validPaths) {
|
||||
bool update = false;
|
||||
ValidPathInfo info = queryPathInfo(*i);
|
||||
ValidPathInfo info = queryPathInfo(*i, true);
|
||||
|
||||
/* Check the references: each reference should be valid, and
|
||||
it should have a matching referrer. */
|
||||
for (PathSet::iterator j = info.references.begin();
|
||||
j != info.references.end(); ++j)
|
||||
{
|
||||
if (referrersCache.find(*j) == referrersCache.end())
|
||||
queryReferrers(*j, referrersCache[*j]);
|
||||
if (referrersCache[*j].find(*i) == referrersCache[*j].end()) {
|
||||
printMsg(lvlError, format("adding missing referrer mapping from `%1%' to `%2%'")
|
||||
% *j % *i);
|
||||
appendReferrer(*j, *i, true);
|
||||
}
|
||||
foreach (PathSet::iterator, j, info.references) {
|
||||
if (validPaths.find(*j) == validPaths.end()) {
|
||||
printMsg(lvlError, format("incomplete closure: `%1%' needs missing `%2%'")
|
||||
% *i % *j);
|
||||
/* nothing we can do about it... */
|
||||
} else {
|
||||
if (referrersCache.find(*j) == referrersCache.end())
|
||||
queryReferrers(*j, referrersCache[*j]);
|
||||
if (referrersCache[*j].find(*i) == referrersCache[*j].end()) {
|
||||
printMsg(lvlError, format("adding missing referrer mapping from `%1%' to `%2%'")
|
||||
% *j % *i);
|
||||
appendReferrer(*j, *i, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,7 +1051,11 @@ void LocalStore::verifyStore(bool checkContents)
|
||||
}
|
||||
|
||||
/* Check the content hash (optionally - slow). */
|
||||
if (checkContents) {
|
||||
if (info.hash.hashSize == 0) {
|
||||
printMsg(lvlError, format("re-hashing `%1%'") % *i);
|
||||
info.hash = hashPath(htSHA256, *i);
|
||||
update = true;
|
||||
} else if (checkContents) {
|
||||
debug(format("checking contents of `%1%'") % *i);
|
||||
Hash current = hashPath(info.hash.type, *i);
|
||||
if (current != info.hash) {
|
||||
@@ -1042,10 +1077,12 @@ void LocalStore::verifyStore(bool checkContents)
|
||||
std::map<Path, PathSet> referencesCache;
|
||||
|
||||
Strings entries = readDirectory(nixDBPath + "/referrer");
|
||||
for (Strings::iterator i = entries.begin(); i != entries.end(); ++i) {
|
||||
foreach (Strings::iterator, i, entries) {
|
||||
Path from = nixStore + "/" + *i;
|
||||
|
||||
if (validPaths.find(from) == validPaths.end()) {
|
||||
/* !!! This removes lock files as well. Need to check
|
||||
whether that's okay. */
|
||||
printMsg(lvlError, format("removing referrers file for invalid `%1%'") % from);
|
||||
Path p = referrersFileFor(from);
|
||||
if (unlink(p.c_str()) == -1)
|
||||
@@ -1064,7 +1101,7 @@ void LocalStore::verifyStore(bool checkContents)
|
||||
|
||||
/* Each referrer should have a matching reference. */
|
||||
PathSet referrersNew;
|
||||
for (PathSet::iterator j = referrers.begin(); j != referrers.end(); ++j) {
|
||||
foreach (PathSet::iterator, j, referrers) {
|
||||
if (referencesCache.find(*j) == referencesCache.end())
|
||||
queryReferences(*j, referencesCache[*j]);
|
||||
if (referencesCache[*j].find(from) == referencesCache[*j].end()) {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <ext/stdio_filebuf.h>
|
||||
|
||||
#include "store-api.hh"
|
||||
#include "util.hh"
|
||||
|
||||
@@ -36,15 +34,10 @@ struct OptimiseStats
|
||||
};
|
||||
|
||||
|
||||
typedef __gnu_cxx::stdio_filebuf<char> stdio_filebuf;
|
||||
|
||||
|
||||
struct RunningSubstituter
|
||||
{
|
||||
Pid pid;
|
||||
boost::shared_ptr<stdio_filebuf> toBuf, fromBuf;
|
||||
boost::shared_ptr<std::ostream> to;
|
||||
boost::shared_ptr<std::istream> from;
|
||||
AutoCloseFD to, from;
|
||||
};
|
||||
|
||||
|
||||
@@ -89,11 +82,18 @@ public:
|
||||
bool querySubstitutablePathInfo(const Path & substituter,
|
||||
const Path & path, SubstitutablePathInfo & info);
|
||||
|
||||
Path addToStore(const Path & srcPath, bool fixed = false,
|
||||
bool recursive = false, string hashAlgo = "",
|
||||
Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
|
||||
Path addTextToStore(const string & suffix, const string & s,
|
||||
/* Like addToStore(), but the contents of the path are contained
|
||||
in `dump', which is either a NAR serialisation (if recursive ==
|
||||
true) or simply the contents of a regular file (if recursive ==
|
||||
false). */
|
||||
Path addToStoreFromDump(const string & dump, const string & name,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256);
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references);
|
||||
|
||||
void exportPath(const Path & path, bool sign,
|
||||
@@ -137,6 +137,13 @@ public:
|
||||
|
||||
void registerValidPaths(const ValidPathInfos & infos);
|
||||
|
||||
/* Register that the build of a derivation with output `path' has
|
||||
failed. */
|
||||
void registerFailedPath(const Path & path);
|
||||
|
||||
/* Query whether `path' previously failed to build. */
|
||||
bool hasPathFailed(const Path & path);
|
||||
|
||||
private:
|
||||
|
||||
Path schemaPath;
|
||||
@@ -155,7 +162,7 @@ private:
|
||||
|
||||
void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false);
|
||||
|
||||
ValidPathInfo queryPathInfo(const Path & path);
|
||||
ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false);
|
||||
|
||||
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
|
||||
|
||||
@@ -183,8 +190,8 @@ void copyPath(const Path & src, const Path & dst);
|
||||
|
||||
/* "Fix", or canonicalise, the meta-data of the files in a store path
|
||||
after it has been built. In particular:
|
||||
- the last modification date on each file is set to 0 (i.e.,
|
||||
00:00:00 1/1/1970 UTC)
|
||||
- the last modification date on each file is set to 1 (i.e.,
|
||||
00:00:01 1/1/1970 UTC)
|
||||
- the permissions are set of 444 or 555 (i.e., read-only with or
|
||||
without execute permission; setuid bits etc. are cleared)
|
||||
- the owner and group are set to the Nix user and group, if we're
|
||||
|
||||
@@ -30,16 +30,14 @@ void computeFSClosure(const Path & storePath,
|
||||
else
|
||||
store->queryReferences(storePath, references);
|
||||
|
||||
for (PathSet::iterator i = references.begin();
|
||||
i != references.end(); ++i)
|
||||
foreach (PathSet::iterator, i, references)
|
||||
computeFSClosure(*i, paths, flipDirection);
|
||||
}
|
||||
|
||||
|
||||
Path findOutput(const Derivation & drv, string id)
|
||||
{
|
||||
for (DerivationOutputs::const_iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
foreach (DerivationOutputs::const_iterator, i, drv.outputs)
|
||||
if (i->first == id) return i->second.path;
|
||||
throw Error(format("derivation has no output `%1%'") % id);
|
||||
}
|
||||
@@ -67,20 +65,17 @@ void queryMissing(const PathSet & targets,
|
||||
Derivation drv = derivationFromPath(p);
|
||||
|
||||
bool mustBuild = false;
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
if (!store->isValidPath(i->second.path) && !store->hasSubstitutes(i->second.path))
|
||||
mustBuild = true;
|
||||
|
||||
if (mustBuild) {
|
||||
willBuild.insert(p);
|
||||
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
foreach (DerivationInputs::iterator, i, drv.inputDrvs)
|
||||
todo.insert(i->first);
|
||||
} else
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
todo.insert(i->second.path);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ static void hashAndLink(bool dryRun, HashToPath & hashToPath,
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
foreach (Strings::iterator, i, names)
|
||||
hashAndLink(dryRun, hashToPath, stats, path + "/" + *i);
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats)
|
||||
|
||||
PathSet paths = queryValidPaths();
|
||||
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
foreach (PathSet::iterator, i, paths) {
|
||||
addTempRoot(*i);
|
||||
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
|
||||
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
|
||||
|
||||
@@ -141,9 +141,9 @@ PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
|
||||
}
|
||||
|
||||
|
||||
void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
|
||||
bool PathLocks::lockPaths(const PathSet & _paths,
|
||||
const string & waitMsg, bool wait)
|
||||
{
|
||||
/* May be called only once! */
|
||||
assert(fds.empty());
|
||||
|
||||
/* Note that `fds' is built incrementally so that the destructor
|
||||
@@ -155,7 +155,7 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
|
||||
paths.sort();
|
||||
|
||||
/* Acquire the lock for each path. */
|
||||
for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
|
||||
foreach (Paths::iterator, i, paths) {
|
||||
checkInterrupt();
|
||||
Path path = *i;
|
||||
Path lockPath = path + ".lock";
|
||||
@@ -174,8 +174,15 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
|
||||
|
||||
/* Acquire an exclusive lock. */
|
||||
if (!lockFile(fd, ltWrite, false)) {
|
||||
if (waitMsg != "") printMsg(lvlError, waitMsg);
|
||||
lockFile(fd, ltWrite, true);
|
||||
if (wait) {
|
||||
if (waitMsg != "") printMsg(lvlError, waitMsg);
|
||||
lockFile(fd, ltWrite, true);
|
||||
} else {
|
||||
/* Failed to lock this path; release all other
|
||||
locks. */
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug(format("lock acquired on `%1%'") % lockPath);
|
||||
@@ -199,12 +206,20 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
|
||||
fds.push_back(FDPair(fd.borrow(), lockPath));
|
||||
lockedPaths.insert(lockPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PathLocks::~PathLocks()
|
||||
{
|
||||
for (list<FDPair>::iterator i = fds.begin(); i != fds.end(); i++) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PathLocks::unlock()
|
||||
{
|
||||
foreach (list<FDPair>::iterator, i, fds) {
|
||||
if (deletePaths) deleteLockFilePreClose(i->second, i->first);
|
||||
|
||||
lockedPaths.erase(i->second);
|
||||
@@ -216,6 +231,8 @@ PathLocks::~PathLocks()
|
||||
|
||||
debug(format("lock released on `%1%'") % i->second);
|
||||
}
|
||||
|
||||
fds.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,9 +33,11 @@ public:
|
||||
PathLocks();
|
||||
PathLocks(const PathSet & paths,
|
||||
const string & waitMsg = "");
|
||||
void lockPaths(const PathSet & _paths,
|
||||
const string & waitMsg = "");
|
||||
bool lockPaths(const PathSet & _paths,
|
||||
const string & waitMsg = "",
|
||||
bool wait = true);
|
||||
~PathLocks();
|
||||
void unlock();
|
||||
void setDeletion(bool deletePaths);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
#include "references.hh"
|
||||
#include "hash.hh"
|
||||
#include "util.hh"
|
||||
#include "archive.hh"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -20,8 +13,8 @@ namespace nix {
|
||||
static unsigned int refLength = 32; /* characters */
|
||||
|
||||
|
||||
static void search(size_t len, const unsigned char * s,
|
||||
StringSet & ids, StringSet & seen)
|
||||
static void search(const unsigned char * s, unsigned int len,
|
||||
StringSet & hashes, StringSet & seen)
|
||||
{
|
||||
static bool initialised = false;
|
||||
static bool isBase32[256];
|
||||
@@ -43,93 +36,60 @@ static void search(size_t len, const unsigned char * s,
|
||||
}
|
||||
if (!match) continue;
|
||||
string ref((const char *) s + i, refLength);
|
||||
if (ids.find(ref) != ids.end()) {
|
||||
if (hashes.find(ref) != hashes.end()) {
|
||||
debug(format("found reference to `%1%' at offset `%2%'")
|
||||
% ref % i);
|
||||
seen.insert(ref);
|
||||
ids.erase(ref);
|
||||
hashes.erase(ref);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void checkPath(const string & path,
|
||||
StringSet & ids, StringSet & seen)
|
||||
struct RefScanSink : Sink
|
||||
{
|
||||
checkInterrupt();
|
||||
HashSink hashSink;
|
||||
StringSet hashes;
|
||||
StringSet seen;
|
||||
|
||||
string tail;
|
||||
|
||||
RefScanSink() : hashSink(htSHA256) { }
|
||||
|
||||
debug(format("checking `%1%'") % path);
|
||||
void operator () (const unsigned char * data, unsigned int len);
|
||||
};
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); i++) {
|
||||
search(i->size(), (const unsigned char *) i->c_str(), ids, seen);
|
||||
checkPath(path + "/" + *i, ids, seen);
|
||||
}
|
||||
}
|
||||
void RefScanSink::operator () (const unsigned char * data, unsigned int len)
|
||||
{
|
||||
hashSink(data, len);
|
||||
|
||||
else if (S_ISREG(st.st_mode)) {
|
||||
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
|
||||
/* It's possible that a reference spans the previous and current
|
||||
fragment, so search in the concatenation of the tail of the
|
||||
previous fragment and the start of the current fragment. */
|
||||
string s = tail + string((const char *) data, len > refLength ? refLength : len);
|
||||
search((const unsigned char *) s.c_str(), s.size(), hashes, seen);
|
||||
|
||||
size_t bufSize = 1024 * 1024;
|
||||
assert(refLength <= bufSize);
|
||||
unsigned char * buf = new unsigned char[bufSize];
|
||||
search(data, len, hashes, seen);
|
||||
|
||||
size_t left = st.st_size;
|
||||
bool firstBlock = true;
|
||||
|
||||
while (left > 0) {
|
||||
checkInterrupt();
|
||||
|
||||
size_t read = left > bufSize ? bufSize : left;
|
||||
size_t copiedBytes = 0;
|
||||
|
||||
if (!firstBlock) {
|
||||
/* Move the last (refLength - 1) bytes from the last
|
||||
block to the start of the buffer to deal with
|
||||
references that cross block boundaries. */
|
||||
copiedBytes = refLength - 1;
|
||||
if (read + copiedBytes > bufSize)
|
||||
read -= copiedBytes;
|
||||
memcpy(buf, buf + (bufSize - copiedBytes), copiedBytes);
|
||||
}
|
||||
firstBlock = false;
|
||||
|
||||
readFull(fd, buf + copiedBytes, read);
|
||||
left -= read;
|
||||
|
||||
search(copiedBytes + read, buf, ids, seen);
|
||||
}
|
||||
|
||||
delete[] buf; /* !!! autodelete */
|
||||
}
|
||||
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
string target = readLink(path);
|
||||
search(target.size(), (const unsigned char *) target.c_str(), ids, seen);
|
||||
}
|
||||
|
||||
else throw Error(format("unknown file type: %1%") % path);
|
||||
unsigned int tailLen = len <= refLength ? len : refLength;
|
||||
tail =
|
||||
string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen)) +
|
||||
string((const char *) data + len - tailLen, tailLen);
|
||||
}
|
||||
|
||||
|
||||
PathSet scanForReferences(const string & path, const PathSet & paths)
|
||||
PathSet scanForReferences(const string & path,
|
||||
const PathSet & refs, Hash & hash)
|
||||
{
|
||||
RefScanSink sink;
|
||||
std::map<string, Path> backMap;
|
||||
StringSet ids;
|
||||
StringSet seen;
|
||||
|
||||
/* For efficiency (and a higher hit rate), just search for the
|
||||
hash part of the file name. (This assumes that all references
|
||||
have the form `HASH-bla'). */
|
||||
for (PathSet::const_iterator i = paths.begin(); i != paths.end(); i++) {
|
||||
foreach (PathSet::const_iterator, i, refs) {
|
||||
string baseName = baseNameOf(*i);
|
||||
string::size_type pos = baseName.find('-');
|
||||
if (pos == string::npos)
|
||||
@@ -138,21 +98,25 @@ PathSet scanForReferences(const string & path, const PathSet & paths)
|
||||
assert(s.size() == refLength);
|
||||
assert(backMap.find(s) == backMap.end());
|
||||
// parseHash(htSHA256, s);
|
||||
ids.insert(s);
|
||||
sink.hashes.insert(s);
|
||||
backMap[s] = *i;
|
||||
}
|
||||
|
||||
checkPath(path, ids, seen);
|
||||
/* Look for the hashes in the NAR dump of the path. */
|
||||
dumpPath(path, sink);
|
||||
|
||||
/* Map the hashes found back to their store paths. */
|
||||
PathSet found;
|
||||
for (StringSet::iterator i = seen.begin(); i != seen.end(); i++) {
|
||||
foreach (StringSet::iterator, i, sink.seen) {
|
||||
std::map<string, Path>::iterator j;
|
||||
if ((j = backMap.find(*i)) == backMap.end()) abort();
|
||||
found.insert(j->second);
|
||||
}
|
||||
|
||||
hash = sink.hashSink.finish();
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
#define __REFERENCES_H
|
||||
|
||||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
PathSet scanForReferences(const Path & path, const PathSet & refs);
|
||||
PathSet scanForReferences(const Path & path, const PathSet & refs,
|
||||
Hash & hash);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,22 @@ Path readStorePath(Source & from)
|
||||
PathSet readStorePaths(Source & from)
|
||||
{
|
||||
PathSet paths = readStringSet(from);
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
|
||||
assertStorePath(*i);
|
||||
foreach (PathSet::iterator, i, paths) assertStorePath(*i);
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
RemoteStore::RemoteStore()
|
||||
{
|
||||
initialised = false;
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::openConnection()
|
||||
{
|
||||
if (initialised) return;
|
||||
initialised = true;
|
||||
|
||||
string remoteMode = getEnv("NIX_REMOTE");
|
||||
|
||||
if (remoteMode == "slave")
|
||||
@@ -64,8 +72,8 @@ RemoteStore::RemoteStore()
|
||||
throw Error("Nix daemon protocol version not supported");
|
||||
writeInt(PROTOCOL_VERSION, to);
|
||||
processStderr();
|
||||
|
||||
} catch (Error & e) {
|
||||
}
|
||||
catch (Error & e) {
|
||||
throw Error(format("cannot start worker (%1%)")
|
||||
% e.msg());
|
||||
}
|
||||
@@ -194,6 +202,7 @@ void RemoteStore::setOptions()
|
||||
|
||||
bool RemoteStore::isValidPath(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopIsValidPath, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -204,12 +213,14 @@ bool RemoteStore::isValidPath(const Path & path)
|
||||
|
||||
PathSet RemoteStore::queryValidPaths()
|
||||
{
|
||||
openConnection();
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
bool RemoteStore::hasSubstitutes(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopHasSubstitutes, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -221,6 +232,7 @@ bool RemoteStore::hasSubstitutes(const Path & path)
|
||||
bool RemoteStore::querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info)
|
||||
{
|
||||
openConnection();
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return false;
|
||||
writeInt(wopQuerySubstitutablePathInfo, to);
|
||||
writeString(path, to);
|
||||
@@ -237,6 +249,7 @@ bool RemoteStore::querySubstitutablePathInfo(const Path & path,
|
||||
|
||||
Hash RemoteStore::queryPathHash(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryPathHash, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -248,6 +261,7 @@ Hash RemoteStore::queryPathHash(const Path & path)
|
||||
void RemoteStore::queryReferences(const Path & path,
|
||||
PathSet & references)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryReferences, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -259,6 +273,7 @@ void RemoteStore::queryReferences(const Path & path,
|
||||
void RemoteStore::queryReferrers(const Path & path,
|
||||
PathSet & referrers)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryReferrers, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -269,6 +284,7 @@ void RemoteStore::queryReferrers(const Path & path,
|
||||
|
||||
Path RemoteStore::queryDeriver(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryDeriver, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -278,27 +294,31 @@ Path RemoteStore::queryDeriver(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
Path RemoteStore::addToStore(const Path & _srcPath, bool fixed,
|
||||
bool recursive, string hashAlgo, PathFilter & filter)
|
||||
Path RemoteStore::addToStore(const Path & _srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter)
|
||||
{
|
||||
openConnection();
|
||||
|
||||
Path srcPath(absPath(_srcPath));
|
||||
|
||||
writeInt(wopAddToStore, to);
|
||||
writeString(baseNameOf(srcPath), to);
|
||||
writeInt(fixed ? 1 : 0, to);
|
||||
/* backwards compatibility hack */
|
||||
writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
|
||||
writeInt(recursive ? 1 : 0, to);
|
||||
writeString(hashAlgo, to);
|
||||
writeString(printHashType(hashAlgo), to);
|
||||
dumpPath(srcPath, to, filter);
|
||||
processStderr();
|
||||
return readStorePath(from);
|
||||
}
|
||||
|
||||
|
||||
Path RemoteStore::addTextToStore(const string & suffix, const string & s,
|
||||
Path RemoteStore::addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopAddTextToStore, to);
|
||||
writeString(suffix, to);
|
||||
writeString(name, to);
|
||||
writeString(s, to);
|
||||
writeStringSet(references, to);
|
||||
|
||||
@@ -310,6 +330,7 @@ Path RemoteStore::addTextToStore(const string & suffix, const string & s,
|
||||
void RemoteStore::exportPath(const Path & path, bool sign,
|
||||
Sink & sink)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopExportPath, to);
|
||||
writeString(path, to);
|
||||
writeInt(sign ? 1 : 0, to);
|
||||
@@ -320,10 +341,10 @@ void RemoteStore::exportPath(const Path & path, bool sign,
|
||||
|
||||
Path RemoteStore::importPath(bool requireSignature, Source & source)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopImportPath, to);
|
||||
/* We ignore requireSignature, since the worker forces it to true
|
||||
anyway. */
|
||||
|
||||
anyway. */
|
||||
processStderr(0, &source);
|
||||
return readStorePath(from);
|
||||
}
|
||||
@@ -331,6 +352,7 @@ Path RemoteStore::importPath(bool requireSignature, Source & source)
|
||||
|
||||
void RemoteStore::buildDerivations(const PathSet & drvPaths)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopBuildDerivations, to);
|
||||
writeStringSet(drvPaths, to);
|
||||
processStderr();
|
||||
@@ -340,6 +362,7 @@ void RemoteStore::buildDerivations(const PathSet & drvPaths)
|
||||
|
||||
void RemoteStore::ensurePath(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopEnsurePath, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -349,6 +372,7 @@ void RemoteStore::ensurePath(const Path & path)
|
||||
|
||||
void RemoteStore::addTempRoot(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopAddTempRoot, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -358,6 +382,7 @@ void RemoteStore::addTempRoot(const Path & path)
|
||||
|
||||
void RemoteStore::addIndirectRoot(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopAddIndirectRoot, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
@@ -367,6 +392,7 @@ void RemoteStore::addIndirectRoot(const Path & path)
|
||||
|
||||
void RemoteStore::syncWithGC()
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopSyncWithGC, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
@@ -375,6 +401,7 @@ void RemoteStore::syncWithGC()
|
||||
|
||||
Roots RemoteStore::findRoots()
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopFindRoots, to);
|
||||
processStderr();
|
||||
unsigned int count = readInt(from);
|
||||
@@ -390,12 +417,18 @@ Roots RemoteStore::findRoots()
|
||||
|
||||
void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
{
|
||||
openConnection();
|
||||
|
||||
writeInt(wopCollectGarbage, to);
|
||||
writeInt(options.action, to);
|
||||
writeStringSet(options.pathsToDelete, to);
|
||||
writeInt(options.ignoreLiveness, to);
|
||||
writeLongLong(options.maxFreed, to);
|
||||
writeInt(options.maxLinks, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) {
|
||||
writeInt(options.useAtime, to);
|
||||
writeInt(options.maxAtime, to);
|
||||
}
|
||||
|
||||
processStderr();
|
||||
|
||||
|
||||
@@ -42,11 +42,11 @@ public:
|
||||
bool querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info);
|
||||
|
||||
Path addToStore(const Path & srcPath, bool fixed = false,
|
||||
bool recursive = false, string hashAlgo = "",
|
||||
Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
|
||||
Path addTextToStore(const string & suffix, const string & s,
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references);
|
||||
|
||||
void exportPath(const Path & path, bool sign,
|
||||
@@ -74,6 +74,9 @@ private:
|
||||
FdSource from;
|
||||
Pid child;
|
||||
unsigned int daemonVersion;
|
||||
bool initialised;
|
||||
|
||||
void openConnection();
|
||||
|
||||
void processStderr(Sink * sink = 0, Source * source = 0);
|
||||
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
/* Needed for some ancient environments. */
|
||||
#ifndef ULLONG_MAX
|
||||
#define ULLONG_MAX 18446744073709551615
|
||||
#endif
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -18,7 +12,7 @@ GCOptions::GCOptions()
|
||||
{
|
||||
action = gcDeleteDead;
|
||||
ignoreLiveness = false;
|
||||
maxFreed = ULLONG_MAX;
|
||||
maxFreed = 0;
|
||||
maxLinks = 0;
|
||||
useAtime = false;
|
||||
maxAtime = (time_t) -1;
|
||||
@@ -87,7 +81,7 @@ void checkStoreName(const string & name)
|
||||
reasons (e.g., "." and ".."). */
|
||||
if (string(name, 0, 1) == ".")
|
||||
throw Error(format("illegal name: `%1%'") % name);
|
||||
for (string::const_iterator i = name.begin(); i != name.end(); ++i)
|
||||
foreach (string::const_iterator, i, name)
|
||||
if (!((*i >= 'A' && *i <= 'Z') ||
|
||||
(*i >= 'a' && *i <= 'z') ||
|
||||
(*i >= '0' && *i <= '9') ||
|
||||
@@ -99,55 +93,113 @@ void checkStoreName(const string & name)
|
||||
}
|
||||
|
||||
|
||||
/* Store paths have the following form:
|
||||
|
||||
<store>/<h>-<name>
|
||||
|
||||
where
|
||||
|
||||
<store> = the location of the Nix store, usually /nix/store
|
||||
|
||||
<name> = a human readable name for the path, typically obtained
|
||||
from the name attribute of the derivation, or the name of the
|
||||
source file from which the store path is created
|
||||
|
||||
<h> = base-32 representation of the first 160 bits of a SHA-256
|
||||
hash of <s>; the hash part of the store name
|
||||
|
||||
<s> = the string "<type>:sha256:<h2>:<store>:<name>";
|
||||
note that it includes the location of the store as well as the
|
||||
name to make sure that changes to either of those are reflected
|
||||
in the hash (e.g. you won't get /nix/store/<h>-name1 and
|
||||
/nix/store/<h>-name2 with equal hash parts).
|
||||
|
||||
<type> = one of:
|
||||
"text:<r1>:<r2>:...<rN>"
|
||||
for plain text files written to the store using
|
||||
addTextToStore(); <r1> ... <rN> are the references of the
|
||||
path.
|
||||
"source"
|
||||
for paths copied to the store using addToStore() when recursive
|
||||
= true and hashAlgo = "sha256"
|
||||
"output:out"
|
||||
for either the outputs created by derivations, OR paths copied
|
||||
to the store using addToStore() with recursive != true or
|
||||
hashAlgo != "sha256" (in that case "source" is used; it's
|
||||
silly, but it's done that way for compatibility).
|
||||
|
||||
<h2> = base-16 representation of a SHA-256 hash of:
|
||||
if <type> = "text:...":
|
||||
the string written to the resulting store path
|
||||
if <type> = "source":
|
||||
the serialisation of the path from which this store path is
|
||||
copied, as returned by hashPath()
|
||||
if <type> = "output:out":
|
||||
for non-fixed derivation outputs:
|
||||
the derivation (see hashDerivationModulo() in
|
||||
primops.cc)
|
||||
for paths copied by addToStore() or produced by fixed-output
|
||||
derivations:
|
||||
the string "fixed:out:<rec><algo>:<hash>:", where
|
||||
<rec> = "r:" for recursive (path) hashes, or "" or flat
|
||||
(file) hashes
|
||||
<algo> = "md5", "sha1" or "sha256"
|
||||
<hash> = base-16 representation of the path or flat hash of
|
||||
the contents of the path (or expected contents of the
|
||||
path for fixed-output derivations)
|
||||
|
||||
It would have been nicer to handle fixed-output derivations under
|
||||
"source", e.g. have something like "source:<rec><algo>", but we're
|
||||
stuck with this for now...
|
||||
|
||||
The main reason for this way of computing names is to prevent name
|
||||
collisions (for security). For instance, it shouldn't be feasible
|
||||
to come up with a derivation whose output path collides with the
|
||||
path for a copied source. The former would have a <s> starting with
|
||||
"output:out:", while the latter would have a <2> starting with
|
||||
"source:".
|
||||
*/
|
||||
|
||||
|
||||
Path makeStorePath(const string & type,
|
||||
const Hash & hash, const string & suffix)
|
||||
const Hash & hash, const string & name)
|
||||
{
|
||||
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
|
||||
string s = type + ":sha256:" + printHash(hash) + ":"
|
||||
+ nixStore + ":" + suffix;
|
||||
+ nixStore + ":" + name;
|
||||
|
||||
checkStoreName(suffix);
|
||||
checkStoreName(name);
|
||||
|
||||
return nixStore + "/"
|
||||
+ printHash32(compressHash(hashString(htSHA256, s), 20))
|
||||
+ "-" + suffix;
|
||||
+ "-" + name;
|
||||
}
|
||||
|
||||
|
||||
Path makeFixedOutputPath(bool recursive,
|
||||
string hashAlgo, Hash hash, string name)
|
||||
HashType hashAlgo, Hash hash, string name)
|
||||
{
|
||||
/* !!! copy/paste from primops.cc */
|
||||
Hash h = hashString(htSHA256, "fixed:out:"
|
||||
+ (recursive ? (string) "r:" : "") + hashAlgo + ":"
|
||||
+ printHash(hash) + ":"
|
||||
+ "");
|
||||
return makeStorePath("output:out", h, name);
|
||||
return hashAlgo == htSHA256 && recursive
|
||||
? makeStorePath("source", hash, name)
|
||||
: makeStorePath("output:out", hashString(htSHA256,
|
||||
"fixed:out:" + (recursive ? (string) "r:" : "") +
|
||||
printHashType(hashAlgo) + ":" + printHash(hash) + ":"),
|
||||
name);
|
||||
}
|
||||
|
||||
|
||||
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
|
||||
bool fixed, bool recursive, string hashAlgo, PathFilter & filter)
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter)
|
||||
{
|
||||
Hash h = hashPath(htSHA256, srcPath, filter);
|
||||
|
||||
string baseName = baseNameOf(srcPath);
|
||||
|
||||
Path dstPath;
|
||||
|
||||
if (fixed) {
|
||||
HashType ht(parseHashType(hashAlgo));
|
||||
Hash h2 = recursive ? hashPath(ht, srcPath, filter) : hashFile(ht, srcPath);
|
||||
dstPath = makeFixedOutputPath(recursive, hashAlgo, h2, baseName);
|
||||
}
|
||||
|
||||
else dstPath = makeStorePath("source", h, baseName);
|
||||
|
||||
HashType ht(hashAlgo);
|
||||
Hash h = recursive ? hashPath(ht, srcPath, filter) : hashFile(ht, srcPath);
|
||||
string name = baseNameOf(srcPath);
|
||||
Path dstPath = makeFixedOutputPath(recursive, hashAlgo, h, name);
|
||||
return std::pair<Path, Hash>(dstPath, h);
|
||||
}
|
||||
|
||||
|
||||
Path computeStorePathForText(const string & suffix, const string & s,
|
||||
Path computeStorePathForText(const string & name, const string & s,
|
||||
const PathSet & references)
|
||||
{
|
||||
Hash hash = hashString(htSHA256, s);
|
||||
@@ -155,11 +207,11 @@ Path computeStorePathForText(const string & suffix, const string & s,
|
||||
hacky, but we can't put them in `s' since that would be
|
||||
ambiguous. */
|
||||
string type = "text";
|
||||
for (PathSet::const_iterator i = references.begin(); i != references.end(); ++i) {
|
||||
foreach (PathSet::const_iterator, i, references) {
|
||||
type += ":";
|
||||
type += *i;
|
||||
}
|
||||
return makeStorePath(type, hash, suffix);
|
||||
return makeStorePath(type, hash, name);
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +223,7 @@ string makeValidityRegistration(const PathSet & paths,
|
||||
{
|
||||
string s = "";
|
||||
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
foreach (PathSet::iterator, i, paths) {
|
||||
s += *i + "\n";
|
||||
|
||||
if (showHash)
|
||||
@@ -185,8 +237,7 @@ string makeValidityRegistration(const PathSet & paths,
|
||||
|
||||
s += (format("%1%\n") % references.size()).str();
|
||||
|
||||
for (PathSet::iterator j = references.begin();
|
||||
j != references.end(); ++j)
|
||||
foreach (PathSet::iterator, j, references)
|
||||
s += *j + "\n";
|
||||
}
|
||||
|
||||
@@ -220,9 +271,7 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
|
||||
string showPaths(const PathSet & paths)
|
||||
{
|
||||
string s;
|
||||
for (PathSet::const_iterator i = paths.begin();
|
||||
i != paths.end(); ++i)
|
||||
{
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
if (s.size() != 0) s += ", ";
|
||||
s += "`" + *i + "'";
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ struct GCOptions
|
||||
/* For `gcDeleteSpecific', the paths to delete. */
|
||||
PathSet pathsToDelete;
|
||||
|
||||
/* Stop after at least `maxFreed' bytes have been freed. */
|
||||
/* Stop after at least `maxFreed' bytes have been freed. 0 means
|
||||
no limit. */
|
||||
unsigned long long maxFreed;
|
||||
|
||||
/* Stop after the number of hard links to the Nix store directory
|
||||
@@ -173,13 +174,13 @@ public:
|
||||
derivation is pre-loaded into the Nix store. The function
|
||||
object `filter' can be used to exclude files (see
|
||||
libutil/archive.hh). */
|
||||
virtual Path addToStore(const Path & srcPath, bool fixed = false,
|
||||
bool recursive = false, string hashAlgo = "",
|
||||
virtual Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter) = 0;
|
||||
|
||||
/* Like addToStore, but the contents written to the output path is
|
||||
a regular file containing the given string. */
|
||||
virtual Path addTextToStore(const string & suffix, const string & s,
|
||||
virtual Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references) = 0;
|
||||
|
||||
/* Export a store path, that is, create a NAR dump of the store
|
||||
@@ -274,10 +275,10 @@ Path followLinksToStorePath(const Path & path);
|
||||
|
||||
/* Constructs a unique store path name. */
|
||||
Path makeStorePath(const string & type,
|
||||
const Hash & hash, const string & suffix);
|
||||
const Hash & hash, const string & name);
|
||||
|
||||
Path makeFixedOutputPath(bool recursive,
|
||||
string hashAlgo, Hash hash, string name);
|
||||
HashType hashAlgo, Hash hash, string name);
|
||||
|
||||
|
||||
/* This is the preparatory part of addToStore() and addToStoreFixed();
|
||||
@@ -285,7 +286,7 @@ Path makeFixedOutputPath(bool recursive,
|
||||
Returns the store path and the cryptographic hash of the
|
||||
contents of srcPath. */
|
||||
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
|
||||
bool fixed = false, bool recursive = false, string hashAlgo = "",
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
|
||||
/* Preparatory part of addTextToStore().
|
||||
@@ -302,7 +303,7 @@ std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
|
||||
simply yield a different store path, so other users wouldn't be
|
||||
affected), but it has some backwards compatibility issues (the
|
||||
hashing scheme changes), so I'm not doing that for now. */
|
||||
Path computeStorePathForText(const string & suffix, const string & s,
|
||||
Path computeStorePathForText(const string & name, const string & s,
|
||||
const PathSet & references);
|
||||
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ void LocalStore::upgradeStore12()
|
||||
Paths paths;
|
||||
nixDB.enumTable(noTxn, dbValidPaths, paths);
|
||||
|
||||
for (Paths::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
foreach (Paths::iterator, i, paths) {
|
||||
ValidPathInfo info;
|
||||
info.path = *i;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace nix {
|
||||
#define WORKER_MAGIC_1 0x6e697863
|
||||
#define WORKER_MAGIC_2 0x6478696f
|
||||
|
||||
#define PROTOCOL_VERSION 0x104
|
||||
#define PROTOCOL_VERSION 0x105
|
||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#define _XOPEN_SOURCE 600
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
@@ -11,6 +12,8 @@
|
||||
#include "archive.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -47,17 +50,17 @@ static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
|
||||
}
|
||||
|
||||
|
||||
static void dumpContents(const Path & path, unsigned int size,
|
||||
static void dumpContents(const Path & path, size_t size,
|
||||
Sink & sink)
|
||||
{
|
||||
writeString("contents", sink);
|
||||
writeInt(size, sink);
|
||||
writeLongLong(size, sink);
|
||||
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
|
||||
|
||||
unsigned char buf[65536];
|
||||
unsigned int left = size;
|
||||
size_t left = size;
|
||||
|
||||
while (left > 0) {
|
||||
size_t n = left > sizeof(buf) ? sizeof(buf) : left;
|
||||
@@ -85,7 +88,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
||||
writeString("executable", sink);
|
||||
writeString("", sink);
|
||||
}
|
||||
dumpContents(path, st.st_size, sink);
|
||||
dumpContents(path, (size_t) st.st_size, sink);
|
||||
}
|
||||
|
||||
else if (S_ISDIR(st.st_mode)) {
|
||||
@@ -101,7 +104,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
||||
writeString(readLink(path), sink);
|
||||
}
|
||||
|
||||
else throw Error("unknown file type: " + path);
|
||||
else throw Error(format("file `%1%' has an unknown type") % path);
|
||||
|
||||
writeString(")", sink);
|
||||
}
|
||||
@@ -114,9 +117,9 @@ void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
|
||||
}
|
||||
|
||||
|
||||
static Error badArchive(string s)
|
||||
static SerialisationError badArchive(string s)
|
||||
{
|
||||
return Error("bad archive: " + s);
|
||||
return SerialisationError("bad archive: " + s);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,10 +132,11 @@ static void skipGeneric(Source & source)
|
||||
}
|
||||
|
||||
|
||||
static void restore(const Path & path, Source & source);
|
||||
static void parse(ParseSink & sink, Source & source, const Path & path);
|
||||
|
||||
|
||||
static void restoreEntry(const Path & path, Source & source)
|
||||
|
||||
static void parseEntry(ParseSink & sink, Source & source, const Path & path)
|
||||
{
|
||||
string s, name;
|
||||
|
||||
@@ -150,7 +154,7 @@ static void restoreEntry(const Path & path, Source & source)
|
||||
name = readString(source);
|
||||
} else if (s == "node") {
|
||||
if (s == "") throw badArchive("entry name missing");
|
||||
restore(path + "/" + name, source);
|
||||
parse(sink, source, path + "/" + name);
|
||||
} else {
|
||||
throw badArchive("unknown field " + s);
|
||||
skipGeneric(source);
|
||||
@@ -159,26 +163,31 @@ static void restoreEntry(const Path & path, Source & source)
|
||||
}
|
||||
|
||||
|
||||
static void restoreContents(int fd, const Path & path, Source & source)
|
||||
static void parseContents(ParseSink & sink, Source & source, const Path & path)
|
||||
{
|
||||
unsigned int size = readInt(source);
|
||||
unsigned int left = size;
|
||||
unsigned long long size = readLongLong(source);
|
||||
|
||||
sink.preallocateContents(size);
|
||||
|
||||
unsigned long long left = size;
|
||||
unsigned char buf[65536];
|
||||
|
||||
while (left) {
|
||||
checkInterrupt();
|
||||
unsigned int n = sizeof(buf);
|
||||
if (n > left) n = left;
|
||||
if ((unsigned long long) n > left) n = left;
|
||||
source(buf, n);
|
||||
writeFull(fd, buf, n);
|
||||
sink.receiveContents(buf, n);
|
||||
left -= n;
|
||||
}
|
||||
|
||||
sink.finalizeContents(size);
|
||||
|
||||
readPadding(size, source);
|
||||
}
|
||||
|
||||
|
||||
static void restore(const Path & path, Source & source)
|
||||
static void parse(ParseSink & sink, Source & source, const Path & path)
|
||||
{
|
||||
string s;
|
||||
|
||||
@@ -186,7 +195,6 @@ static void restore(const Path & path, Source & source)
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
||||
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
|
||||
AutoCloseFD fd;
|
||||
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
@@ -204,15 +212,12 @@ static void restore(const Path & path, Source & source)
|
||||
|
||||
if (t == "regular") {
|
||||
type = tpRegular;
|
||||
fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
if (fd == -1)
|
||||
throw SysError("creating file " + path);
|
||||
sink.createRegularFile(path);
|
||||
}
|
||||
|
||||
else if (t == "directory") {
|
||||
sink.createDirectory(path);
|
||||
type = tpDirectory;
|
||||
if (mkdir(path.c_str(), 0777) == -1)
|
||||
throw SysError("creating directory " + path);
|
||||
}
|
||||
|
||||
else if (t == "symlink") {
|
||||
@@ -224,42 +229,109 @@ static void restore(const Path & path, Source & source)
|
||||
}
|
||||
|
||||
else if (s == "contents" && type == tpRegular) {
|
||||
restoreContents(fd, path, source);
|
||||
parseContents(sink, source, path);
|
||||
}
|
||||
|
||||
else if (s == "executable" && type == tpRegular) {
|
||||
readString(source);
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1)
|
||||
throw SysError("fstat");
|
||||
if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
|
||||
throw SysError("fchmod");
|
||||
sink.isExecutable();
|
||||
}
|
||||
|
||||
else if (s == "entry" && type == tpDirectory) {
|
||||
restoreEntry(path, source);
|
||||
parseEntry(sink, source, path);
|
||||
}
|
||||
|
||||
else if (s == "target" && type == tpSymlink) {
|
||||
string target = readString(source);
|
||||
if (symlink(target.c_str(), path.c_str()) == -1)
|
||||
throw SysError("creating symlink " + path);
|
||||
sink.createSymlink(path, target);
|
||||
}
|
||||
|
||||
else {
|
||||
throw badArchive("unknown field " + s);
|
||||
skipGeneric(source);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void parseDump(ParseSink & sink, Source & source)
|
||||
{
|
||||
string version;
|
||||
try {
|
||||
version = readString(source);
|
||||
} catch (SerialisationError & e) {
|
||||
/* This generally means the integer at the start couldn't be
|
||||
decoded. Ignore and throw the exception below. */
|
||||
}
|
||||
if (version != archiveVersion1)
|
||||
throw badArchive("input doesn't look like a Nix archive");
|
||||
parse(sink, source, "");
|
||||
}
|
||||
|
||||
|
||||
struct RestoreSink : ParseSink
|
||||
{
|
||||
Path dstPath;
|
||||
AutoCloseFD fd;
|
||||
|
||||
void createDirectory(const Path & path)
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
if (mkdir(p.c_str(), 0777) == -1)
|
||||
throw SysError(format("creating directory `%1%'") % p);
|
||||
};
|
||||
|
||||
void createRegularFile(const Path & path)
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
if (fd == -1) throw SysError(format("creating file `%1%'") % p);
|
||||
}
|
||||
|
||||
void isExecutable()
|
||||
{
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1)
|
||||
throw SysError("fstat");
|
||||
if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
|
||||
throw SysError("fchmod");
|
||||
}
|
||||
|
||||
void preallocateContents(unsigned long long len)
|
||||
{
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
if (len) {
|
||||
errno = posix_fallocate(fd, 0, len);
|
||||
if (errno) throw SysError(format("preallocating file of %1% bytes") % len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void receiveContents(unsigned char * data, unsigned int len)
|
||||
{
|
||||
writeFull(fd, data, len);
|
||||
}
|
||||
|
||||
void finalizeContents(unsigned long long size)
|
||||
{
|
||||
errno = ftruncate(fd, size);
|
||||
if (errno) throw SysError(format("truncating file to its allocated length of %1% bytes") % size);
|
||||
}
|
||||
|
||||
void createSymlink(const Path & path, const string & target)
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
if (symlink(target.c_str(), p.c_str()) == -1)
|
||||
throw SysError(format("creating symlink `%1%'") % p);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void restorePath(const Path & path, Source & source)
|
||||
{
|
||||
if (readString(source) != archiveVersion1)
|
||||
throw badArchive("expected Nix archive");
|
||||
restore(path, source);
|
||||
RestoreSink sink;
|
||||
sink.dstPath = path;
|
||||
parseDump(sink, source);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,21 @@ extern PathFilter defaultPathFilter;
|
||||
void dumpPath(const Path & path, Sink & sink,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
|
||||
struct ParseSink
|
||||
{
|
||||
virtual void createDirectory(const Path & path) { };
|
||||
|
||||
virtual void createRegularFile(const Path & path) { };
|
||||
virtual void isExecutable() { };
|
||||
virtual void preallocateContents(unsigned long long size) { };
|
||||
virtual void receiveContents(unsigned char * data, unsigned int len) { };
|
||||
virtual void finalizeContents(unsigned long long size) { };
|
||||
|
||||
virtual void createSymlink(const Path & path, const string & target) { };
|
||||
};
|
||||
|
||||
void parseDump(ParseSink & sink, Source & source);
|
||||
|
||||
void restorePath(const Path & path, Source & source);
|
||||
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ void ATermMap::remove(ATerm key)
|
||||
}
|
||||
|
||||
|
||||
unsigned int ATermMap::size()
|
||||
unsigned int ATermMap::size() const
|
||||
{
|
||||
return count; /* STL nomenclature */
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
void remove(ATerm key);
|
||||
|
||||
unsigned int size();
|
||||
unsigned int size() const;
|
||||
|
||||
struct const_iterator
|
||||
{
|
||||
|
||||
@@ -335,4 +335,13 @@ HashType parseHashType(const string & s)
|
||||
}
|
||||
|
||||
|
||||
string printHashType(HashType ht)
|
||||
{
|
||||
if (ht == htMD5) return "md5";
|
||||
else if (ht == htSHA1) return "sha1";
|
||||
else if (ht == htSHA256) return "sha256";
|
||||
else throw Error("cannot print unknown hash type");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ Hash compressHash(const Hash & hash, unsigned int newSize);
|
||||
/* Parse a string representing a hash type. */
|
||||
HashType parseHashType(const string & s);
|
||||
|
||||
/* And the reverse. */
|
||||
string printHashType(HashType ht);
|
||||
|
||||
|
||||
union Ctx;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "serialise.hh"
|
||||
#include "util.hh"
|
||||
#include "aterm.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -73,6 +73,67 @@ void writeStringSet(const StringSet & ss, Sink & sink)
|
||||
}
|
||||
|
||||
|
||||
void writeATerm(ATerm t, Sink & sink)
|
||||
{
|
||||
int len;
|
||||
unsigned char *buf = (unsigned char *) ATwriteToBinaryString(t, &len);
|
||||
AutoDeleteArray<unsigned char> d(buf);
|
||||
writeInt(len, sink);
|
||||
sink(buf, len);
|
||||
}
|
||||
|
||||
|
||||
/* convert the ATermMap to a list of couple because many terms are shared
|
||||
between the keys and between the values. Thus the BAF stored by
|
||||
writeATerm consume less memory space. The list of couples is saved
|
||||
inside a tree structure of /treedepth/ height because the serialiasation
|
||||
of ATerm cause a tail recurssion on list tails. */
|
||||
void writeATermMap(const ATermMap & tm, Sink & sink)
|
||||
{
|
||||
const unsigned int treedepth = 5;
|
||||
const unsigned int maxarity = 128; // 2 < maxarity < MAX_ARITY (= 255)
|
||||
const unsigned int bufsize = treedepth * maxarity;
|
||||
|
||||
AFun map = ATmakeAFun("map", 2, ATfalse);
|
||||
AFun node = ATmakeAFun("node", maxarity, ATfalse);
|
||||
ATerm empty = (ATerm) ATmakeAppl0(ATmakeAFun("empty", 0, ATfalse));
|
||||
|
||||
unsigned int c[treedepth];
|
||||
ATerm *buf = new ATerm[bufsize];
|
||||
AutoDeleteArray<ATerm> d(buf);
|
||||
|
||||
memset(buf, 0, bufsize * sizeof(ATerm));
|
||||
ATprotectArray(buf, bufsize);
|
||||
|
||||
for (unsigned int j = 0; j < treedepth; j++)
|
||||
c[j] = 0;
|
||||
|
||||
for (ATermMap::const_iterator i = tm.begin(); i != tm.end(); ++i) {
|
||||
unsigned int depth = treedepth - 1;
|
||||
ATerm term = (ATerm) ATmakeAppl2(map, i->key, i->value);
|
||||
buf[depth * maxarity + c[depth]++] = term;
|
||||
while (c[depth] % maxarity == 0) {
|
||||
c[depth] = 0;
|
||||
term = (ATerm) ATmakeApplArray(node, &buf[depth * maxarity]);
|
||||
depth--;
|
||||
buf[depth * maxarity + c[depth]++] = term;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int depth = treedepth;
|
||||
ATerm last_node = empty;
|
||||
while (depth--) {
|
||||
buf[depth * maxarity + c[depth]++] = last_node;
|
||||
while (c[depth] % maxarity)
|
||||
buf[depth * maxarity + c[depth]++] = empty;
|
||||
last_node = (ATerm) ATmakeApplArray(node, &buf[depth * maxarity]);
|
||||
}
|
||||
|
||||
writeATerm(last_node, sink);
|
||||
ATunprotectArray(buf);
|
||||
}
|
||||
|
||||
|
||||
void readPadding(unsigned int len, Source & source)
|
||||
{
|
||||
if (len % 8) {
|
||||
@@ -80,7 +141,7 @@ void readPadding(unsigned int len, Source & source)
|
||||
unsigned int n = 8 - (len % 8);
|
||||
source(zero, n);
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
if (zero[i]) throw Error("non-zero padding");
|
||||
if (zero[i]) throw SerialisationError("non-zero padding");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +151,7 @@ unsigned int readInt(Source & source)
|
||||
unsigned char buf[8];
|
||||
source(buf, sizeof(buf));
|
||||
if (buf[4] || buf[5] || buf[6] || buf[7])
|
||||
throw Error("implementation cannot deal with > 32-bit integers");
|
||||
throw SerialisationError("implementation cannot deal with > 32-bit integers");
|
||||
return
|
||||
buf[0] |
|
||||
(buf[1] << 8) |
|
||||
@@ -136,4 +197,48 @@ StringSet readStringSet(Source & source)
|
||||
}
|
||||
|
||||
|
||||
ATerm readATerm(Source & source)
|
||||
{
|
||||
unsigned int len = readInt(source);
|
||||
unsigned char * buf = new unsigned char[len];
|
||||
AutoDeleteArray<unsigned char> d(buf);
|
||||
source(buf, len);
|
||||
ATerm t = ATreadFromBinaryString((char *) buf, len);
|
||||
if (t == 0)
|
||||
throw SerialisationError("cannot read a valid ATerm.");
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
static void recursiveInitATermMap(ATermMap &tm, bool &stop, ATermAppl node)
|
||||
{
|
||||
const unsigned int arity = ATgetArity(ATgetAFun(node));
|
||||
ATerm key, value;
|
||||
|
||||
switch (arity) {
|
||||
case 0:
|
||||
stop = true;
|
||||
return;
|
||||
case 2:
|
||||
key = ATgetArgument(node, 0);
|
||||
value = ATgetArgument(node, 1);
|
||||
tm.set(key, value);
|
||||
return;
|
||||
default:
|
||||
for (unsigned int i = 0; i < arity && !stop; i++)
|
||||
recursiveInitATermMap(tm, stop, (ATermAppl) ATgetArgument(node, i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ATermMap readATermMap(Source & source)
|
||||
{
|
||||
ATermMap tm;
|
||||
bool stop = false;
|
||||
|
||||
recursiveInitATermMap(tm, stop, (ATermAppl) readATerm(source));
|
||||
return tm;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define __SERIALISE_H
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include "aterm-map.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -35,7 +35,7 @@ struct FdSink : Sink
|
||||
|
||||
FdSink()
|
||||
{
|
||||
fd = 0;
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
FdSink(int fd)
|
||||
@@ -54,7 +54,7 @@ struct FdSource : Source
|
||||
|
||||
FdSource()
|
||||
{
|
||||
fd = 0;
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
FdSource(int fd)
|
||||
@@ -80,9 +80,9 @@ struct StringSink : Sink
|
||||
/* A source that reads data from a string. */
|
||||
struct StringSource : Source
|
||||
{
|
||||
string & s;
|
||||
const string & s;
|
||||
unsigned int pos;
|
||||
StringSource(string & _s) : s(_s), pos(0) { }
|
||||
StringSource(const string & _s) : s(_s), pos(0) { }
|
||||
virtual void operator () (unsigned char * data, unsigned int len)
|
||||
{
|
||||
s.copy((char *) data, len, pos);
|
||||
@@ -98,12 +98,19 @@ void writeInt(unsigned int n, Sink & sink);
|
||||
void writeLongLong(unsigned long long n, Sink & sink);
|
||||
void writeString(const string & s, Sink & sink);
|
||||
void writeStringSet(const StringSet & ss, Sink & sink);
|
||||
void writeATerm(ATerm t, Sink & sink);
|
||||
void writeATermMap(const ATermMap & tm, Sink & sink);
|
||||
|
||||
void readPadding(unsigned int len, Source & source);
|
||||
unsigned int readInt(Source & source);
|
||||
unsigned long long readLongLong(Source & source);
|
||||
string readString(Source & source);
|
||||
StringSet readStringSet(Source & source);
|
||||
ATerm readATerm(Source & source);
|
||||
ATermMap readATermMap(Source & source);
|
||||
|
||||
|
||||
MakeError(SerialisationError, Error)
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -24,12 +24,14 @@ using boost::format;
|
||||
class BaseError : public std::exception
|
||||
{
|
||||
protected:
|
||||
string prefix_; // used for location traces etc.
|
||||
string err;
|
||||
public:
|
||||
BaseError(const format & f);
|
||||
~BaseError() throw () { };
|
||||
const char * what() const throw () { return err.c_str(); }
|
||||
const string & msg() const throw () { return err; }
|
||||
const string & prefix() const throw () { return prefix_; }
|
||||
BaseError & addPrefix(const format & f);
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ BaseError::BaseError(const format & f)
|
||||
|
||||
BaseError & BaseError::addPrefix(const format & f)
|
||||
{
|
||||
err = f.str() + err;
|
||||
prefix_ = f.str() + prefix_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -229,6 +229,33 @@ void writeFile(const Path & path, const string & s)
|
||||
}
|
||||
|
||||
|
||||
string readLine(int fd)
|
||||
{
|
||||
string s;
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
char ch;
|
||||
ssize_t rd = read(fd, &ch, 1);
|
||||
if (rd == -1) {
|
||||
if (errno != EINTR)
|
||||
throw SysError("reading a line");
|
||||
} else if (rd == 0)
|
||||
throw Error("unexpected EOF reading a line");
|
||||
else {
|
||||
if (ch == '\n') return s;
|
||||
s += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void writeLine(int fd, string s)
|
||||
{
|
||||
s += '\n';
|
||||
writeFull(fd, (const unsigned char *) s.c_str(), s.size());
|
||||
}
|
||||
|
||||
|
||||
static void _computePathSize(const Path & path,
|
||||
unsigned long long & bytes, unsigned long long & blocks)
|
||||
{
|
||||
@@ -469,11 +496,7 @@ void warnOnce(bool & haveWarned, const format & f)
|
||||
|
||||
static void defaultWriteToStderr(const unsigned char * buf, size_t count)
|
||||
{
|
||||
try {
|
||||
writeFull(STDERR_FILENO, buf, count);
|
||||
} catch (SysError & e) {
|
||||
/* ignore EPIPE etc. */
|
||||
}
|
||||
writeFull(STDERR_FILENO, buf, count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -60,6 +60,12 @@ string readFile(const Path & path);
|
||||
/* Write a string to a file. */
|
||||
void writeFile(const Path & path, const string & s);
|
||||
|
||||
/* Read a line from a file descriptor. */
|
||||
string readLine(int fd);
|
||||
|
||||
/* Write a line to a file descriptor. */
|
||||
void writeLine(int fd, string s);
|
||||
|
||||
/* Compute the sum of the sizes of all files in `path'. */
|
||||
void computePathSize(const Path & path,
|
||||
unsigned long long & bytes, unsigned long long & blocks);
|
||||
|
||||
@@ -251,15 +251,14 @@ static string optimisticLockProfile(const Path & profile)
|
||||
}
|
||||
|
||||
|
||||
static bool createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
static bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
const Path & profile, bool keepDerivations,
|
||||
const string & lockToken)
|
||||
{
|
||||
/* Build the components in the user environment, if they don't
|
||||
exist already. */
|
||||
PathSet drvsToBuild;
|
||||
for (DrvInfos::const_iterator i = elems.begin();
|
||||
i != elems.end(); ++i)
|
||||
foreach (DrvInfos::const_iterator, i, elems)
|
||||
/* Call to `isDerivation' is for compatibility with Nix <= 0.7
|
||||
user environments. */
|
||||
if (i->queryDrvPath(state) != "" &&
|
||||
@@ -277,19 +276,16 @@ static bool createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
PathSet references;
|
||||
ATermList manifest = ATempty;
|
||||
ATermList inputs = ATempty;
|
||||
for (DrvInfos::const_iterator i = elems.begin();
|
||||
i != elems.end(); ++i)
|
||||
{
|
||||
foreach (DrvInfos::iterator, i, elems) {
|
||||
/* Create a pseudo-derivation containing the name, system,
|
||||
output path, and optionally the derivation path, as well as
|
||||
the meta attributes. */
|
||||
Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
|
||||
|
||||
/* Round trip to get rid of "bad" meta values (like
|
||||
functions). */
|
||||
MetaInfo meta = i->queryMetaInfo(state);
|
||||
ATermList metaList = ATempty;
|
||||
foreach (MetaInfo::iterator, j, meta)
|
||||
metaList = ATinsert(metaList,
|
||||
makeBind(toATerm(j->first), makeStr(j->second), makeNoPos()));
|
||||
i->setMetaInfo(meta);
|
||||
|
||||
ATermList as = ATmakeList5(
|
||||
makeBind(toATerm("type"),
|
||||
@@ -300,7 +296,8 @@ static bool createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
makeStr(i->system), makeNoPos()),
|
||||
makeBind(toATerm("outPath"),
|
||||
makeStr(i->queryOutPath(state)), makeNoPos()),
|
||||
makeBind(toATerm("meta"), makeAttrs(metaList), makeNoPos()));
|
||||
makeBind(toATerm("meta"),
|
||||
i->attrs->get(toATerm("meta")), makeNoPos()));
|
||||
|
||||
if (drvPath != "") as = ATinsert(as,
|
||||
makeBind(toATerm("drvPath"),
|
||||
@@ -361,13 +358,23 @@ static bool createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
}
|
||||
|
||||
|
||||
static int getPriority(EvalState & state, const DrvInfo & drv)
|
||||
{
|
||||
MetaValue value = drv.queryMetaInfo(state, "priority");
|
||||
int prio = 0;
|
||||
if (value.type == MetaValue::tpInt) prio = value.intValue;
|
||||
else if (value.type == MetaValue::tpString)
|
||||
/* Backwards compatibility. Priorities used to be strings
|
||||
before we had support for integer meta field. */
|
||||
string2Int(value.stringValue, prio);
|
||||
return prio;
|
||||
}
|
||||
|
||||
|
||||
static int comparePriorities(EvalState & state,
|
||||
const DrvInfo & drv1, const DrvInfo & drv2)
|
||||
{
|
||||
int prio1, prio2;
|
||||
if (!string2Int(drv1.queryMetaInfo(state, "priority"), prio1)) prio1 = 0;
|
||||
if (!string2Int(drv2.queryMetaInfo(state, "priority"), prio2)) prio2 = 0;
|
||||
return prio2 - prio1; /* higher number = lower priority, so negate */
|
||||
return getPriority(state, drv2) - getPriority(state, drv1);
|
||||
}
|
||||
|
||||
|
||||
@@ -594,6 +601,13 @@ static void printMissing(EvalState & state, const DrvInfos & elems)
|
||||
}
|
||||
|
||||
|
||||
static bool keep(MetaInfo & meta)
|
||||
{
|
||||
MetaValue value = meta["keep"];
|
||||
return value.type == MetaValue::tpString && value.stringValue == "true";
|
||||
}
|
||||
|
||||
|
||||
static void installDerivations(Globals & globals,
|
||||
const Strings & args, const Path & profile)
|
||||
{
|
||||
@@ -628,7 +642,7 @@ static void installDerivations(Globals & globals,
|
||||
MetaInfo meta = i->queryMetaInfo(globals.state);
|
||||
if (!globals.preserveInstalled &&
|
||||
newNames.find(drvName.name) != newNames.end() &&
|
||||
meta["keep"] != "true")
|
||||
!keep(meta))
|
||||
printMsg(lvlInfo,
|
||||
format("replacing old `%1%'") % i->name);
|
||||
else
|
||||
@@ -691,7 +705,7 @@ static void upgradeDerivations(Globals & globals,
|
||||
DrvName drvName(i->name);
|
||||
|
||||
MetaInfo meta = i->queryMetaInfo(globals.state);
|
||||
if (meta["keep"] == "true") {
|
||||
if (keep(meta)) {
|
||||
newElems.push_back(*i);
|
||||
continue;
|
||||
}
|
||||
@@ -709,9 +723,9 @@ static void upgradeDerivations(Globals & globals,
|
||||
if (newName.name == drvName.name) {
|
||||
int d = comparePriorities(globals.state, *i, *j);
|
||||
if (d == 0) d = compareVersions(drvName.version, newName.version);
|
||||
if (upgradeType == utLt && d < 0 ||
|
||||
upgradeType == utLeq && d <= 0 ||
|
||||
upgradeType == utEq && d == 0 ||
|
||||
if ((upgradeType == utLt && d < 0) ||
|
||||
(upgradeType == utLeq && d <= 0) ||
|
||||
(upgradeType == utEq && d == 0) ||
|
||||
upgradeType == utAlways)
|
||||
{
|
||||
int d2 = -1;
|
||||
@@ -770,7 +784,10 @@ static void setMetaFlag(EvalState & state, DrvInfo & drv,
|
||||
const string & name, const string & value)
|
||||
{
|
||||
MetaInfo meta = drv.queryMetaInfo(state);
|
||||
meta[name] = value;
|
||||
MetaValue v;
|
||||
v.type = MetaValue::tpString;
|
||||
v.stringValue = value;
|
||||
meta[name] = v;
|
||||
drv.setMetaInfo(meta);
|
||||
}
|
||||
|
||||
@@ -913,7 +930,7 @@ void printTable(Table & table)
|
||||
vector<unsigned int> widths;
|
||||
widths.resize(nrColumns);
|
||||
|
||||
for (Table::iterator i = table.begin(); i != table.end(); ++i) {
|
||||
foreach (Table::iterator, i, table) {
|
||||
assert(i->size() == nrColumns);
|
||||
Strings::iterator j;
|
||||
unsigned int column;
|
||||
@@ -921,14 +938,15 @@ void printTable(Table & table)
|
||||
if (j->size() > widths[column]) widths[column] = j->size();
|
||||
}
|
||||
|
||||
for (Table::iterator i = table.begin(); i != table.end(); ++i) {
|
||||
foreach (Table::iterator, i, table) {
|
||||
Strings::iterator j;
|
||||
unsigned int column;
|
||||
for (j = i->begin(), column = 0; j != i->end(); ++j, ++column)
|
||||
{
|
||||
cout << *j;
|
||||
for (j = i->begin(), column = 0; j != i->end(); ++j, ++column) {
|
||||
string s = *j;
|
||||
replace(s.begin(), s.end(), '\n', ' ');
|
||||
cout << s;
|
||||
if (column < nrColumns - 1)
|
||||
cout << string(widths[column] - j->size() + 2, ' ');
|
||||
cout << string(widths[column] - s.size() + 2, ' ');
|
||||
}
|
||||
cout << std::endl;
|
||||
}
|
||||
@@ -1075,9 +1093,7 @@ static void opQuery(Globals & globals,
|
||||
XMLWriter xml(true, *(xmlOutput ? &cout : &dummy));
|
||||
XMLOpenElement xmlRoot(xml, "items");
|
||||
|
||||
for (vector<DrvInfo>::iterator i = elems2.begin();
|
||||
i != elems2.end(); ++i)
|
||||
{
|
||||
foreach (vector<DrvInfo>::iterator, i, elems2) {
|
||||
try {
|
||||
|
||||
/* For table output. */
|
||||
@@ -1164,7 +1180,8 @@ static void opQuery(Globals & globals,
|
||||
|
||||
if (printDescription) {
|
||||
MetaInfo meta = i->queryMetaInfo(globals.state);
|
||||
string descr = meta["description"];
|
||||
MetaValue value = meta["description"];
|
||||
string descr = value.type == MetaValue::tpString ? value.stringValue : "";
|
||||
if (xmlOutput) {
|
||||
if (descr != "") attrs["description"] = descr;
|
||||
} else
|
||||
@@ -1178,8 +1195,23 @@ static void opQuery(Globals & globals,
|
||||
for (MetaInfo::iterator j = meta.begin(); j != meta.end(); ++j) {
|
||||
XMLAttrs attrs2;
|
||||
attrs2["name"] = j->first;
|
||||
attrs2["value"] = j->second;
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
if (j->second.type == MetaValue::tpString) {
|
||||
attrs2["type"] = "string";
|
||||
attrs2["value"] = j->second.stringValue;
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (j->second.type == MetaValue::tpInt) {
|
||||
attrs2["type"] = "int";
|
||||
attrs2["value"] = (format("%1%") % j->second.intValue).str();
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (j->second.type == MetaValue::tpStrings) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
foreach (Strings::iterator, k, j->second.stringValues) {
|
||||
XMLAttrs attrs3;
|
||||
attrs3["value"] = *k;
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1230,12 +1262,13 @@ static void switchGeneration(Globals & globals, int dstGen)
|
||||
(dstGen >= 0 && i->number == dstGen))
|
||||
dst = *i;
|
||||
|
||||
if (!dst)
|
||||
if (!dst) {
|
||||
if (dstGen == prevGen)
|
||||
throw Error(format("no generation older than the current (%1%) exists")
|
||||
% curGen);
|
||||
else
|
||||
throw Error(format("generation %1% does not exist") % dstGen);
|
||||
}
|
||||
|
||||
printMsg(lvlInfo, format("switching from generation %1% to %2%")
|
||||
% curGen % dst.number);
|
||||
|
||||
@@ -34,6 +34,8 @@ struct Decoder
|
||||
void pushChar(char c);
|
||||
|
||||
void finishLine();
|
||||
|
||||
void decodeFile(istream & st);
|
||||
};
|
||||
|
||||
|
||||
@@ -122,6 +124,7 @@ void Decoder::finishLine()
|
||||
|
||||
if (line[i] == '<') cout << "<";
|
||||
else if (line[i] == '&') cout << "&";
|
||||
else if (line[i] == '\r') ; /* ignore carriage return */
|
||||
else if (line[i] < 32 && line[i] != 9) cout << "�";
|
||||
else if (i + sz + 33 < line.size() &&
|
||||
string(line, i, sz) == storeDir &&
|
||||
@@ -158,16 +161,26 @@ void Decoder::finishLine()
|
||||
}
|
||||
|
||||
|
||||
void Decoder::decodeFile(istream & st)
|
||||
{
|
||||
int c;
|
||||
|
||||
cout << "<logfile>" << endl;
|
||||
|
||||
while ((c = st.get()) != EOF) {
|
||||
pushChar(c);
|
||||
}
|
||||
|
||||
if (line.size()) finishLine();
|
||||
|
||||
while (level--) cout << "</nest>" << endl;
|
||||
|
||||
cout << "</logfile>" << endl;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
Decoder dec;
|
||||
int c;
|
||||
|
||||
cout << "<logfile>" << endl;
|
||||
|
||||
while ((c = getchar()) != EOF) {
|
||||
dec.pushChar(c);
|
||||
}
|
||||
|
||||
cout << "</logfile>" << endl;
|
||||
dec.decodeFile(cin);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
|
||||
if (isDerivation(*i)) drvPaths.insert(*i);
|
||||
store->buildDerivations(drvPaths);
|
||||
|
||||
foreach (Strings::iterator, i,opArgs)
|
||||
foreach (Strings::iterator, i, opArgs)
|
||||
cout << format("%1%\n") % realisePath(*i);
|
||||
}
|
||||
|
||||
@@ -125,11 +125,11 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
|
||||
if (opArgs.empty())
|
||||
throw UsageError("first argument must be hash algorithm");
|
||||
|
||||
string hashAlgo = opArgs.front();
|
||||
HashType hashAlgo = parseHashType(opArgs.front());
|
||||
opArgs.pop_front();
|
||||
|
||||
for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
|
||||
cout << format("%1%\n") % store->addToStore(*i, true, recursive, hashAlgo);
|
||||
cout << format("%1%\n") % store->addToStore(*i, recursive, hashAlgo);
|
||||
}
|
||||
|
||||
|
||||
@@ -151,14 +151,17 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs)
|
||||
if (*i == "--recursive") recursive = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
if (opArgs.size() != 3)
|
||||
throw UsageError(format("`--print-fixed-path' requires three arguments"));
|
||||
|
||||
Strings::iterator i = opArgs.begin();
|
||||
string hashAlgo = *i++;
|
||||
HashType hashAlgo = parseHashType(*i++);
|
||||
string hash = *i++;
|
||||
string name = *i++;
|
||||
|
||||
cout << format("%1%\n") %
|
||||
makeFixedOutputPath(recursive, hashAlgo,
|
||||
parseHash16or32(parseHashType(hashAlgo), hash), name);
|
||||
parseHash16or32(hashAlgo, hash), name);
|
||||
}
|
||||
|
||||
|
||||
@@ -528,7 +531,10 @@ static void opGC(Strings opFlags, Strings opArgs)
|
||||
else if (*i == "--print-live") options.action = GCOptions::gcReturnLive;
|
||||
else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead;
|
||||
else if (*i == "--delete") options.action = GCOptions::gcDeleteDead;
|
||||
else if (*i == "--max-freed") options.maxFreed = getIntArg(*i, i, opFlags.end());
|
||||
else if (*i == "--max-freed") {
|
||||
options.maxFreed = getIntArg(*i, i, opFlags.end());
|
||||
if (options.maxFreed == 0) options.maxFreed = 1;
|
||||
}
|
||||
else if (*i == "--max-links") options.maxLinks = getIntArg(*i, i, opFlags.end());
|
||||
else if (*i == "--use-atime") options.useAtime = true;
|
||||
else if (*i == "--max-atime") {
|
||||
@@ -614,16 +620,19 @@ static void opExport(Strings opFlags, Strings opArgs)
|
||||
static void opImport(Strings opFlags, Strings opArgs)
|
||||
{
|
||||
bool requireSignature = false;
|
||||
for (Strings::iterator i = opFlags.begin();
|
||||
i != opFlags.end(); ++i)
|
||||
foreach (Strings::iterator, i, opFlags)
|
||||
if (*i == "--require-signature") requireSignature = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
if (!opArgs.empty()) throw UsageError("no arguments expected");
|
||||
|
||||
FdSource source(STDIN_FILENO);
|
||||
while (readInt(source) == 1)
|
||||
while (true) {
|
||||
unsigned long long n = readLongLong(source);
|
||||
if (n == 0) break;
|
||||
if (n != 1) throw Error("input doesn't look like something created by `nix-store --export'");
|
||||
cout << format("%1%\n") % store->importPath(requireSignature, source) << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -112,12 +112,12 @@ static void sigPollHandler(int sigNo)
|
||||
_isInterrupted = 1;
|
||||
blockInt = 1;
|
||||
canSendStderr = false;
|
||||
string s = "SIGPOLL\n";
|
||||
write(STDERR_FILENO, s.c_str(), s.size());
|
||||
const char * s = "SIGPOLL\n";
|
||||
write(STDERR_FILENO, s, strlen(s));
|
||||
}
|
||||
} else {
|
||||
string s = "spurious SIGPOLL\n";
|
||||
write(STDERR_FILENO, s.c_str(), s.size());
|
||||
const char * s = "spurious SIGPOLL\n";
|
||||
write(STDERR_FILENO, s, strlen(s));
|
||||
}
|
||||
}
|
||||
catch (Error & e) {
|
||||
@@ -223,6 +223,43 @@ struct TunnelSource : Source
|
||||
};
|
||||
|
||||
|
||||
/* If the NAR archive contains a single file at top-level, then save
|
||||
the contents of the file to `s'. Otherwise barf. */
|
||||
struct RetrieveRegularNARSink : ParseSink
|
||||
{
|
||||
string s;
|
||||
|
||||
void createDirectory(const Path & path)
|
||||
{
|
||||
throw Error("regular file expected");
|
||||
}
|
||||
|
||||
void receiveContents(unsigned char * data, unsigned int len)
|
||||
{
|
||||
s.append((const char *) data, len);
|
||||
}
|
||||
|
||||
void createSymlink(const Path & path, const string & target)
|
||||
{
|
||||
throw Error("regular file expected");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Adapter class of a Source that saves all data read to `s'. */
|
||||
struct SavingSourceAdapter : Source
|
||||
{
|
||||
Source & orig;
|
||||
string s;
|
||||
SavingSourceAdapter(Source & orig) : orig(orig) { }
|
||||
void operator () (unsigned char * data, unsigned int len)
|
||||
{
|
||||
orig(data, len);
|
||||
s.append((const char *) data, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void performOp(unsigned int clientVersion,
|
||||
Source & from, Sink & to, unsigned int op)
|
||||
{
|
||||
@@ -288,19 +325,33 @@ static void performOp(unsigned int clientVersion,
|
||||
}
|
||||
|
||||
case wopAddToStore: {
|
||||
/* !!! uberquick hack */
|
||||
string baseName = readString(from);
|
||||
bool fixed = readInt(from) == 1;
|
||||
bool fixed = readInt(from) == 1; /* obsolete */
|
||||
bool recursive = readInt(from) == 1;
|
||||
string hashAlgo = readString(from);
|
||||
|
||||
Path tmp = createTempDir();
|
||||
AutoDelete delTmp(tmp);
|
||||
Path tmp2 = tmp + "/" + baseName;
|
||||
restorePath(tmp2, from);
|
||||
string s = readString(from);
|
||||
/* Compatibility hack. */
|
||||
if (!fixed) {
|
||||
s = "sha256";
|
||||
recursive = true;
|
||||
}
|
||||
HashType hashAlgo = parseHashType(s);
|
||||
|
||||
SavingSourceAdapter savedNAR(from);
|
||||
RetrieveRegularNARSink savedRegular;
|
||||
|
||||
if (recursive) {
|
||||
/* Get the entire NAR dump from the client and save it to
|
||||
a string so that we can pass it to
|
||||
addToStoreFromDump(). */
|
||||
ParseSink sink; /* null sink; just parse the NAR */
|
||||
parseDump(sink, savedNAR);
|
||||
} else {
|
||||
parseDump(savedRegular, from);
|
||||
}
|
||||
|
||||
startWork();
|
||||
Path path = store->addToStore(tmp2, fixed, recursive, hashAlgo);
|
||||
Path path = dynamic_cast<LocalStore *>(store.get())
|
||||
->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
|
||||
stopWork();
|
||||
|
||||
writeString(path, to);
|
||||
@@ -401,6 +452,10 @@ static void performOp(unsigned int clientVersion,
|
||||
options.ignoreLiveness = readInt(from);
|
||||
options.maxFreed = readLongLong(from);
|
||||
options.maxLinks = readInt(from);
|
||||
if (GET_PROTOCOL_MINOR(clientVersion) >= 5) {
|
||||
options.useAtime = readInt(from);
|
||||
options.maxAtime = readInt(from);
|
||||
}
|
||||
|
||||
GCResults results;
|
||||
|
||||
|
||||
@@ -1,46 +1,35 @@
|
||||
TESTS_ENVIRONMENT = $(SHELL) -e
|
||||
TESTS_ENVIRONMENT = $(bash) -e
|
||||
|
||||
extra1 = $(shell pwd)/test-tmp/shared
|
||||
|
||||
simple.sh substitutes.sh substitutes2.sh fallback.sh: simple.nix
|
||||
dependencies.sh gc.sh nix-push.sh nix-pull.in logging.sh nix-build.sh install-package.sh check-refs.sh: dependencies.nix
|
||||
locking.sh: locking.nix
|
||||
parallel.sh: parallel.nix
|
||||
build-hook.sh: build-hook.nix
|
||||
gc-concurrent.sh: gc-concurrent.nix gc-concurrent2.nix
|
||||
user-envs.sh: user-envs.nix
|
||||
fixed.sh: fixed.nix
|
||||
gc-runtime.sh: gc-runtime.nix
|
||||
check-refs.sh: check-refs.nix
|
||||
filter-source.sh: filter-source.nix
|
||||
|
||||
TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
||||
locking.sh parallel.sh build-hook.sh substitutes.sh substitutes2.sh \
|
||||
parallel.sh build-hook.sh substitutes.sh substitutes2.sh \
|
||||
fallback.sh nix-push.sh gc.sh gc-concurrent.sh verify.sh nix-pull.sh \
|
||||
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
|
||||
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
|
||||
remote-store.sh export.sh
|
||||
remote-store.sh export.sh export-graph.sh negative-caching.sh
|
||||
|
||||
XFAIL_TESTS =
|
||||
|
||||
include ../substitute.mk
|
||||
|
||||
$(TESTS): common.sh
|
||||
$(TESTS): common.sh config.nix
|
||||
|
||||
EXTRA_DIST = $(TESTS) \
|
||||
simple.nix.in simple.builder.sh \
|
||||
config.nix.in \
|
||||
simple.nix simple.builder.sh \
|
||||
hash-check.nix \
|
||||
dependencies.nix.in dependencies.builder*.sh \
|
||||
locking.nix.in locking.builder.sh \
|
||||
parallel.nix.in parallel.builder.sh \
|
||||
build-hook.nix.in build-hook.hook.sh \
|
||||
dependencies.nix dependencies.builder*.sh \
|
||||
parallel.nix parallel.builder.sh \
|
||||
build-hook.nix build-hook.hook.sh \
|
||||
substituter.sh substituter2.sh \
|
||||
gc-concurrent.nix.in gc-concurrent.builder.sh \
|
||||
gc-concurrent2.nix.in gc-concurrent2.builder.sh \
|
||||
user-envs.nix.in user-envs.builder.sh \
|
||||
fixed.nix.in fixed.builder1.sh fixed.builder2.sh \
|
||||
gc-runtime.nix.in \
|
||||
check-refs.nix.in \
|
||||
filter-source.nix.in \
|
||||
gc-concurrent.nix gc-concurrent.builder.sh gc-concurrent2.builder.sh \
|
||||
user-envs.nix user-envs.builder.sh \
|
||||
fixed.nix fixed.builder1.sh fixed.builder2.sh \
|
||||
gc-runtime.nix \
|
||||
check-refs.nix \
|
||||
filter-source.nix \
|
||||
export-graph.nix \
|
||||
negative-caching.nix \
|
||||
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) \
|
||||
common.sh.in
|
||||
|
||||
27
tests/add.sh
27
tests/add.sh
@@ -1,13 +1,28 @@
|
||||
source common.sh
|
||||
|
||||
file=./add.sh
|
||||
path1=$($nixstore --add ./dummy)
|
||||
echo $path1
|
||||
|
||||
path=$($nixstore --add $file)
|
||||
path2=$($nixstore --add-fixed sha256 --recursive ./dummy)
|
||||
echo $path2
|
||||
|
||||
echo $path
|
||||
if test "$path1" != "$path2"; then
|
||||
echo "nix-store --add and --add-fixed mismatch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
hash=$($nixstore -q --hash $path)
|
||||
path3=$($nixstore --add-fixed sha256 ./dummy)
|
||||
echo $path3
|
||||
test "$path1" != "$path3" || exit 1
|
||||
|
||||
echo $hash
|
||||
path4=$($nixstore --add-fixed sha1 --recursive ./dummy)
|
||||
echo $path4
|
||||
test "$path1" != "$path4" || exit 1
|
||||
|
||||
test "$hash" = "sha256:$($nixhash --type sha256 --base32 $file)"
|
||||
hash1=$($nixstore -q --hash $path1)
|
||||
echo $hash1
|
||||
|
||||
hash2=$($nixhash --type sha256 --base32 ./dummy)
|
||||
echo $hash2
|
||||
|
||||
test "$hash1" = "sha256:$hash2"
|
||||
|
||||
@@ -11,11 +11,11 @@ outPath=$(sed 's/Derive(\[("out",\"\([^\"]*\)\".*/\1/' $drv)
|
||||
echo "output path is $outPath" >&2
|
||||
|
||||
if $(echo $outPath | grep -q input-1); then
|
||||
echo "accept" >&3
|
||||
read x <&4
|
||||
echo "# accept" >&2
|
||||
read x
|
||||
echo "got $x"
|
||||
mkdir $outPath
|
||||
echo "BAR" > $outPath/foo
|
||||
else
|
||||
echo "decline" >&3
|
||||
echo "# decline" >&2
|
||||
fi
|
||||
|
||||
21
tests/build-hook.nix
Normal file
21
tests/build-hook.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
with import ./config.nix;
|
||||
|
||||
let
|
||||
|
||||
input1 = mkDerivation {
|
||||
name = "build-hook-input-1";
|
||||
builder = ./dependencies.builder1.sh;
|
||||
};
|
||||
|
||||
input2 = mkDerivation {
|
||||
name = "build-hook-input-2";
|
||||
builder = ./dependencies.builder2.sh;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
mkDerivation {
|
||||
name = "build-hook";
|
||||
builder = ./dependencies.builder0.sh;
|
||||
inherit input1 input2;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
let {
|
||||
|
||||
input1 = derivation {
|
||||
name = "build-hook-input-1";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder1.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
input2 = derivation {
|
||||
name = "build-hook-input-2";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder2.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
body = derivation {
|
||||
name = "build-hook";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder0.sh];
|
||||
PATH = "@testPath@";
|
||||
inherit input1 input2;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,11 +2,7 @@ source common.sh
|
||||
|
||||
export NIX_BUILD_HOOK="build-hook.hook.sh"
|
||||
|
||||
drvPath=$($nixinstantiate build-hook.nix)
|
||||
|
||||
echo "derivation is $drvPath"
|
||||
|
||||
outPath=$($nixstore -quf "$drvPath")
|
||||
outPath=$($nixbuild build-hook.nix)
|
||||
|
||||
echo "output path is $outPath"
|
||||
|
||||
|
||||
58
tests/check-refs.nix
Normal file
58
tests/check-refs.nix
Normal file
@@ -0,0 +1,58 @@
|
||||
with import ./config.nix;
|
||||
|
||||
rec {
|
||||
|
||||
dep = import ./dependencies.nix;
|
||||
|
||||
makeTest = nr: args: mkDerivation ({
|
||||
name = "check-refs-" + toString nr;
|
||||
} // args);
|
||||
|
||||
src = builtins.toFile "aux-ref" "bla bla";
|
||||
|
||||
test1 = makeTest 1 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test2 = makeTest 2 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link";
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test3 = makeTest 3 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
|
||||
allowedReferences = [];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test4 = makeTest 4 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
|
||||
allowedReferences = [dep];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test5 = makeTest 5 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out";
|
||||
allowedReferences = [];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test6 = makeTest 6 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
|
||||
allowedReferences = [];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test7 = makeTest 7 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
|
||||
allowedReferences = ["out"];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test8 = makeTest 8 {
|
||||
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link";
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
rec {
|
||||
|
||||
dep = import ./dependencies.nix;
|
||||
|
||||
makeTest = nr: args: derivation ({
|
||||
name = "check-refs-" + toString nr;
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
PATH = "@testPath@";
|
||||
} // args);
|
||||
|
||||
src = builtins.toFile "aux-ref" "bla bla";
|
||||
|
||||
test1 = makeTest 1 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link")];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test2 = makeTest 2 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link")];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test3 = makeTest 3 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link")];
|
||||
allowedReferences = [];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test4 = makeTest 4 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link")];
|
||||
allowedReferences = [dep];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test5 = makeTest 5 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out")];
|
||||
allowedReferences = [];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test6 = makeTest 6 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link")];
|
||||
allowedReferences = [];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test7 = makeTest 7 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link")];
|
||||
allowedReferences = ["out"];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
test8 = makeTest 8 {
|
||||
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link")];
|
||||
inherit dep;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -39,6 +39,11 @@ export dot=@dot@
|
||||
export xmllint="@xmllint@"
|
||||
export xmlflags="@xmlflags@"
|
||||
export xsltproc="@xsltproc@"
|
||||
export SHELL="@shell@"
|
||||
|
||||
# Hack to get "atdiff" to run on Cygwin (Windows looks for
|
||||
# DLLs in $PATH).
|
||||
export PATH=$aterm_bin/../lib:$PATH
|
||||
|
||||
export version=@version@
|
||||
export system=@system@
|
||||
@@ -54,6 +59,11 @@ readLink() {
|
||||
ls -l "$1" | sed 's/.*->\ //'
|
||||
}
|
||||
|
||||
clearProfiles() {
|
||||
profiles="$NIX_STATE_DIR"/profiles
|
||||
rm -f $profiles/*
|
||||
}
|
||||
|
||||
clearStore() {
|
||||
echo "clearing store..."
|
||||
chmod -R +w "$NIX_STORE_DIR"
|
||||
@@ -62,13 +72,16 @@ clearStore() {
|
||||
rm -rf "$NIX_DB_DIR"
|
||||
mkdir "$NIX_DB_DIR"
|
||||
$nixstore --init
|
||||
}
|
||||
|
||||
clearProfiles() {
|
||||
profiles="$NIX_STATE_DIR"/profiles
|
||||
rm -f $profiles/*
|
||||
clearProfiles
|
||||
rm -f "$NIX_STATE_DIR"/gcroots/auto/*
|
||||
rm -f "$NIX_STATE_DIR"/gcroots/ref
|
||||
}
|
||||
|
||||
clearManifests() {
|
||||
rm -f $NIX_STATE_DIR/manifests/*
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
17
tests/config.nix.in
Normal file
17
tests/config.nix.in
Normal file
@@ -0,0 +1,17 @@
|
||||
rec {
|
||||
shell = "@shell@";
|
||||
|
||||
path = "@testPath@";
|
||||
|
||||
system = "@system@";
|
||||
|
||||
shared = "@extra1@";
|
||||
|
||||
mkDerivation = args:
|
||||
derivation ({
|
||||
inherit system;
|
||||
builder = shell;
|
||||
args = ["-e" args.builder];
|
||||
PATH = path;
|
||||
} // removeAttrs args ["builder"]);
|
||||
}
|
||||
@@ -5,3 +5,5 @@ ln -s $input2 $out/input-2
|
||||
|
||||
# Self-reference.
|
||||
ln -s $out $out/self
|
||||
|
||||
echo FOO
|
||||
|
||||
22
tests/dependencies.nix
Normal file
22
tests/dependencies.nix
Normal file
@@ -0,0 +1,22 @@
|
||||
with import ./config.nix;
|
||||
|
||||
let {
|
||||
|
||||
input1 = mkDerivation {
|
||||
name = "dependencies-input-1";
|
||||
builder = ./dependencies.builder1.sh;
|
||||
};
|
||||
|
||||
input2 = mkDerivation {
|
||||
name = "dependencies-input-2";
|
||||
builder = ./. ~ "dependencies.builder2.sh";
|
||||
};
|
||||
|
||||
body = mkDerivation {
|
||||
name = "dependencies";
|
||||
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
||||
input1 = input1 + "/.";
|
||||
inherit input2;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
let {
|
||||
|
||||
input1 = derivation {
|
||||
name = "dependencies-input-1";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder1.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
input2 = derivation {
|
||||
name = "dependencies-input-2";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" (./. ~ "dependencies.builder2.sh")];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
body = derivation {
|
||||
name = "dependencies";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" (./dependencies.builder0.sh + "/FOOBAR/../.")];
|
||||
PATH = "@testPath@";
|
||||
input1 = input1 + "/.";
|
||||
inherit input2;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
source common.sh
|
||||
|
||||
clearStore
|
||||
|
||||
drvPath=$($nixinstantiate dependencies.nix)
|
||||
|
||||
echo "derivation is $drvPath"
|
||||
@@ -13,7 +15,7 @@ if test -n "$dot"; then
|
||||
$dot < $TEST_ROOT/graph
|
||||
fi
|
||||
|
||||
outPath=$($nixstore -rvv "$drvPath")
|
||||
outPath=$($nixstore -rvv "$drvPath") || fail "build failed"
|
||||
|
||||
# Test Graphviz graph generation.
|
||||
$nixstore -q --graph "$outPath" > $TEST_ROOT/graph
|
||||
|
||||
29
tests/export-graph.nix
Normal file
29
tests/export-graph.nix
Normal file
@@ -0,0 +1,29 @@
|
||||
with import ./config.nix;
|
||||
|
||||
rec {
|
||||
|
||||
printRefs =
|
||||
''
|
||||
echo $exportReferencesGraph
|
||||
while read path; do
|
||||
read drv
|
||||
read nrRefs
|
||||
echo "$path has $nrRefs references"
|
||||
echo "$path" >> $out
|
||||
for ((n = 0; n < $nrRefs; n++)); do read ref; echo "ref $ref"; test -e "$ref"; done
|
||||
done < refs
|
||||
'';
|
||||
|
||||
runtimeGraph = mkDerivation {
|
||||
name = "dependencies";
|
||||
builder = builtins.toFile "build-graph-builder" "${printRefs}";
|
||||
exportReferencesGraph = ["refs" (import ./dependencies.nix)];
|
||||
};
|
||||
|
||||
buildGraph = mkDerivation {
|
||||
name = "dependencies";
|
||||
builder = builtins.toFile "build-graph-builder" "${printRefs}";
|
||||
exportReferencesGraph = ["refs" (import ./dependencies.nix).drvPath];
|
||||
};
|
||||
|
||||
}
|
||||
30
tests/export-graph.sh
Normal file
30
tests/export-graph.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
source common.sh
|
||||
|
||||
clearStore
|
||||
clearProfiles
|
||||
|
||||
checkRef() {
|
||||
$nixstore -q --references ./result | grep -q "$1" || fail "missing reference $1"
|
||||
}
|
||||
|
||||
# Test the export of the runtime dependency graph.
|
||||
|
||||
outPath=$($nixbuild ./export-graph.nix -A runtimeGraph)
|
||||
|
||||
test $($nixstore -q --references ./result | wc -l) = 2 || fail "bad nr of references"
|
||||
|
||||
checkRef input-2
|
||||
for i in $(cat $outPath); do checkRef $i; done
|
||||
|
||||
# Test the export of the build-time dependency graph.
|
||||
|
||||
$nixstore --gc # should force rebuild of input-1
|
||||
|
||||
outPath=$($nixbuild ./export-graph.nix -A buildGraph)
|
||||
|
||||
checkRef input-1
|
||||
checkRef input-1.drv
|
||||
checkRef input-2
|
||||
checkRef input-2.drv
|
||||
|
||||
for i in $(cat $outPath); do checkRef $i; done
|
||||
@@ -2,7 +2,7 @@ source common.sh
|
||||
|
||||
clearStore
|
||||
|
||||
outPath=$($nixstore -r $($nixinstantiate dependencies.nix))
|
||||
outPath=$($nixbuild dependencies.nix)
|
||||
|
||||
$nixstore --export $outPath > $TEST_ROOT/exp
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
derivation {
|
||||
with import ./config.nix;
|
||||
|
||||
mkDerivation {
|
||||
name = "filter";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" (builtins.toFile "builder" "PATH=@testPath@; ln -s $input $out")];
|
||||
builder = builtins.toFile "builder" "ln -s $input $out";
|
||||
input =
|
||||
let filter = path: type:
|
||||
type != "symlink"
|
||||
@@ -1,14 +1,13 @@
|
||||
with import ./config.nix;
|
||||
|
||||
rec {
|
||||
|
||||
f2 = dummy: builder: mode: algo: hash: derivation {
|
||||
f2 = dummy: builder: mode: algo: hash: mkDerivation {
|
||||
name = "fixed";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" builder];
|
||||
inherit builder;
|
||||
outputHashMode = mode;
|
||||
outputHashAlgo = algo;
|
||||
outputHash = hash;
|
||||
PATH = "@testPath@";
|
||||
inherit dummy;
|
||||
impureEnvVars = ["IMPURE_VAR1" "IMPURE_VAR2"];
|
||||
};
|
||||
@@ -20,7 +19,6 @@ rec {
|
||||
(f ./fixed.builder1.sh "flat" "sha1" "a0b65939670bc2c010f4d5d6a0b3e4e4590fb92b")
|
||||
(f ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
|
||||
(f ./fixed.builder2.sh "recursive" "sha1" "vw46m23bizj4n8afrc0fj19wrp7mj3c0")
|
||||
(f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik")
|
||||
];
|
||||
|
||||
good2 = [
|
||||
@@ -30,6 +28,9 @@ rec {
|
||||
(f ./fixed.builder2.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858")
|
||||
];
|
||||
|
||||
sameAsAdd =
|
||||
f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik";
|
||||
|
||||
bad = [
|
||||
(f ./fixed.builder1.sh "flat" "md5" "0ddd8be4b179a529afa5f2ffae4b9858")
|
||||
];
|
||||
@@ -6,22 +6,16 @@ export IMPURE_VAR1=foo
|
||||
export IMPURE_VAR2=bar
|
||||
|
||||
echo 'testing good...'
|
||||
drvs=$($nixinstantiate fixed.nix -A good)
|
||||
echo $drvs
|
||||
$nixstore -r $drvs
|
||||
$nixbuild fixed.nix -A good
|
||||
|
||||
echo 'testing good2...'
|
||||
drvs=$($nixinstantiate fixed.nix -A good2)
|
||||
echo $drvs
|
||||
$nixstore -r $drvs
|
||||
$nixbuild fixed.nix -A good2
|
||||
|
||||
echo 'testing bad...'
|
||||
drvs=$($nixinstantiate fixed.nix -A bad)
|
||||
echo $drvs
|
||||
if $nixstore -r $drvs; then false; fi
|
||||
$nixbuild fixed.nix -A bad && fail "should fail"
|
||||
|
||||
echo 'testing reallyBad...'
|
||||
if $nixinstantiate fixed.nix -A reallyBad; then false; fi
|
||||
$nixinstantiate fixed.nix -A reallyBad && fail "should fail"
|
||||
|
||||
# While we're at it, check attribute selection a bit more.
|
||||
echo 'testing attribute selection...'
|
||||
@@ -31,6 +25,28 @@ test $($nixinstantiate fixed.nix -A good.1 | wc -l) = 1
|
||||
# Only one should run at the same time.
|
||||
echo 'testing parallelSame...'
|
||||
clearStore
|
||||
drvs=$($nixinstantiate fixed.nix -A parallelSame)
|
||||
echo $drvs
|
||||
$nixstore -r $drvs -j2
|
||||
$nixbuild fixed.nix -A parallelSame -j2
|
||||
|
||||
# Fixed-output derivations with a recursive SHA-256 hash should
|
||||
# produce the same path as "nix-store --add".
|
||||
echo 'testing sameAsAdd...'
|
||||
out=$($nixbuild fixed.nix -A sameAsAdd)
|
||||
|
||||
# This is what fixed.builder2 produces...
|
||||
rm -rf $TEST_ROOT/fixed
|
||||
mkdir $TEST_ROOT/fixed
|
||||
mkdir $TEST_ROOT/fixed/bla
|
||||
echo "Hello World!" > $TEST_ROOT/fixed/foo
|
||||
ln -s foo $TEST_ROOT/fixed/bar
|
||||
|
||||
out2=$($nixstore --add $TEST_ROOT/fixed)
|
||||
echo $out2
|
||||
test "$out" = "$out2" || exit 1
|
||||
|
||||
out3=$($nixstore --add-fixed --recursive sha256 $TEST_ROOT/fixed)
|
||||
echo $out3
|
||||
test "$out" = "$out3" || exit 1
|
||||
|
||||
out4=$($nixstore --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed)
|
||||
echo $out4
|
||||
test "$out" = "$out4" || exit 1
|
||||
|
||||
27
tests/gc-concurrent.nix
Normal file
27
tests/gc-concurrent.nix
Normal file
@@ -0,0 +1,27 @@
|
||||
with import ./config.nix;
|
||||
|
||||
rec {
|
||||
|
||||
input1 = mkDerivation {
|
||||
name = "dependencies-input-1";
|
||||
builder = ./dependencies.builder1.sh;
|
||||
};
|
||||
|
||||
input2 = mkDerivation {
|
||||
name = "dependencies-input-2";
|
||||
builder = ./dependencies.builder2.sh;
|
||||
};
|
||||
|
||||
test1 = mkDerivation {
|
||||
name = "gc-concurrent";
|
||||
builder = ./gc-concurrent.builder.sh;
|
||||
inherit input1 input2;
|
||||
};
|
||||
|
||||
test2 = mkDerivation {
|
||||
name = "gc-concurrent2";
|
||||
builder = ./gc-concurrent2.builder.sh;
|
||||
inherit input1 input2;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
let {
|
||||
|
||||
input1 = derivation {
|
||||
name = "dependencies-input-1";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder1.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
input2 = derivation {
|
||||
name = "dependencies-input-2";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder2.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
body = derivation {
|
||||
name = "gc-concurrent";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./gc-concurrent.builder.sh];
|
||||
PATH = "@testPath@";
|
||||
inherit input1 input2;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,27 +1,34 @@
|
||||
source common.sh
|
||||
|
||||
$NIX_BIN_DIR/nix-collect-garbage -vvvvv
|
||||
clearStore
|
||||
|
||||
drvPath1=$($nixinstantiate gc-concurrent.nix)
|
||||
drvPath1=$($nixinstantiate gc-concurrent.nix -A test1)
|
||||
outPath1=$($nixstore -q $drvPath1)
|
||||
|
||||
drvPath2=$($nixinstantiate gc-concurrent2.nix)
|
||||
drvPath2=$($nixinstantiate gc-concurrent.nix -A test2)
|
||||
outPath2=$($nixstore -q $drvPath2)
|
||||
|
||||
rm -f "$NIX_STATE_DIR"/gcroots/foo
|
||||
drvPath3=$($nixinstantiate simple.nix)
|
||||
outPath3=$($nixstore -r $drvPath3)
|
||||
|
||||
! test -e $outPath3.lock
|
||||
touch $outPath3.lock
|
||||
|
||||
rm -f "$NIX_STATE_DIR"/gcroots/foo*
|
||||
ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo
|
||||
ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2
|
||||
|
||||
# Start build #1 in the background. It starts immediately.
|
||||
$nixstore -rvv "$drvPath1" &
|
||||
pid1=$!
|
||||
|
||||
# Start build #2 in the background after 6 seconds.
|
||||
(sleep 6 && $nixstore -rvv "$drvPath2") &
|
||||
# Start build #2 in the background after 10 seconds.
|
||||
(sleep 10 && $nixstore -rvv "$drvPath2") &
|
||||
pid2=$!
|
||||
|
||||
# Run the garbage collector while the build is running.
|
||||
sleep 4
|
||||
$NIX_BIN_DIR/nix-collect-garbage -vvvvv
|
||||
sleep 6
|
||||
$NIX_BIN_DIR/nix-collect-garbage
|
||||
|
||||
# Wait for build #1/#2 to finish.
|
||||
echo waiting for pid $pid1 to finish...
|
||||
@@ -39,4 +46,13 @@ cat $outPath1/input-2/bar
|
||||
# derivation is a GC root.
|
||||
cat $outPath2/foobar
|
||||
|
||||
rm "$NIX_STATE_DIR"/gcroots/foo
|
||||
rm -f "$NIX_STATE_DIR"/gcroots/foo*
|
||||
|
||||
# The collector should have deleted lock files for paths that have
|
||||
# been built previously.
|
||||
! test -e $outPath3.lock
|
||||
|
||||
# If we run the collector now, it should delete outPath1/2.
|
||||
$NIX_BIN_DIR/nix-collect-garbage
|
||||
! test -e $outPath1
|
||||
! test -e $outPath2
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
let {
|
||||
|
||||
input1 = derivation {
|
||||
name = "dependencies-input-1";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder1.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
input2 = derivation {
|
||||
name = "dependencies-input-2";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./dependencies.builder2.sh];
|
||||
PATH = "@testPath@";
|
||||
};
|
||||
|
||||
body = derivation {
|
||||
name = "gc-concurrent2";
|
||||
system = "@system@";
|
||||
builder = "@shell@";
|
||||
args = ["-e" "-x" ./gc-concurrent2.builder.sh];
|
||||
PATH = "@testPath@";
|
||||
inherit input1 input2;
|
||||
};
|
||||
|
||||
}
|
||||
17
tests/gc-runtime.nix
Normal file
17
tests/gc-runtime.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
with import ./config.nix;
|
||||
|
||||
mkDerivation {
|
||||
name = "gc-runtime";
|
||||
builder =
|
||||
# Test inline source file definitions.
|
||||
builtins.toFile "builder.sh" ''
|
||||
mkdir $out
|
||||
|
||||
cat > $out/program <<EOF
|
||||
#! ${shell}
|
||||
sleep 10000
|
||||
EOF
|
||||
|
||||
chmod +x $out/program
|
||||
'';
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user