Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2615a6251a | ||
|
|
710175e6a0 | ||
|
|
ed0db2e0d8 | ||
|
|
1472cc4825 | ||
|
|
2de8504791 | ||
|
|
31e4aa6439 | ||
|
|
ebbb6ce578 | ||
|
|
c32e01eab2 | ||
|
|
08f9cfe267 | ||
|
|
96c7b98bf0 | ||
|
|
555347744d | ||
|
|
e374dbf89b | ||
|
|
01e30360d4 | ||
|
|
163db7367f | ||
|
|
161aab582b | ||
|
|
a24cb19361 | ||
|
|
9ee3b7a37a | ||
|
|
dc0ef2ca98 | ||
|
|
2e16ff22ac | ||
|
|
5cde23f869 | ||
|
|
0a2de7f543 | ||
|
|
95b49f8044 | ||
|
|
68022552d2 | ||
|
|
c34a153ae5 | ||
|
|
b4f88d0ec3 | ||
|
|
469f1eba56 | ||
|
|
e405ca506e | ||
|
|
c602930e08 | ||
|
|
4b7b0bd12c | ||
|
|
74867e72f2 | ||
|
|
f8035d06f2 | ||
|
|
9ad39df282 | ||
|
|
d551062ec4 | ||
|
|
236eb59293 | ||
|
|
720f06e3b0 | ||
|
|
37483672d4 | ||
|
|
d34b4d4f28 | ||
|
|
b9c9b461ea | ||
|
|
4ce652640b | ||
|
|
fd30f52cfc | ||
|
|
17f05dba77 | ||
|
|
d6b6b2d3a8 | ||
|
|
d2e963f7a3 | ||
|
|
c95b4ad290 | ||
|
|
d99d04e644 | ||
|
|
545145cd58 | ||
|
|
9df93f30bd | ||
|
|
06434072e7 | ||
|
|
06d3d7355d | ||
|
|
177a7782ae | ||
|
|
4a013962bd | ||
|
|
758bd4673a | ||
|
|
9f4c19276d | ||
|
|
26ff1cdf89 | ||
|
|
64c617e984 | ||
|
|
2ac02440dc |
@@ -1,3 +1,3 @@
|
||||
SUBDIRS = externals src scripts corepkgs
|
||||
SUBDIRS = externals src scripts corepkgs doc
|
||||
|
||||
EXTRA_DIST = boost/*.hpp boost/format/*.hpp substitute.mk
|
||||
@@ -1,4 +1,4 @@
|
||||
AC_INIT(nix, 0.2pre1)
|
||||
AC_INIT(nix, 0.3)
|
||||
AC_CONFIG_SRCDIR(src/nix.cc)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
AM_INIT_AUTOMAKE
|
||||
@@ -11,10 +11,13 @@ AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
|
||||
AC_PATH_PROG(wget, wget)
|
||||
|
||||
AC_CHECK_LIB(pthread, pthread_mutex_init)
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile externals/Makefile src/Makefile scripts/Makefile
|
||||
corepkgs/Makefile corepkgs/fetchurl/Makefile
|
||||
corepkgs/nar/Makefile])
|
||||
corepkgs/nar/Makefile
|
||||
doc/Makefile doc/manual/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "downloading $url into $out..."
|
||||
wget "$url" -O "$out" || exit 1
|
||||
|
||||
actual=$(md5sum -b $out | cut -c1-32)
|
||||
if ! test "$actual" == "$md5"; then
|
||||
echo "hash is $actual, expected $md5"
|
||||
exit 1
|
||||
fi
|
||||
19
corepkgs/fetchurl/fetchurl.sh.in
Normal file
19
corepkgs/fetchurl/fetchurl.sh.in
Normal file
@@ -0,0 +1,19 @@
|
||||
#! /bin/sh
|
||||
|
||||
export PATH=/bin:/usr/bin
|
||||
|
||||
echo "downloading $url into $out..."
|
||||
|
||||
prefetch=@prefix@/store/nix-prefetch-url-$md5
|
||||
if test -f "$prefetch"; then
|
||||
echo "using prefetched $prefetch";
|
||||
mv $prefetch $out || exit 1
|
||||
else
|
||||
@wget@ "$url" -O "$out" || exit 1
|
||||
fi
|
||||
|
||||
actual=$(@bindir@/nix-hash --flat $out)
|
||||
if test "$actual" != "$md5"; then
|
||||
echo "hash is $actual, expected $md5"
|
||||
exit 1
|
||||
fi
|
||||
1
doc/Makefile.am
Normal file
1
doc/Makefile.am
Normal file
@@ -0,0 +1 @@
|
||||
SUBDIRS = manual
|
||||
30
doc/manual/Makefile.am
Normal file
30
doc/manual/Makefile.am
Normal file
@@ -0,0 +1,30 @@
|
||||
DOCBOOK_DTD = /nix/current/xml/dtd/docbook
|
||||
DOCBOOK_XSL = /nix/current/xml/xsl/docbook
|
||||
|
||||
ENV = SGML_CATALOG_FILES=$(DOCBOOK_DTD)/docbook.cat
|
||||
|
||||
XMLLINT = $(ENV) xmllint --catalogs
|
||||
XSLTPROC = $(ENV) xsltproc --catalogs
|
||||
|
||||
SOURCES = book.xml introduction.xml installation.xml nix-reference.xml \
|
||||
troubleshooting.xml bugs.xml
|
||||
|
||||
book.is-valid: $(SOURCES)
|
||||
$(XMLLINT) --noout --valid book.xml
|
||||
touch $@
|
||||
|
||||
man1_MANS = nix.1 fix.1
|
||||
|
||||
man nix.1 fix.1: $(SOURCES) book.is-valid
|
||||
$(XSLTPROC) $(DOCBOOK_XSL)/manpages/docbook.xsl book.xml
|
||||
|
||||
book.html: $(SOURCES) book.is-valid
|
||||
$(XSLTPROC) --output book.html $(DOCBOOK_XSL)/html/docbook.xsl book.xml
|
||||
|
||||
all-local: book.html
|
||||
|
||||
install-data-local: book.html
|
||||
$(INSTALL) -d $(datadir)/nix/manual
|
||||
$(INSTALL_DATA) book.html $(datadir)/nix/manual
|
||||
|
||||
EXTRA_DIST = $(SOURCES) book.html nix.1 fix.1 book.is-valid
|
||||
64
doc/manual/book.xml
Normal file
64
doc/manual/book.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE book
|
||||
PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
|
||||
[
|
||||
<!ENTITY introduction SYSTEM "introduction.xml">
|
||||
<!ENTITY installation SYSTEM "installation.xml">
|
||||
<!ENTITY nix-reference SYSTEM "nix-reference.xml">
|
||||
<!ENTITY fix-reference SYSTEM "fix-reference.xml">
|
||||
<!ENTITY troubleshooting SYSTEM "troubleshooting.xml">
|
||||
<!ENTITY bugs SYSTEM "bugs.xml">
|
||||
]>
|
||||
|
||||
<book>
|
||||
<title>Nix: The Manual</title>
|
||||
|
||||
<bookinfo>
|
||||
<author>
|
||||
<firstname>Eelco</firstname>
|
||||
<surname>Dolstra</surname>
|
||||
</author>
|
||||
<copyright>
|
||||
<year>2003</year>
|
||||
<holder>Eelco Dolstra</holder>
|
||||
</copyright>
|
||||
</bookinfo>
|
||||
|
||||
&introduction;
|
||||
&installation;
|
||||
|
||||
<chapter>
|
||||
<title>A Guided Tour</title>
|
||||
<para>
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Nix Syntax and Semantics</title>
|
||||
<para>
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Fix Language Reference</title>
|
||||
<para>
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Writing Builders</title>
|
||||
<para>
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<appendix>
|
||||
<title>Command Reference</title>
|
||||
&nix-reference;
|
||||
&fix-reference;
|
||||
</appendix>
|
||||
|
||||
&troubleshooting;
|
||||
&bugs;
|
||||
|
||||
</book>
|
||||
43
doc/manual/bugs.xml
Normal file
43
doc/manual/bugs.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<appendix>
|
||||
<title>Bugs</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Nix should automatically recover the Berkeley DB database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Nix should automatically remove Berkeley DB logfiles.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Unify the concepts of successors and substitutes into a general notion
|
||||
of <emphasis>equivalent expressions</emphasis>. Expressions are
|
||||
equivalent if they have the same target paths with the same
|
||||
identifiers. However, even though they are functionally equivalent,
|
||||
they may differ stronly with respect to their <emphasis>performance
|
||||
characteristics</emphasis>. For example, realising a slice is more
|
||||
efficient that realising the derivation from which that slice was
|
||||
produced. On the other hand, distributing sources may be more
|
||||
efficient (storage- or bandwidth-wise) than distributing binaries. So
|
||||
we need to be able to attach weigths or priorities or performance
|
||||
annotations to expressions; Nix can then choose the most efficient
|
||||
expression dependent on the context.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</appendix>
|
||||
|
||||
<!--
|
||||
local variables:
|
||||
sgml-parent-document: ("book.xml" "appendix")
|
||||
end:
|
||||
-->
|
||||
37
doc/manual/fix-reference.xml
Normal file
37
doc/manual/fix-reference.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<refentry>
|
||||
<refnamediv>
|
||||
<refname>fix</refname>
|
||||
<refpurpose>generate Nix expressions from a high-level description</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>fix</command>
|
||||
<group choice='opt' rep='repeat'>
|
||||
<arg><option>--verbose</option></arg>
|
||||
<arg><option>-v</option></arg>
|
||||
</group>
|
||||
<arg rep='repeat'><replaceable>files</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
The command <command>fix</command> generates Nix expressions from
|
||||
expressions is Fix's own high-level language. While Nix expressions are
|
||||
very primitive and not intended to be written directly, Fix expressions
|
||||
are quite easy to write.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
|
||||
|
||||
<!--
|
||||
local variables:
|
||||
sgml-parent-document: ("book.xml" "refentry")
|
||||
end:
|
||||
-->
|
||||
80
doc/manual/installation.xml
Normal file
80
doc/manual/installation.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<chapter>
|
||||
<title>Installation</title>
|
||||
|
||||
<sect1>
|
||||
<title>Prerequisites</title>
|
||||
|
||||
<para>
|
||||
Nix uses Sleepycat's Berkeley DB and CWI's ATerm library. However, these
|
||||
are fetched automatically as part of the build process.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Other than that, you need a good C++ compiler. GCC 2.95 does not appear
|
||||
to work; please use GCC 3.x.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Obtaining Nix</title>
|
||||
|
||||
<para>
|
||||
Nix can be obtained from its <ulink
|
||||
url='http://losser.st-lab.cs.uu.nl:12080/repos/trace/nix/trunk'>Subversion
|
||||
repository</ulink>. For example, the following command will check out
|
||||
the latest revision into a directory called <filename>nix</filename>:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ svn checkout http://losser.st-lab.cs.uu.nl:12080/repos/trace/nix/trunk nix</screen>
|
||||
|
||||
<para>
|
||||
Likewise, specific releases can be obtained from the <ulink
|
||||
url='http://losser.st-lab.cs.uu.nl:12080/repos/trace/nix/tags'>tags
|
||||
directory</ulink> of the repository. If you don't have Subversion, you
|
||||
can download a <ulink
|
||||
url='http://losser.st-lab.cs.uu.nl:12080/dist/trace/'>compressed
|
||||
tar-file</ulink> of the latest revision of the repository.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Building Nix</title>
|
||||
|
||||
<para>
|
||||
To build Nix, do the following:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ autoreconf -i
|
||||
$ ./configure <replaceable>options...</replaceable>
|
||||
$ make
|
||||
$ make install</screen>
|
||||
|
||||
<para>
|
||||
Currently, the only useful switch for <command>configure</command> is
|
||||
<option>--prefix=<replaceable>prefix</replaceable></option> to specify
|
||||
where Nix is to be installed. The default installation directory is
|
||||
<filename>/nix</filename>. You can change this to any location you like.
|
||||
You should ensure that you have write permission to the installation
|
||||
prefix.
|
||||
</para>
|
||||
|
||||
<warning>
|
||||
<para>
|
||||
It is advisable <emphasis>not</emphasis> to change the installation
|
||||
prefix, since doing so will in all likelihood make it impossible to use
|
||||
derivates built on other systems.
|
||||
</para>
|
||||
</warning>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
<!--
|
||||
local variables:
|
||||
sgml-parent-document: ("book.xml" "chapter")
|
||||
end:
|
||||
-->
|
||||
184
doc/manual/introduction.xml
Normal file
184
doc/manual/introduction.xml
Normal file
@@ -0,0 +1,184 @@
|
||||
<chapter>
|
||||
<title>Introduction</title>
|
||||
|
||||
<sect1>
|
||||
<title>The problem space</title>
|
||||
|
||||
<para>
|
||||
Nix is a system for controlling the automatic creation and distribution
|
||||
of data, such as computer programs and other software artifacts. This is
|
||||
a very general problem, and there are many applications that fall under
|
||||
this description.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>Build management</title>
|
||||
|
||||
<para>
|
||||
Build management tools are used to perform <emphasis>software
|
||||
builds</emphasis>, that is, the construction of derived products such
|
||||
as executable programs from source code. A commonly used build tool is
|
||||
Make, which is a standard tool on Unix systems. These tools have to
|
||||
deal with several issues:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Package management</title>
|
||||
|
||||
<para>
|
||||
After software has been built, is must also be
|
||||
<emphasis>deployed</emphasis> in the intended target environment, e.g.,
|
||||
the user's workstation. Examples include the Red Hat package manager
|
||||
(RPM), Microsoft's MSI, and so on. Here also we have to deal with
|
||||
several issues:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The <emphasis>creation</emphasis> of packages from some formal
|
||||
description of what artifacts should be distributed in the
|
||||
package.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The <emphasis>deployment</emphasis> of packages, that is, the
|
||||
mechanism by which we get them onto the intended target
|
||||
environment. This can be as simple as copying a file, but
|
||||
complexity comes from the wide range of possible installation
|
||||
media (such as a network install), and the scalability of the
|
||||
process (if a program must be installed on a thousand systems, we
|
||||
do not want to visit each system and perform some manual steps to
|
||||
install the program on that system; that is, the complexity for
|
||||
the system administrator should be constant, not linear).
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<sect1>
|
||||
<title>What Nix can do for you</title>
|
||||
|
||||
<para>
|
||||
Here is a summary of what Nix provides:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Reliable dependencies.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Support for variability.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Transparent source/binary deployment.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Easy configuration duplication.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Automatic storage management.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Atomic upgrades and rollbacks.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Support for many simultaneous configurations.</emphasis>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Here is what Nix doesn't yet provide, but will:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>Build management.</emphasis> In principle it is already
|
||||
possible to do build management using Fix (by writing builders that
|
||||
perform appropriate build steps), but the Fix language is not yet
|
||||
powerful enough to make this pleasant. The <ulink
|
||||
url='http://www.cs.uu.nl/~eelco/maak/'>Maak build manager</ulink>
|
||||
should be retargeted to produce Nix expressions, or alternatively,
|
||||
extend Fix with Maak's semantics and concrete syntax (since Fix needs
|
||||
a concrete syntax anyway). Another interesting idea is to write a
|
||||
<command>make</command> implementation that uses Nix as a back-end to
|
||||
support <ulink
|
||||
url='http://www.research.att.com/~bs/bs_faq.html#legacy'>legacy</ulink>
|
||||
build files.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<sect1>
|
||||
<title>The Nix system</title>
|
||||
|
||||
<para>
|
||||
...
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Existing tools in this field generally both a underlying model (such as
|
||||
the derivation graph of build tools, or the versioning scheme that
|
||||
determines when two packages are <quote>compatible</quote> in a package
|
||||
management system) and a formalism that allows ...
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Following the principle of separation of mechanism and policy, the Nix
|
||||
system separates the <emphasis>low-level aspect</emphasis> of file system
|
||||
object management form the <emphasis>high-level aspect</emphasis> of the
|
||||
...
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
<!--
|
||||
local variables:
|
||||
sgml-parent-document: ("book.xml" "chapter")
|
||||
end:
|
||||
-->
|
||||
444
doc/manual/nix-reference.xml
Normal file
444
doc/manual/nix-reference.xml
Normal file
@@ -0,0 +1,444 @@
|
||||
<refentry>
|
||||
<refnamediv>
|
||||
<refname>nix</refname>
|
||||
<refpurpose>manipulate or query the Nix store</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix</command>
|
||||
<group choice='opt'>
|
||||
<arg><option>--path</option></arg>
|
||||
<arg><option>-p</option></arg>
|
||||
</group>
|
||||
<group choice='opt' rep='repeat'>
|
||||
<arg><option>--verbose</option></arg>
|
||||
<arg><option>-v</option></arg>
|
||||
</group>
|
||||
<group choice='opt' rep='repeat'>
|
||||
<arg><option>--keep-failed</option></arg>
|
||||
<arg><option>-K</option></arg>
|
||||
</group>
|
||||
<arg choice='plain'><replaceable>operation</replaceable></arg>
|
||||
<arg rep='repeat'><replaceable>options</replaceable></arg>
|
||||
<arg rep='repeat'><replaceable>arguments</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
The command <command>nix</command> provides access to the Nix store. This
|
||||
is the (set of) path(s) where Nix expressions and the file system objects
|
||||
built by them are stored.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>nix</command> has many subcommands called
|
||||
<emphasis>operations</emphasis>. These are individually documented
|
||||
below. Exactly one operation must always be provided.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Common Options</title>
|
||||
|
||||
<para>
|
||||
In this section the options that are common to all Nix operations are
|
||||
listed. These options are allowed for every subcommand (although they
|
||||
may not always have an effect).
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--path</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Indicates that any identifier arguments to the operation are paths
|
||||
in the store rather than identifiers.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--verbose</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Increases the level of verbosity of diagnostic messages printed on
|
||||
standard error. For each Nix operation, the information printed on
|
||||
standard output is well-defined and specified below in the
|
||||
respective sections. Any diagnostic information is printed on
|
||||
standard error, never on standard output.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This option may be specified repeatedly. Currently, the following
|
||||
verbosity levels exist:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>0</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Print error messages only.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>1</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Print informational messages.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>2</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Print even more informational messages.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>3</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Print messages that should only be useful for debugging.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>4</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<quote>Vomit mode</quote>: print vast amounts of debug
|
||||
information.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--keep-failed</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Specifies that in case of a build failure, the temporary directory
|
||||
(usually in <filename>/tmp</filename>) in which the build takes
|
||||
place should not be deleted. The path of the build directory is
|
||||
printed as an informational message.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsect1>
|
||||
<title>Operation <option>--install</option></title>
|
||||
|
||||
<refsect2>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix</command>
|
||||
<group>
|
||||
<arg><option>--install</option></arg>
|
||||
<arg><option>-i</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>ids</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
The operation <option>--install</option> realises the Nix expressions
|
||||
identified by <replaceable>ids</replaceable> in the file system. If
|
||||
these expressions are derivation expressions, they are first
|
||||
normalised. That is, their target paths are are built, unless a normal
|
||||
form is already known.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The identifiers of the normal forms of the given Nix expressions are
|
||||
printed on standard output.
|
||||
</para>
|
||||
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsect1>
|
||||
<title>Operation <option>--delete</option></title>
|
||||
|
||||
<refsect2>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix</command>
|
||||
<group>
|
||||
<arg><option>--delete</option></arg>
|
||||
<arg><option>-d</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
The operation <option>--delete</option> unconditionally deletes the
|
||||
paths <replaceable>paths</replaceable> from the Nix store. It is an
|
||||
error to attempt to delete paths outside of the store.
|
||||
</para>
|
||||
|
||||
<warning>
|
||||
<para>
|
||||
This operation should almost never be called directly, since no
|
||||
attempt is made to verify that no references exist to the paths to
|
||||
be deleted. Therefore, careless deletion can result in an
|
||||
inconsistent system. Deletion of paths in the store is done by the
|
||||
garbage collector (which uses <option>--delete</option> to delete
|
||||
unreferenced paths).
|
||||
|
||||
</para>
|
||||
</warning>
|
||||
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsect1>
|
||||
<title>Operation <option>--query</option></title>
|
||||
|
||||
<refsect2>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix</command>
|
||||
<group>
|
||||
<arg><option>--query</option></arg>
|
||||
<arg><option>-q</option></arg>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<arg><option>--list</option></arg>
|
||||
<arg><option>-l</option></arg>
|
||||
</group>
|
||||
<group>
|
||||
<arg><option>--requisites</option></arg>
|
||||
<arg><option>-r</option></arg>
|
||||
</group>
|
||||
<group>
|
||||
<arg><option>--expansion</option></arg>
|
||||
<arg><option>-e</option></arg>
|
||||
</group>
|
||||
<group>
|
||||
<arg><option>--graph</option></arg>
|
||||
<arg><option>-g</option></arg>
|
||||
</group>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
The operation <option>--query</option> displays various bits of
|
||||
information about Nix expressions or paths in the store. The queries
|
||||
are described in <xref linkend='nixref-queries' />. At most one query
|
||||
can be specified; the default query is <option>--list</option>.
|
||||
</para>
|
||||
|
||||
</refsect2>
|
||||
|
||||
<refsect2 id='nixref-queries'>
|
||||
<title>Queries</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--list</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Prints out the target paths of the Nix expressions indicated by
|
||||
the identifiers <replaceable>args</replaceable>. In the case of
|
||||
a derivation expression, these are the paths that will be
|
||||
produced by the builder of the expression. In the case of a
|
||||
slice expression, these are the root paths (which are generally
|
||||
the paths that were produced by the builder of the derivation
|
||||
expression of which the slice is a normal form).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This query has one option:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--normalise</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Causes the target paths of the <emphasis>normal
|
||||
forms</emphasis> of the expressions to be printed, rather
|
||||
than the target paths of the expressions themselves.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--requisites</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Prints out the requisite paths of the Nix expressions indicated
|
||||
by the identifiers <replaceable>args</replaceable>. The
|
||||
requisite paths of a Nix expression are the paths that need to be
|
||||
present in the system to be able to realise the expression. That
|
||||
is, they form the <emphasis>closure</emphasis> of the expression
|
||||
in the file system (i.e., no path in the set of requisite paths
|
||||
points to anything outside the set of requisite paths).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The notion of requisite paths is very useful when one wants to
|
||||
distribute Nix expressions. Since they form a closure, they are
|
||||
the only paths one needs to distribute to another system to be
|
||||
able to realise the expression on the other system.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This query is generally used to implement various kinds of
|
||||
distribution. A <emphasis>source distribution</emphasis> is
|
||||
obtained by distributing the requisite paths of a derivation
|
||||
expression. A <emphasis>binary distribution</emphasis> is
|
||||
obtained by distributing the requisite paths of a slice
|
||||
expression (i.e., the normal form of a derivation expression; you
|
||||
can directly specify the identifier of the slice expression, or
|
||||
use <option>--normalise</option> and specify the identifier of a
|
||||
derivation expression). A <emphasis>cache
|
||||
distribution</emphasis> is obtained by distributing the
|
||||
requisite paths of a derivation expression and specifying the
|
||||
option <option>--include-successors</option>. This will include
|
||||
not just the paths of a source and binary distribution, but also
|
||||
all expressions and paths of subterms of the source. This is
|
||||
useful if one wants to realise on the target system a Nix
|
||||
expression that is similar but not quite the same as the one
|
||||
being distributed, since any common subterms will be reused.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This query has a number of options:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--normalise</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Causes the requisite paths of the <emphasis>normal
|
||||
forms</emphasis> of the expressions to be printed, rather
|
||||
than the requisite paths of the expressions themselves.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--exclude-exprs</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Excludes the paths of Nix expressions. This causes the
|
||||
closure property to be lost, that is, the resulting set of
|
||||
paths is not enough to ensure realisibility.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--include-successors</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Also include the requisites of successors (normal forms).
|
||||
Only the requisites of <emphasis>known</emphasis>
|
||||
successors are included, i.e., the normal forms of
|
||||
derivation expressions that have never been normalised will
|
||||
not be included.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that not just the successor of a derivation expression
|
||||
will be included, but also the successors of all input
|
||||
expressions of that derivation expression. I.e., all
|
||||
normal forms of subterms involved in the normalisation of
|
||||
the top-level term are included.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--expansion</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
For each identifier in <replaceable>args</replaceable>, prints
|
||||
all expansions of that identifier, that is, all paths whose
|
||||
current content matches the identifier.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--graph</option></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Prints a graph of the closure of the expressions identified by
|
||||
<replaceable>args</replaceable> in the format of the
|
||||
<command>dot</command> tool of AT&T's GraphViz package.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
|
||||
<!--
|
||||
local variables:
|
||||
sgml-parent-document: ("book.xml" "refentry")
|
||||
end:
|
||||
-->
|
||||
48
doc/manual/troubleshooting.xml
Normal file
48
doc/manual/troubleshooting.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<appendix>
|
||||
<title>Troubleshooting</title>
|
||||
|
||||
<sect1>
|
||||
<title>Database hangs</title>
|
||||
|
||||
<para>
|
||||
If Nix or Fix appear to hang immediately after they are started, Nix's
|
||||
database is probably <quote>wedged</quote>, i.e., some process died while
|
||||
it held a lock on the database. The solution is to ensure that no other
|
||||
processes are accessing the database and then run the following command:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ db_recover -e -h <replaceable>prefix</replaceable>/var/nix/db</screen>
|
||||
|
||||
<para>
|
||||
Here, <replaceable>prefix</replaceable> should be replaced by Nix's
|
||||
installation prefix.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1>
|
||||
<title>Database logfile removal</title>
|
||||
|
||||
<para>
|
||||
Every time a Nix database transaction takes place, Nix writes a record of
|
||||
this transaction to a <emphasis>log</emphasis> in its database directory
|
||||
to ensure that the operation can be replayed in case of a application or
|
||||
system crash. However, without manual intervention, the log grows
|
||||
indefinitely. Hence, unused log files should be deleted periodically.
|
||||
This can be accomplished using the following command:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ rm `db_archive -a -h <replaceable>prefix</replaceable>/var/nix/db`</screen>
|
||||
|
||||
</sect1>
|
||||
|
||||
</appendix>
|
||||
|
||||
<!--
|
||||
local variables:
|
||||
sgml-parent-document: ("book.xml" "appendix")
|
||||
end:
|
||||
-->
|
||||
14
externals/Makefile.am
vendored
14
externals/Makefile.am
vendored
@@ -1,10 +1,13 @@
|
||||
# Berkeley DB
|
||||
|
||||
DB = db-4.0.14
|
||||
DB_URL = http://www.sleepycat.com/update/snapshot/db-4.0.14.tar.gz
|
||||
|
||||
$(DB).tar.gz:
|
||||
wget $(DB_URL)
|
||||
@echo "Nix requires Berkeley DB to build."
|
||||
@echo "Please download version 4.0.14 from"
|
||||
@echo " http://www.sleepycat.com/update/snapshot/db-4.0.14.tar.gz"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
$(DB): $(DB).tar.gz
|
||||
gunzip < $(DB).tar.gz | tar xvf -
|
||||
@@ -26,10 +29,13 @@ build-db: have-db
|
||||
# CWI ATerm
|
||||
|
||||
ATERM = aterm-2.0
|
||||
ATERM_URL = http://www.cwi.nl/projects/MetaEnv/aterm/aterm-2.0.tar.gz
|
||||
|
||||
$(ATERM).tar.gz:
|
||||
wget $(ATERM_URL)
|
||||
@echo "Nix requires the CWI ATerm library to build."
|
||||
@echo "Please download version 2.0 from"
|
||||
@echo " http://www.cwi.nl/projects/MetaEnv/aterm/aterm-2.0.tar.gz"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
$(ATERM): $(ATERM).tar.gz
|
||||
gunzip < $(ATERM).tar.gz | tar xvf -
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
bin_SCRIPTS = nix-switch nix-collect-garbage \
|
||||
nix-pull nix-push
|
||||
nix-pull nix-push nix-prefetch-url
|
||||
|
||||
noinst_SCRIPTS = nix-profile.sh
|
||||
|
||||
@@ -14,5 +14,6 @@ include ../substitute.mk
|
||||
|
||||
EXTRA_DIST = nix-switch.in nix-collect-garbage.in \
|
||||
nix-pull.in nix-push.in nix-profile.sh.in \
|
||||
nix-prefetch-url.in \
|
||||
prebuilts.conf
|
||||
|
||||
|
||||
48
scripts/nix-prefetch-url.in
Normal file
48
scripts/nix-prefetch-url.in
Normal file
@@ -0,0 +1,48 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use IPC::Open2;
|
||||
|
||||
my $url = shift @ARGV;
|
||||
defined $url or die;
|
||||
|
||||
print "fetching $url...\n";
|
||||
|
||||
my $out = "@prefix@/store/nix-prefetch-url-$$";
|
||||
|
||||
system "@wget@ '$url' -O '$out'";
|
||||
$? == 0 or die "unable to fetch $url";
|
||||
|
||||
my $hash=`@bindir@/nix-hash --flat $out`;
|
||||
$? == 0 or die "unable to hash $out";
|
||||
chomp $hash;
|
||||
|
||||
print "file has hash $hash\n";
|
||||
|
||||
my $out2 = "@prefix@/store/nix-prefetch-url-$hash";
|
||||
rename $out, $out2;
|
||||
|
||||
# Create a Fix expression.
|
||||
my $fixexpr =
|
||||
"App(IncludeFix(\"fetchurl/fetchurl.fix\"), " .
|
||||
"[(\"url\", \"$url\"), (\"md5\", \"$hash\")])";
|
||||
|
||||
# Instantiate a Nix expression.
|
||||
print STDERR "running fix...\n";
|
||||
my $pid = open2(\*READ, \*WRITE, "fix -") or die "cannot run fix";
|
||||
|
||||
print WRITE $fixexpr;
|
||||
close WRITE;
|
||||
|
||||
my $id = <READ>;
|
||||
chomp $id;
|
||||
|
||||
waitpid $pid, 0;
|
||||
$? == 0 or die "fix failed";
|
||||
|
||||
# Run Nix.
|
||||
print STDERR "running nix...\n";
|
||||
system "nix --install $id > /dev/null";
|
||||
$? == 0 or die "`nix --install' failed";
|
||||
|
||||
unlink $out2;
|
||||
@@ -1,11 +1,18 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use IPC::Open2;
|
||||
|
||||
my $tmpfile = "@localstatedir@/nix/pull.tmp";
|
||||
my $conffile = "@sysconfdir@/nix/prebuilts.conf";
|
||||
|
||||
my @ids;
|
||||
my @subs;
|
||||
my @sucs;
|
||||
|
||||
my $fullexpr = "[";
|
||||
my $first = 1;
|
||||
|
||||
open CONFFILE, "<$conffile";
|
||||
|
||||
while (<CONFFILE>) {
|
||||
@@ -41,7 +48,7 @@ while (<CONFFILE>) {
|
||||
$outname = "unnamed";
|
||||
}
|
||||
|
||||
print "registering $id -> $url/$fn\n";
|
||||
print STDERR "$id ($outname)\n";
|
||||
|
||||
# Construct a Fix expression that fetches and unpacks a
|
||||
# Nix archive from the network.
|
||||
@@ -55,23 +62,14 @@ while (<CONFFILE>) {
|
||||
", (\"id\", \"$id\")" .
|
||||
"])";
|
||||
|
||||
my $fixfile = "/tmp/nix-pull-tmp.fix";
|
||||
open FIX, ">$fixfile";
|
||||
print FIX $fixexpr;
|
||||
close FIX;
|
||||
if (!$first) { $fullexpr .= "," };
|
||||
$first = 0;
|
||||
$fullexpr .= $fixexpr; # !!! O(n^2)?
|
||||
|
||||
# Instantiate a Nix expression from the Fix expression.
|
||||
my $nid = `fix $fixfile`;
|
||||
$? and die "instantiating Nix archive expression";
|
||||
chomp $nid;
|
||||
die unless $nid =~ /^([0-9a-z]{32})$/;
|
||||
|
||||
push @subs, $id;
|
||||
push @subs, $nid;
|
||||
push @ids, $id;
|
||||
|
||||
# Does the name encode a successor relation?
|
||||
if (defined $fsid) {
|
||||
print "NORMAL $fsid -> $id\n";
|
||||
push @sucs, $fsid;
|
||||
push @sucs, $id;
|
||||
}
|
||||
@@ -84,8 +82,34 @@ while (<CONFFILE>) {
|
||||
|
||||
}
|
||||
|
||||
$fullexpr .= "]";
|
||||
|
||||
# Instantiate Nix expressions from the Fix expressions we created above.
|
||||
print STDERR "running fix...\n";
|
||||
my $pid = open2(\*READ, \*WRITE, "fix -") or die "cannot run fix";
|
||||
|
||||
print WRITE $fullexpr;
|
||||
close WRITE;
|
||||
my $i = 0;
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die unless /^([0-9a-z]{32})$/;
|
||||
my $nid = $1;
|
||||
die unless ($i < scalar @ids);
|
||||
my $id = $ids[$i++];
|
||||
push @subs, $id;
|
||||
push @subs, $nid;
|
||||
}
|
||||
|
||||
waitpid $pid, 0;
|
||||
$? == 0 or die "fix failed";
|
||||
|
||||
# Register all substitutes.
|
||||
print STDERR "registering substitutes...\n";
|
||||
system "nix --substitute @subs";
|
||||
if ($?) { die "`nix --substitute' failed"; }
|
||||
|
||||
# Register all successors.
|
||||
print STDERR "registering successors...\n";
|
||||
system "nix --successor @sucs";
|
||||
if ($?) { die "`nix --successor' failed"; }
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my @pushlist;
|
||||
my $fixfile = "/tmp/nix-push-tmp.fix";
|
||||
open FIX, ">$fixfile";
|
||||
print FIX "[";
|
||||
my $first = 1;
|
||||
|
||||
foreach my $id (@ARGV) {
|
||||
|
||||
@@ -35,6 +38,7 @@ foreach my $id (@ARGV) {
|
||||
foreach my $path (@paths) {
|
||||
|
||||
next unless ($path =~ /\/([0-9a-z]{32})[^\/]*/);
|
||||
print "$path\n";
|
||||
my $pathid = $1;
|
||||
|
||||
# Construct a name for the Nix archive. If the file is an
|
||||
@@ -51,30 +55,41 @@ foreach my $id (@ARGV) {
|
||||
"[ (\"path\", Slice([\"$pathid\"], [(\"$path\", \"$pathid\", [])]))" .
|
||||
"])";
|
||||
|
||||
my $fixfile = "/tmp/nix-push-tmp.fix";
|
||||
open FIX, ">$fixfile";
|
||||
print FIX "," unless ($first);
|
||||
$first = 0;
|
||||
print FIX $fixexpr;
|
||||
close FIX;
|
||||
|
||||
# Instantiate a Nix expression from the Fix expression.
|
||||
my $nid = `fix $fixfile`;
|
||||
$? and die "instantiating Nix archive expression";
|
||||
chomp $nid;
|
||||
die unless $nid =~ /^([0-9a-z]{32})$/;
|
||||
|
||||
# Realise the Nix expression.
|
||||
system "nix --install $nid";
|
||||
if ($?) { die "`nix --install' failed"; }
|
||||
my $npath = `nix --query --list $nid 2> /dev/null`;
|
||||
$? and die "`nix --query --list' failed";
|
||||
chomp $npath;
|
||||
|
||||
push @pushlist, "$npath/*";
|
||||
|
||||
print "$path -> $npath\n";
|
||||
}
|
||||
}
|
||||
|
||||
print FIX "]";
|
||||
close FIX;
|
||||
|
||||
# Instantiate a Nix expression from the Fix expression.
|
||||
my @nids;
|
||||
print STDERR "running fix...\n";
|
||||
open NIDS, "fix $fixfile |" or die "cannot run fix";
|
||||
while (<NIDS>) {
|
||||
chomp;
|
||||
die unless /^([0-9a-z]{32})$/;
|
||||
push @nids, $1;
|
||||
}
|
||||
|
||||
|
||||
# Realise the Nix expression.
|
||||
my @pushlist;
|
||||
print STDERR "creating archives...\n";
|
||||
system "nix --install @nids > /dev/null";
|
||||
if ($?) { die "`nix --install' failed"; }
|
||||
|
||||
open NIDS, "nix --query --list @nids |" or die "cannot run nix";
|
||||
while (<NIDS>) {
|
||||
chomp;
|
||||
die unless (/^\//);
|
||||
print "$_\n";
|
||||
push @pushlist, "$_/*";
|
||||
}
|
||||
|
||||
# Push the prebuilts to the server. !!! FIXME
|
||||
if (scalar @pushlist > 0) {
|
||||
system "rsync -av -e ssh @pushlist eelco\@losser.st-lab.cs.uu.nl:/home/eelco/public_html/nix-dist/";
|
||||
|
||||
@@ -4,11 +4,15 @@ use strict;
|
||||
|
||||
my $keep = 0;
|
||||
my $sourceroot = 0;
|
||||
my $name = "current";
|
||||
my $srcid;
|
||||
|
||||
foreach my $arg (@ARGV) {
|
||||
my $argnr = 0;
|
||||
while ($argnr < scalar @ARGV) {
|
||||
my $arg = $ARGV[$argnr++];
|
||||
if ($arg eq "--keep") { $keep = 1; }
|
||||
elsif ($arg eq "--source-root") { $sourceroot = 1; }
|
||||
elsif ($arg eq "--name") { $name = $ARGV[$argnr++]; }
|
||||
elsif ($arg =~ /^([0-9a-z]{32})$/) { $srcid = $arg; }
|
||||
else { die "unknown argument `$arg'" };
|
||||
}
|
||||
@@ -54,7 +58,7 @@ if ($sourceroot) {
|
||||
close ID;
|
||||
}
|
||||
|
||||
my $current = "$linkdir/current";
|
||||
my $current = "$linkdir/$name";
|
||||
|
||||
# Read the current generation so that we can delete it (if --keep
|
||||
# wasn't specified).
|
||||
@@ -70,7 +74,7 @@ my $oldlink = readlink($current);
|
||||
|
||||
print "switching $current to $link\n";
|
||||
|
||||
my $tmplink = "$linkdir/new_current";
|
||||
my $tmplink = "$linkdir/.new_$name";
|
||||
symlink($link, $tmplink) or die "cannot create $tmplink";
|
||||
rename($tmplink, $current) or die "cannot rename $tmplink";
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ noinst_LIBRARIES = libnix.a libshared.a
|
||||
|
||||
libnix_a_SOURCES = util.cc hash.cc archive.cc md5.c \
|
||||
store.cc fstate.cc normalise.cc exec.cc \
|
||||
globals.cc db.cc references.cc
|
||||
globals.cc db.cc references.cc pathlocks.cc
|
||||
|
||||
libshared_a_SOURCES = shared.cc
|
||||
|
||||
@@ -42,7 +42,9 @@ nix.o: nix-help.txt.hh
|
||||
|
||||
install-data-local:
|
||||
$(INSTALL) -d $(localstatedir)/nix
|
||||
$(INSTALL) -d $(localstatedir)/nix/db
|
||||
$(INSTALL) -d $(localstatedir)/nix/links
|
||||
rm -f $(prefix)/current
|
||||
ln -sf $(localstatedir)/nix/links/current $(prefix)/current
|
||||
$(INSTALL) -d $(localstatedir)/log/nix
|
||||
$(INSTALL) -d $(prefix)/store
|
||||
|
||||
209
src/db.cc
209
src/db.cc
@@ -3,66 +3,172 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <db_cxx.h>
|
||||
|
||||
|
||||
/* Wrapper classes that ensures that the database is closed upon
|
||||
object destruction. */
|
||||
class Db2 : public Db
|
||||
/* Wrapper class to ensure proper destruction. */
|
||||
class DestroyDbc
|
||||
{
|
||||
Dbc * dbc;
|
||||
public:
|
||||
Db2(DbEnv *env, u_int32_t flags) : Db(env, flags) { }
|
||||
~Db2() { close(0); }
|
||||
DestroyDbc(Dbc * _dbc) : dbc(_dbc) { }
|
||||
~DestroyDbc() { dbc->close(); /* close() frees dbc */ }
|
||||
};
|
||||
|
||||
|
||||
class DbcClose
|
||||
{
|
||||
Dbc * cursor;
|
||||
public:
|
||||
DbcClose(Dbc * c) : cursor(c) { }
|
||||
~DbcClose() { cursor->close(); }
|
||||
};
|
||||
|
||||
|
||||
static auto_ptr<Db2> openDB(const string & filename, const string & dbname,
|
||||
bool readonly)
|
||||
{
|
||||
auto_ptr<Db2> db(new Db2(0, 0));
|
||||
|
||||
db->open(filename.c_str(), dbname.c_str(),
|
||||
DB_HASH, readonly ? DB_RDONLY : DB_CREATE, 0666);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
static void rethrow(DbException & e)
|
||||
{
|
||||
throw Error(e.what());
|
||||
}
|
||||
|
||||
|
||||
void createDB(const string & filename, const string & dbname)
|
||||
Transaction::Transaction()
|
||||
: txn(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Transaction::Transaction(Database & db)
|
||||
{
|
||||
db.requireEnv();
|
||||
try {
|
||||
openDB(filename, dbname, false);
|
||||
db.env->txn_begin(0, &txn, 0);
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
bool queryDB(const string & filename, const string & dbname,
|
||||
Transaction::~Transaction()
|
||||
{
|
||||
if (txn) abort();
|
||||
}
|
||||
|
||||
|
||||
void Transaction::commit()
|
||||
{
|
||||
if (!txn) throw Error("commit called on null transaction");
|
||||
debug(format("committing transaction %1%") % (void *) txn);
|
||||
DbTxn * txn2 = txn;
|
||||
txn = 0;
|
||||
try {
|
||||
txn2->commit(0);
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
void Transaction::abort()
|
||||
{
|
||||
if (!txn) throw Error("abort called on null transaction");
|
||||
debug(format("aborting transaction %1%") % (void *) txn);
|
||||
DbTxn * txn2 = txn;
|
||||
txn = 0;
|
||||
try {
|
||||
txn2->abort();
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
void Database::requireEnv()
|
||||
{
|
||||
if (!env) throw Error("database environment not open");
|
||||
}
|
||||
|
||||
|
||||
Db * Database::getDb(TableId table)
|
||||
{
|
||||
map<TableId, Db *>::iterator i = tables.find(table);
|
||||
if (i == tables.end())
|
||||
throw Error("unknown table id");
|
||||
return i->second;
|
||||
}
|
||||
|
||||
|
||||
Database::Database()
|
||||
: env(0)
|
||||
, nextId(1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Database::~Database()
|
||||
{
|
||||
if (env) {
|
||||
debug(format("closing database environment"));
|
||||
|
||||
try {
|
||||
|
||||
for (map<TableId, Db *>::iterator i = tables.begin();
|
||||
i != tables.end(); i++)
|
||||
{
|
||||
debug(format("closing table %1%") % i->first);
|
||||
Db * db = i->second;
|
||||
db->close(0);
|
||||
delete db;
|
||||
}
|
||||
|
||||
env->txn_checkpoint(0, 0, 0);
|
||||
env->close(0);
|
||||
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
|
||||
delete env;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Database::open(const string & path)
|
||||
{
|
||||
try {
|
||||
|
||||
if (env) throw Error(format("environment already open"));
|
||||
|
||||
env = new DbEnv(0);
|
||||
|
||||
env->set_lg_bsize(32 * 1024); /* default */
|
||||
env->set_lg_max(256 * 1024); /* must be > 4 * lg_bsize */
|
||||
env->set_lk_detect(DB_LOCK_DEFAULT);
|
||||
|
||||
env->open(path.c_str(),
|
||||
DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
|
||||
DB_CREATE,
|
||||
0666);
|
||||
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
TableId Database::openTable(const string & tableName)
|
||||
{
|
||||
requireEnv();
|
||||
TableId table = nextId++;
|
||||
|
||||
try {
|
||||
|
||||
Db * db = new Db(env, 0);
|
||||
|
||||
try {
|
||||
// !!! fixme when switching to BDB 4.1: use txn.
|
||||
db->open(tableName.c_str(), 0, DB_HASH, DB_CREATE, 0666);
|
||||
} catch (...) {
|
||||
delete db;
|
||||
throw;
|
||||
}
|
||||
|
||||
tables[table] = db;
|
||||
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
|
||||
bool Database::queryString(const Transaction & txn, TableId table,
|
||||
const string & key, string & data)
|
||||
{
|
||||
try {
|
||||
|
||||
int err;
|
||||
auto_ptr<Db2> db = openDB(filename, dbname, true);
|
||||
Db * db = getDb(table);
|
||||
|
||||
Dbt kt((void *) key.c_str(), key.length());
|
||||
Dbt dt;
|
||||
|
||||
err = db->get(0, &kt, &dt, 0);
|
||||
int err = db->get(txn.txn, &kt, &dt, 0);
|
||||
if (err) return false;
|
||||
|
||||
if (!dt.get_data())
|
||||
@@ -76,12 +182,12 @@ bool queryDB(const string & filename, const string & dbname,
|
||||
}
|
||||
|
||||
|
||||
bool queryListDB(const string & filename, const string & dbname,
|
||||
bool Database::queryStrings(const Transaction & txn, TableId table,
|
||||
const string & key, Strings & data)
|
||||
{
|
||||
string d;
|
||||
|
||||
if (!queryDB(filename, dbname, key, d))
|
||||
if (!queryString(txn, table, key, d))
|
||||
return false;
|
||||
|
||||
string::iterator it = d.begin();
|
||||
@@ -110,19 +216,19 @@ bool queryListDB(const string & filename, const string & dbname,
|
||||
}
|
||||
|
||||
|
||||
void setDB(const string & filename, const string & dbname,
|
||||
void Database::setString(const Transaction & txn, TableId table,
|
||||
const string & key, const string & data)
|
||||
{
|
||||
try {
|
||||
auto_ptr<Db2> db = openDB(filename, dbname, false);
|
||||
Db * db = getDb(table);
|
||||
Dbt kt((void *) key.c_str(), key.length());
|
||||
Dbt dt((void *) data.c_str(), data.length());
|
||||
db->put(0, &kt, &dt, 0);
|
||||
db->put(txn.txn, &kt, &dt, 0);
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
void setListDB(const string & filename, const string & dbname,
|
||||
void Database::setStrings(const Transaction & txn, TableId table,
|
||||
const string & key, const Strings & data)
|
||||
{
|
||||
string d;
|
||||
@@ -141,34 +247,33 @@ void setListDB(const string & filename, const string & dbname,
|
||||
d += s;
|
||||
}
|
||||
|
||||
setDB(filename, dbname, key, d);
|
||||
setString(txn, table, key, d);
|
||||
}
|
||||
|
||||
|
||||
void delDB(const string & filename, const string & dbname,
|
||||
void Database::delPair(const Transaction & txn, TableId table,
|
||||
const string & key)
|
||||
{
|
||||
try {
|
||||
auto_ptr<Db2> db = openDB(filename, dbname, false);
|
||||
Db * db = getDb(table);
|
||||
Dbt kt((void *) key.c_str(), key.length());
|
||||
db->del(0, &kt, 0);
|
||||
db->del(txn.txn, &kt, 0);
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
void enumDB(const string & filename, const string & dbname,
|
||||
void Database::enumTable(const Transaction & txn, TableId table,
|
||||
Strings & keys)
|
||||
{
|
||||
try {
|
||||
Db * db = getDb(table);
|
||||
|
||||
auto_ptr<Db2> db = openDB(filename, dbname, true);
|
||||
|
||||
Dbc * cursor;
|
||||
db->cursor(0, &cursor, 0);
|
||||
DbcClose cursorCloser(cursor);
|
||||
Dbc * dbc;
|
||||
db->cursor(txn.txn, &dbc, 0);
|
||||
DestroyDbc destroyDbc(dbc);
|
||||
|
||||
Dbt kt, dt;
|
||||
while (cursor->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND)
|
||||
while (dbc->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND)
|
||||
keys.push_back(
|
||||
string((char *) kt.get_data(), kt.get_size()));
|
||||
|
||||
|
||||
78
src/db.hh
78
src/db.hh
@@ -3,29 +3,81 @@
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include <db_cxx.h>
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void createDB(const string & filename, const string & dbname);
|
||||
|
||||
bool queryDB(const string & filename, const string & dbname,
|
||||
const string & key, string & data);
|
||||
class Database;
|
||||
|
||||
bool queryListDB(const string & filename, const string & dbname,
|
||||
const string & key, Strings & data);
|
||||
|
||||
void setDB(const string & filename, const string & dbname,
|
||||
const string & key, const string & data);
|
||||
class Transaction
|
||||
{
|
||||
friend class Database;
|
||||
|
||||
void setListDB(const string & filename, const string & dbname,
|
||||
const string & key, const Strings & data);
|
||||
private:
|
||||
DbTxn * txn;
|
||||
|
||||
public:
|
||||
Transaction();
|
||||
Transaction(Database & _db);
|
||||
~Transaction();
|
||||
|
||||
void delDB(const string & filename, const string & dbname,
|
||||
const string & key);
|
||||
void abort();
|
||||
void commit();
|
||||
};
|
||||
|
||||
|
||||
#define noTxn Transaction()
|
||||
|
||||
|
||||
typedef unsigned int TableId; /* table handles */
|
||||
|
||||
|
||||
class Database
|
||||
{
|
||||
friend class Transaction;
|
||||
|
||||
private:
|
||||
DbEnv * env;
|
||||
|
||||
TableId nextId;
|
||||
map<TableId, Db *> tables;
|
||||
|
||||
void requireEnv();
|
||||
|
||||
Db * getDb(TableId table);
|
||||
|
||||
public:
|
||||
Database();
|
||||
~Database();
|
||||
|
||||
void open(const string & path);
|
||||
|
||||
TableId openTable(const string & table);
|
||||
|
||||
bool queryString(const Transaction & txn, TableId table,
|
||||
const string & key, string & data);
|
||||
|
||||
bool queryStrings(const Transaction & txn, TableId table,
|
||||
const string & key, Strings & data);
|
||||
|
||||
void setString(const Transaction & txn, TableId table,
|
||||
const string & key, const string & data);
|
||||
|
||||
void setStrings(const Transaction & txn, TableId table,
|
||||
const string & key, const Strings & data);
|
||||
|
||||
void delPair(const Transaction & txn, TableId table,
|
||||
const string & key);
|
||||
|
||||
void enumTable(const Transaction & txn, TableId table,
|
||||
Strings & keys);
|
||||
};
|
||||
|
||||
void enumDB(const string & filename, const string & dbname,
|
||||
Strings & keys);
|
||||
|
||||
#endif /* !__DB_H */
|
||||
|
||||
49
src/exec.cc
49
src/exec.cc
@@ -34,8 +34,12 @@ public:
|
||||
};
|
||||
|
||||
|
||||
static string pathNullDevice = "/dev/null";
|
||||
|
||||
|
||||
/* Run a program. */
|
||||
void runProgram(const string & program, Environment env)
|
||||
void runProgram(const string & program,
|
||||
const Strings & args, const Environment & env)
|
||||
{
|
||||
/* Create a log file. */
|
||||
string logFileName = nixLogDir + "/run.log";
|
||||
@@ -68,15 +72,25 @@ void runProgram(const string & program, Environment env)
|
||||
if (chdir(tmpDir.c_str()) == -1)
|
||||
throw SysError(format("changing into to `%1%'") % tmpDir);
|
||||
|
||||
/* Fill in the environment. We don't bother freeing
|
||||
the strings, since we'll exec or die soon
|
||||
anyway. */
|
||||
const char * env2[env.size() + 1];
|
||||
int i = 0;
|
||||
for (Environment::iterator it = env.begin();
|
||||
it != env.end(); it++, i++)
|
||||
env2[i] = (new string(it->first + "=" + it->second))->c_str();
|
||||
env2[i] = 0;
|
||||
/* Fill in the arguments. */
|
||||
const char * argArr[args.size() + 2];
|
||||
const char * * p = argArr;
|
||||
string progName = baseNameOf(program);
|
||||
*p++ = progName.c_str();
|
||||
for (Strings::const_iterator i = args.begin();
|
||||
i != args.end(); i++)
|
||||
*p++ = i->c_str();
|
||||
*p = 0;
|
||||
|
||||
/* Fill in the environment. */
|
||||
Strings envStrs;
|
||||
const char * envArr[env.size() + 1];
|
||||
p = envArr;
|
||||
for (Environment::const_iterator i = env.begin();
|
||||
i != env.end(); i++)
|
||||
*p++ = envStrs.insert(envStrs.end(),
|
||||
i->first + "=" + i->second)->c_str();
|
||||
*p = 0;
|
||||
|
||||
/* Dup the log handle into stderr. */
|
||||
if (dup2(fileno(logFile), STDERR_FILENO) == -1)
|
||||
@@ -86,8 +100,15 @@ void runProgram(const string & program, Environment env)
|
||||
if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
|
||||
throw SysError("cannot dup stderr into stdout");
|
||||
|
||||
/* Reroute stdin to /dev/null. */
|
||||
int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
|
||||
if (fdDevNull == -1)
|
||||
throw SysError(format("cannot open `%1%'") % pathNullDevice);
|
||||
if (dup2(fdDevNull, STDIN_FILENO) == -1)
|
||||
throw SysError("cannot dup null device into stdin");
|
||||
|
||||
/* Execute the program. This should not return. */
|
||||
execle(program.c_str(), baseNameOf(program).c_str(), 0, env2);
|
||||
execve(program.c_str(), (char * *) argArr, (char * *) envArr);
|
||||
|
||||
throw SysError(format("unable to execute %1%") % program);
|
||||
|
||||
@@ -111,7 +132,11 @@ void runProgram(const string & program, Environment env)
|
||||
throw Error("unable to wait for child");
|
||||
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
delTmpDir.cancel();
|
||||
if (keepFailed) {
|
||||
msg(lvlTalkative,
|
||||
format("build failed; keeping build directory `%1%'") % tmpDir);
|
||||
delTmpDir.cancel();
|
||||
}
|
||||
throw Error("unable to build package");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
@@ -12,7 +14,8 @@ typedef map<string, string> Environment;
|
||||
|
||||
|
||||
/* Run a program. */
|
||||
void runProgram(const string & program, Environment env);
|
||||
void runProgram(const string & program,
|
||||
const Strings & args, const Environment & env);
|
||||
|
||||
|
||||
#endif /* !__EXEC_H */
|
||||
|
||||
196
src/fix.cc
196
src/fix.cc
@@ -9,13 +9,22 @@
|
||||
typedef ATerm Expr;
|
||||
|
||||
typedef map<ATerm, ATerm> NormalForms;
|
||||
typedef map<FSId, Strings> PkgPaths;
|
||||
typedef map<FSId, Hash> PkgHashes;
|
||||
|
||||
struct EvalState
|
||||
{
|
||||
Strings searchDirs;
|
||||
NormalForms normalForms;
|
||||
PkgPaths pkgPaths;
|
||||
PkgHashes pkgHashes; /* normalised package hashes */
|
||||
Expr blackHole;
|
||||
|
||||
EvalState()
|
||||
{
|
||||
blackHole = ATmake("BlackHole()");
|
||||
if (!blackHole) throw Error("cannot build black hole");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -51,10 +60,16 @@ static Expr substExpr(string x, Expr rep, Expr e)
|
||||
else
|
||||
return e;
|
||||
|
||||
if (ATmatch(e, "Lam(<str>, <term>)", &s, &e2))
|
||||
if (x == s)
|
||||
return e;
|
||||
/* !!! unfair substitutions */
|
||||
ATermList formals;
|
||||
if (ATmatch(e, "Function([<list>], <term>)", &formals, &e2)) {
|
||||
while (!ATisEmpty(formals)) {
|
||||
if (!ATmatch(ATgetFirst(formals), "<str>", &s))
|
||||
throw badTerm("not a list of formals", (ATerm) formals);
|
||||
if (x == (string) s)
|
||||
return e;
|
||||
formals = ATgetNext(formals);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generically substitute in subterms. */
|
||||
|
||||
@@ -106,7 +121,20 @@ static Expr substExprMany(ATermList formals, ATermList args, Expr body)
|
||||
}
|
||||
|
||||
|
||||
Hash hashPackage(EvalState & state, FState fs)
|
||||
static Strings fstatePathsCached(EvalState & state, const FSId & id)
|
||||
{
|
||||
PkgPaths::iterator i = state.pkgPaths.find(id);
|
||||
if (i != state.pkgPaths.end())
|
||||
return i->second;
|
||||
else {
|
||||
Strings paths = fstatePaths(id);
|
||||
state.pkgPaths[id] = paths;
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Hash hashPackage(EvalState & state, FState fs)
|
||||
{
|
||||
if (fs.type == FState::fsDerive) {
|
||||
for (FSIds::iterator i = fs.derive.inputs.begin();
|
||||
@@ -122,6 +150,42 @@ Hash hashPackage(EvalState & state, FState fs)
|
||||
}
|
||||
|
||||
|
||||
static string processBinding(EvalState & state, Expr e, FState & fs)
|
||||
{
|
||||
char * s1;
|
||||
|
||||
if (ATmatch(e, "FSId(<str>)", &s1)) {
|
||||
FSId id = parseHash(s1);
|
||||
Strings paths = fstatePathsCached(state, id);
|
||||
if (paths.size() != 1) abort();
|
||||
string path = *(paths.begin());
|
||||
fs.derive.inputs.push_back(id);
|
||||
return path;
|
||||
}
|
||||
|
||||
if (ATmatch(e, "<str>", &s1))
|
||||
return s1;
|
||||
|
||||
if (ATmatch(e, "True")) return "1";
|
||||
|
||||
if (ATmatch(e, "False")) return "";
|
||||
|
||||
ATermList l;
|
||||
if (ATmatch(e, "[<list>]", &l)) {
|
||||
string s;
|
||||
bool first = true;
|
||||
while (!ATisEmpty(l)) {
|
||||
if (!first) s = s + " "; else first = false;
|
||||
s += processBinding(state, evalExpr(state, ATgetFirst(l)), fs);
|
||||
l = ATgetNext(l);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
throw badTerm("invalid package binding", e);
|
||||
}
|
||||
|
||||
|
||||
static Expr evalExpr2(EvalState & state, Expr e)
|
||||
{
|
||||
char * s1;
|
||||
@@ -130,6 +194,9 @@ static Expr evalExpr2(EvalState & state, Expr e)
|
||||
|
||||
/* Normal forms. */
|
||||
if (ATmatch(e, "<str>", &s1) ||
|
||||
ATmatch(e, "[<list>]", &e1) ||
|
||||
ATmatch(e, "True") ||
|
||||
ATmatch(e, "False") ||
|
||||
ATmatch(e, "Function([<list>], <term>)", &e1, &e2) ||
|
||||
ATmatch(e, "FSId(<str>)", &s1))
|
||||
return e;
|
||||
@@ -143,7 +210,8 @@ static Expr evalExpr2(EvalState & state, Expr e)
|
||||
}
|
||||
|
||||
/* Application. */
|
||||
if (ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) {
|
||||
if (ATmatch(e, "Call(<term>, [<list>])", &e1, &e2) ||
|
||||
ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) {
|
||||
e1 = evalExpr(state, e1);
|
||||
if (!ATmatch(e1, "Function([<list>], <term>)", &e3, &e4))
|
||||
throw badTerm("expecting a function", e1);
|
||||
@@ -151,6 +219,37 @@ static Expr evalExpr2(EvalState & state, Expr e)
|
||||
substExprMany((ATermList) e3, (ATermList) e2, e4));
|
||||
}
|
||||
|
||||
/* Conditional. */
|
||||
if (ATmatch(e, "If(<term>, <term>, <term>)", &e1, &e2, &e3)) {
|
||||
e1 = evalExpr(state, e1);
|
||||
Expr x;
|
||||
if (ATmatch(e1, "True")) x = e2;
|
||||
else if (ATmatch(e1, "False")) x = e3;
|
||||
else throw badTerm("expecting a boolean", e1);
|
||||
return evalExpr(state, x);
|
||||
}
|
||||
|
||||
/* Ad-hoc function for string matching. */
|
||||
if (ATmatch(e, "HasSubstr(<term>, <term>)", &e1, &e2)) {
|
||||
e1 = evalExpr(state, e1);
|
||||
e2 = evalExpr(state, e2);
|
||||
|
||||
char * s1, * s2;
|
||||
if (!ATmatch(e1, "<str>", &s1))
|
||||
throw badTerm("expecting a string", e1);
|
||||
if (!ATmatch(e2, "<str>", &s2))
|
||||
throw badTerm("expecting a string", e2);
|
||||
|
||||
return
|
||||
string(s1).find(string(s2)) != string::npos ?
|
||||
ATmake("True") : ATmake("False");
|
||||
}
|
||||
|
||||
/* Platform constant. */
|
||||
if (ATmatch(e, "Platform")) {
|
||||
return ATmake("<str>", SYSTEM);
|
||||
}
|
||||
|
||||
/* Fix inclusion. */
|
||||
if (ATmatch(e, "IncludeFix(<str>)", &s1)) {
|
||||
string fileName(s1);
|
||||
@@ -211,24 +310,29 @@ static Expr evalExpr2(EvalState & state, Expr e)
|
||||
string key = it->first;
|
||||
ATerm value = it->second;
|
||||
|
||||
if (ATmatch(value, "FSId(<str>)", &s1)) {
|
||||
FSId id = parseHash(s1);
|
||||
Strings paths = fstatePaths(id);
|
||||
if (paths.size() != 1) abort();
|
||||
string path = *(paths.begin());
|
||||
fs.derive.inputs.push_back(id);
|
||||
fs.derive.env.push_back(StringPair(key, path));
|
||||
if (key == "build") fs.derive.builder = path;
|
||||
}
|
||||
else if (ATmatch(value, "<str>", &s1)) {
|
||||
if (key == "name") name = s1;
|
||||
if (key == "args") {
|
||||
ATermList args;
|
||||
if (!ATmatch(value, "[<list>]", &args))
|
||||
throw badTerm("list expected", value);
|
||||
|
||||
while (!ATisEmpty(args)) {
|
||||
Expr arg = evalExpr(state, ATgetFirst(args));
|
||||
fs.derive.args.push_back(processBinding(state, arg, fs));
|
||||
args = ATgetNext(args);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
string s = processBinding(state, value, fs);
|
||||
fs.derive.env.push_back(StringPair(key, s));
|
||||
|
||||
if (key == "build") fs.derive.builder = s;
|
||||
if (key == "name") name = s;
|
||||
if (key == "id") {
|
||||
outId = parseHash(s1);
|
||||
outId = parseHash(s);
|
||||
outIdGiven = true;
|
||||
}
|
||||
fs.derive.env.push_back(StringPair(key, s1));
|
||||
}
|
||||
else throw badTerm("invalid package argument", value);
|
||||
|
||||
bnds = ATinsert(bnds,
|
||||
ATmake("(<str>, <term>)", key.c_str(), value));
|
||||
@@ -276,12 +380,19 @@ static Expr evalExpr2(EvalState & state, Expr e)
|
||||
|
||||
static Expr evalExpr(EvalState & state, Expr e)
|
||||
{
|
||||
Nest nest(lvlVomit, format("evaluating expression: %1%") % printTerm(e));
|
||||
|
||||
/* Consult the memo table to quickly get the normal form of
|
||||
previously evaluated expressions. */
|
||||
NormalForms::iterator i = state.normalForms.find(e);
|
||||
if (i != state.normalForms.end()) return i->second;
|
||||
if (i != state.normalForms.end()) {
|
||||
if (i->second == state.blackHole)
|
||||
throw badTerm("infinite recursion", e);
|
||||
return i->second;
|
||||
}
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
state.normalForms[e] = state.blackHole;
|
||||
Expr nf = evalExpr2(state, e);
|
||||
state.normalForms[e] = nf;
|
||||
return nf;
|
||||
@@ -299,10 +410,40 @@ static Expr evalFile(EvalState & state, string relPath)
|
||||
}
|
||||
|
||||
|
||||
static Expr evalStdin(EvalState & state)
|
||||
{
|
||||
Nest nest(lvlTalkative, format("evaluating standard input"));
|
||||
Expr e = ATreadFromFile(stdin);
|
||||
if (!e)
|
||||
throw Error(format("unable to read a term from stdin"));
|
||||
return evalExpr(state, e);
|
||||
}
|
||||
|
||||
|
||||
static void printFSId(EvalState & state, Expr e)
|
||||
{
|
||||
ATermList es;
|
||||
char * s;
|
||||
if (ATmatch(e, "FSId(<str>)", &s)) {
|
||||
cout << format("%1%\n") % s;
|
||||
}
|
||||
else if (ATmatch(e, "[<list>]", &es)) {
|
||||
while (!ATisEmpty(es)) {
|
||||
printFSId(state, evalExpr(state, ATgetFirst(es)));
|
||||
es = ATgetNext(es);
|
||||
}
|
||||
}
|
||||
else throw badTerm("top level does not evaluate to a (list of) Nix expression(s)", e);
|
||||
}
|
||||
|
||||
|
||||
void run(Strings args)
|
||||
{
|
||||
openDB();
|
||||
|
||||
EvalState state;
|
||||
Strings files;
|
||||
bool readStdin = false;
|
||||
|
||||
state.searchDirs.push_back(".");
|
||||
state.searchDirs.push_back(nixDataDir + "/fix");
|
||||
@@ -319,23 +460,24 @@ void run(Strings args)
|
||||
}
|
||||
else if (arg == "--verbose" || arg == "-v")
|
||||
verbosity = (Verbosity) ((int) verbosity + 1);
|
||||
else if (arg == "-")
|
||||
readStdin = true;
|
||||
else if (arg[0] == '-')
|
||||
throw UsageError(format("unknown flag `%1%`") % arg);
|
||||
else
|
||||
files.push_back(arg);
|
||||
}
|
||||
|
||||
if (files.empty()) throw UsageError("no files specified");
|
||||
if (readStdin) {
|
||||
Expr e = evalStdin(state);
|
||||
printFSId(state, e);
|
||||
}
|
||||
|
||||
for (Strings::iterator it = files.begin();
|
||||
it != files.end(); it++)
|
||||
{
|
||||
Expr e = evalFile(state, *it);
|
||||
char * s;
|
||||
if (ATmatch(e, "FSId(<str>)", &s)) {
|
||||
cout << format("%1%\n") % s;
|
||||
}
|
||||
else throw badTerm("top level is not a package", e);
|
||||
printFSId(state, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ FSId writeTerm(ATerm t, const string & suffix, FSId id)
|
||||
// debug(format("written term %1% = %2%") % (string) id %
|
||||
// printTerm(t));
|
||||
|
||||
registerPath(path, id);
|
||||
Transaction txn(nixDB);
|
||||
registerPath(txn, path, id);
|
||||
txn.commit();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -117,13 +120,19 @@ static bool parseSlice(ATerm t, Slice & slice)
|
||||
|
||||
static bool parseDerive(ATerm t, Derive & derive)
|
||||
{
|
||||
ATermList outs, ins, bnds;
|
||||
ATermList outs, ins, args, bnds;
|
||||
char * builder;
|
||||
char * platform;
|
||||
|
||||
if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>])",
|
||||
&outs, &ins, &builder, &platform, &bnds))
|
||||
return false;
|
||||
if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>], [<list>])",
|
||||
&outs, &ins, &platform, &builder, &args, &bnds))
|
||||
{
|
||||
/* !!! compatibility -> remove eventually */
|
||||
if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>])",
|
||||
&outs, &ins, &builder, &platform, &bnds))
|
||||
return false;
|
||||
args = ATempty;
|
||||
}
|
||||
|
||||
while (!ATisEmpty(outs)) {
|
||||
char * s1, * s2;
|
||||
@@ -139,6 +148,15 @@ static bool parseDerive(ATerm t, Derive & derive)
|
||||
derive.builder = builder;
|
||||
derive.platform = platform;
|
||||
|
||||
while (!ATisEmpty(args)) {
|
||||
char * s;
|
||||
ATerm arg = ATgetFirst(args);
|
||||
if (!ATmatch(arg, "<str>", &s))
|
||||
throw badTerm("string expected", arg);
|
||||
derive.args.push_back(s);
|
||||
args = ATgetNext(args);
|
||||
}
|
||||
|
||||
while (!ATisEmpty(bnds)) {
|
||||
char * s1, * s2;
|
||||
ATerm bnd = ATgetFirst(bnds);
|
||||
@@ -201,6 +219,11 @@ static ATerm unparseDerive(const Derive & derive)
|
||||
ATmake("(<str>, <str>)",
|
||||
i->first.c_str(), ((string) i->second).c_str()));
|
||||
|
||||
ATermList args = ATempty;
|
||||
for (Strings::const_iterator i = derive.args.begin();
|
||||
i != derive.args.end(); i++)
|
||||
args = ATinsert(args, ATmake("<str>", i->c_str()));
|
||||
|
||||
ATermList env = ATempty;
|
||||
for (StringPairs::const_iterator i = derive.env.begin();
|
||||
i != derive.env.end(); i++)
|
||||
@@ -208,11 +231,12 @@ static ATerm unparseDerive(const Derive & derive)
|
||||
ATmake("(<str>, <str>)",
|
||||
i->first.c_str(), i->second.c_str()));
|
||||
|
||||
return ATmake("Derive(<term>, <term>, <str>, <str>, <term>)",
|
||||
return ATmake("Derive(<term>, <term>, <str>, <str>, <term>, <term>)",
|
||||
ATreverse(outs),
|
||||
unparseIds(derive.inputs),
|
||||
derive.builder.c_str(),
|
||||
derive.platform.c_str(),
|
||||
derive.builder.c_str(),
|
||||
ATreverse(args),
|
||||
ATreverse(env));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,9 @@ struct Derive
|
||||
{
|
||||
DeriveOutputs outputs;
|
||||
FSIds inputs;
|
||||
string builder;
|
||||
string platform;
|
||||
string builder;
|
||||
Strings args;
|
||||
StringPairs env;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,22 +2,34 @@
|
||||
#include "db.hh"
|
||||
|
||||
|
||||
string dbPath2Id = "path2id";
|
||||
string dbId2Paths = "id2paths";
|
||||
string dbSuccessors = "successors";
|
||||
string dbSubstitutes = "substitutes";
|
||||
Database nixDB;
|
||||
|
||||
|
||||
TableId dbPath2Id;
|
||||
TableId dbId2Paths;
|
||||
TableId dbSuccessors;
|
||||
TableId dbSubstitutes;
|
||||
|
||||
|
||||
string nixStore = "/UNINIT";
|
||||
string nixDataDir = "/UNINIT";
|
||||
string nixLogDir = "/UNINIT";
|
||||
string nixDB = "/UNINIT";
|
||||
string nixDBPath = "/UNINIT";
|
||||
|
||||
|
||||
bool keepFailed = false;
|
||||
|
||||
|
||||
void openDB()
|
||||
{
|
||||
nixDB.open(nixDBPath);
|
||||
dbPath2Id = nixDB.openTable("path2id");
|
||||
dbId2Paths = nixDB.openTable("id2paths");
|
||||
dbSuccessors = nixDB.openTable("successors");
|
||||
dbSubstitutes = nixDB.openTable("substitutes");
|
||||
}
|
||||
|
||||
|
||||
void initDB()
|
||||
{
|
||||
createDB(nixDB, dbPath2Id);
|
||||
createDB(nixDB, dbId2Paths);
|
||||
createDB(nixDB, dbSuccessors);
|
||||
createDB(nixDB, dbSubstitutes);
|
||||
}
|
||||
|
||||
@@ -3,22 +3,27 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "db.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/* Database names. */
|
||||
extern Database nixDB;
|
||||
|
||||
|
||||
/* Database tables. */
|
||||
|
||||
/* dbPath2Id :: Path -> FSId
|
||||
|
||||
Each pair (p, id) records that path $p$ contains an expansion of
|
||||
$id$. */
|
||||
extern string dbPath2Id;
|
||||
extern TableId dbPath2Id;
|
||||
|
||||
|
||||
/* dbId2Paths :: FSId -> [Path]
|
||||
|
||||
A mapping from ids to lists of paths. */
|
||||
extern string dbId2Paths;
|
||||
extern TableId dbId2Paths;
|
||||
|
||||
|
||||
/* dbSuccessors :: FSId -> FSId
|
||||
@@ -30,7 +35,7 @@ extern string dbId2Paths;
|
||||
Note that a term $y$ is successor of $x$ iff there exists a
|
||||
sequence of rewrite steps that rewrites $x$ into $y$.
|
||||
*/
|
||||
extern string dbSuccessors;
|
||||
extern TableId dbSuccessors;
|
||||
|
||||
|
||||
/* dbSubstitutes :: FSId -> [FSId]
|
||||
@@ -46,7 +51,7 @@ extern string dbSuccessors;
|
||||
this case might be an fstate expression that fetches the Nix
|
||||
archive.
|
||||
*/
|
||||
extern string dbSubstitutes;
|
||||
extern TableId dbSubstitutes;
|
||||
|
||||
|
||||
/* Path names. */
|
||||
@@ -60,12 +65,20 @@ extern string nixDataDir; /* !!! fix */
|
||||
/* nixLogDir is the directory where we log various operations. */
|
||||
extern string nixLogDir;
|
||||
|
||||
/* nixDB is the file name of the Berkeley DB database where we
|
||||
maintain the dbXXX mappings. */
|
||||
extern string nixDB;
|
||||
/* nixDBPath is the path name of our Berkeley DB environment. */
|
||||
extern string nixDBPath;
|
||||
|
||||
|
||||
/* Initialize the databases. */
|
||||
/* Misc. global flags. */
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
extern bool keepFailed;
|
||||
|
||||
|
||||
/* Open the database environment. */
|
||||
void openDB();
|
||||
|
||||
/* Create the required database tables. */
|
||||
void initDB();
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,13 @@
|
||||
|
||||
void run(Strings args)
|
||||
{
|
||||
for (Strings::iterator it = args.begin();
|
||||
it != args.end(); it++)
|
||||
cout << format("%1%\n") % (string) hashPath(*it);
|
||||
bool flat = false;
|
||||
for (Strings::iterator i = args.begin();
|
||||
i != args.end(); i++)
|
||||
if (*i == "--flat") flat = true;
|
||||
else
|
||||
cout << format("%1%\n") % (string)
|
||||
(flat ? hashFile(*i) : hashPath(*i));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,3 +34,4 @@ Query flags:
|
||||
Options:
|
||||
|
||||
--verbose / -v: verbose operation (may be repeated)
|
||||
--keep-failed / -K: keep temporary directories of failed builds
|
||||
|
||||
10
src/nix.cc
10
src/nix.cc
@@ -239,14 +239,16 @@ static void opSuccessor(Strings opFlags, Strings opArgs)
|
||||
{
|
||||
if (!opFlags.empty()) throw UsageError("unknown flag");
|
||||
if (opArgs.size() % 2) throw UsageError("expecting even number of arguments");
|
||||
|
||||
|
||||
Transaction txn(nixDB); /* !!! this could be a big transaction */
|
||||
for (Strings::iterator i = opArgs.begin();
|
||||
i != opArgs.end(); )
|
||||
{
|
||||
FSId id1 = parseHash(*i++);
|
||||
FSId id2 = parseHash(*i++);
|
||||
registerSuccessor(id1, id2);
|
||||
registerSuccessor(txn, id1, id2);
|
||||
}
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -335,6 +337,8 @@ static void opVerify(Strings opFlags, Strings opArgs)
|
||||
list. */
|
||||
void run(Strings args)
|
||||
{
|
||||
openDB();
|
||||
|
||||
Strings opFlags, opArgs;
|
||||
Operation op = 0;
|
||||
|
||||
@@ -368,6 +372,8 @@ void run(Strings args)
|
||||
pathArgs = true;
|
||||
else if (arg == "--verbose" || arg == "-v")
|
||||
verbosity = (Verbosity) ((int) verbosity + 1);
|
||||
else if (arg == "--keep-failed" || arg == "-K")
|
||||
keepFailed = true;
|
||||
else if (arg == "--help")
|
||||
printHelp();
|
||||
else if (arg[0] == '-')
|
||||
|
||||
268
src/normalise.cc
268
src/normalise.cc
@@ -4,24 +4,40 @@
|
||||
#include "references.hh"
|
||||
#include "db.hh"
|
||||
#include "exec.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
|
||||
void registerSuccessor(const FSId & id1, const FSId & id2)
|
||||
void registerSuccessor(const Transaction & txn,
|
||||
const FSId & id1, const FSId & id2)
|
||||
{
|
||||
setDB(nixDB, dbSuccessors, id1, id2);
|
||||
nixDB.setString(txn, dbSuccessors, id1, id2);
|
||||
}
|
||||
|
||||
|
||||
static FSId storeSuccessor(const FSId & id1, ATerm sc)
|
||||
static FSId useSuccessor(const FSId & id)
|
||||
{
|
||||
FSId id2 = writeTerm(sc, "-s-" + (string) id1);
|
||||
registerSuccessor(id1, id2);
|
||||
return id2;
|
||||
string idSucc;
|
||||
if (nixDB.queryString(noTxn, dbSuccessors, id, idSucc)) {
|
||||
debug(format("successor %1% -> %2%") % (string) id % idSucc);
|
||||
return parseHash(idSucc);
|
||||
} else
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
typedef set<FSId> FSIdSet;
|
||||
typedef map<string, FSId> OutPaths;
|
||||
typedef map<string, SliceElem> ElemMap;
|
||||
|
||||
|
||||
Strings pathsFromOutPaths(const OutPaths & ps)
|
||||
{
|
||||
Strings ss;
|
||||
for (OutPaths::const_iterator i = ps.begin();
|
||||
i != ps.end(); i++)
|
||||
ss.push_back(i->first);
|
||||
return ss;
|
||||
}
|
||||
|
||||
|
||||
FSId normaliseFState(FSId id, FSIdSet pending)
|
||||
@@ -30,19 +46,66 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||
|
||||
/* Try to substitute $id$ by any known successors in order to
|
||||
speed up the rewrite process. */
|
||||
string idSucc;
|
||||
while (queryDB(nixDB, dbSuccessors, id, idSucc)) {
|
||||
debug(format("successor %1% -> %2%") % (string) id % idSucc);
|
||||
id = parseHash(idSucc);
|
||||
}
|
||||
id = useSuccessor(id);
|
||||
|
||||
/* Get the fstate expression. */
|
||||
FState fs = parseFState(termFromId(id));
|
||||
|
||||
/* It this is a normal form (i.e., a slice) we are done. */
|
||||
/* If this is a normal form (i.e., a slice) we are done. */
|
||||
if (fs.type == FState::fsSlice) return id;
|
||||
if (fs.type != FState::fsDerive) abort();
|
||||
|
||||
/* Otherwise, it's a derivation. */
|
||||
|
||||
/* Otherwise, it's a derive expression, and we have to build it to
|
||||
determine its normal form. */
|
||||
|
||||
|
||||
/* Some variables. */
|
||||
|
||||
/* Output paths, with their ids. */
|
||||
OutPaths outPaths;
|
||||
|
||||
/* Input paths, with their slice elements. */
|
||||
ElemMap inMap;
|
||||
|
||||
/* Referencable paths (i.e., input and output paths). */
|
||||
Strings allPaths;
|
||||
|
||||
/* The environment to be passed to the builder. */
|
||||
Environment env;
|
||||
|
||||
|
||||
/* Parse the outputs. */
|
||||
for (DeriveOutputs::iterator i = fs.derive.outputs.begin();
|
||||
i != fs.derive.outputs.end(); i++)
|
||||
{
|
||||
debug(format("building %1% in `%2%'") % (string) i->second % i->first);
|
||||
outPaths[i->first] = i->second;
|
||||
allPaths.push_back(i->first);
|
||||
}
|
||||
|
||||
/* Obtain locks on all output paths. The locks are automatically
|
||||
released when we exit this function or Nix crashes. */
|
||||
PathLocks outputLocks(pathsFromOutPaths(outPaths));
|
||||
|
||||
/* Now check again whether there is a successor. This is because
|
||||
another process may have started building in parallel. After
|
||||
it has finished and released the locks, we can (and should)
|
||||
reuse its results. (Strictly speaking the first successor
|
||||
check above can be omitted, but that would be less efficient.)
|
||||
Note that since we now hold the locks on the output paths, no
|
||||
other process can build this expression, so no further checks
|
||||
are necessary. */
|
||||
{
|
||||
FSId id2 = useSuccessor(id);
|
||||
if (id2 != id) {
|
||||
FState fs = parseFState(termFromId(id2));
|
||||
debug(format("skipping build of %1%, someone beat us to it")
|
||||
% (string) id);
|
||||
if (fs.type != FState::fsSlice) abort();
|
||||
return id2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Right platform? */
|
||||
if (fs.derive.platform != thisSystem)
|
||||
@@ -50,14 +113,12 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||
% fs.derive.platform % thisSystem);
|
||||
|
||||
/* Realise inputs (and remember all input paths). */
|
||||
typedef map<string, SliceElem> ElemMap;
|
||||
|
||||
ElemMap inMap;
|
||||
|
||||
for (FSIds::iterator i = fs.derive.inputs.begin();
|
||||
i != fs.derive.inputs.end(); i++) {
|
||||
FSId nf = normaliseFState(*i, pending);
|
||||
realiseSlice(nf, pending);
|
||||
/* !!! nf should be a root of the garbage collector while we
|
||||
are building */
|
||||
FState fs = parseFState(termFromId(nf));
|
||||
if (fs.type != FState::fsSlice) abort();
|
||||
for (SliceElems::iterator j = fs.slice.elems.begin();
|
||||
@@ -65,38 +126,30 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||
inMap[j->path] = *j;
|
||||
}
|
||||
|
||||
Strings inPaths;
|
||||
for (ElemMap::iterator i = inMap.begin(); i != inMap.end(); i++)
|
||||
inPaths.push_back(i->second.path);
|
||||
allPaths.push_back(i->second.path);
|
||||
|
||||
/* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
|
||||
PATH is not set. We don't want this, so we fill it in with some dummy
|
||||
value. */
|
||||
env["PATH"] = "/path-not-set";
|
||||
|
||||
/* Build the environment. */
|
||||
Environment env;
|
||||
for (StringPairs::iterator i = fs.derive.env.begin();
|
||||
i != fs.derive.env.end(); i++)
|
||||
env[i->first] = i->second;
|
||||
|
||||
/* Parse the outputs. */
|
||||
typedef map<string, FSId> OutPaths;
|
||||
OutPaths outPaths;
|
||||
for (DeriveOutputs::iterator i = fs.derive.outputs.begin();
|
||||
i != fs.derive.outputs.end(); i++)
|
||||
{
|
||||
debug(format("building %1% in `%2%'") % (string) i->second % i->first);
|
||||
outPaths[i->first] = i->second;
|
||||
inPaths.push_back(i->first);
|
||||
}
|
||||
|
||||
/* We can skip running the builder if we can expand all output
|
||||
paths from their ids. */
|
||||
bool fastBuild = true;
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
{
|
||||
try {
|
||||
expandId(i->second, i->first, "/", pending);
|
||||
} catch (Error & e) {
|
||||
debug(format("fast build failed for `%1%': %2%")
|
||||
% i->first % e.what());
|
||||
% i->first % e.what());
|
||||
fastBuild = false;
|
||||
break;
|
||||
}
|
||||
@@ -104,73 +157,139 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||
|
||||
if (!fastBuild) {
|
||||
|
||||
/* Check that none of the outputs exist. */
|
||||
/* If any of the outputs already exist but are not registered,
|
||||
delete them. */
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
if (pathExists(i->first))
|
||||
throw Error(format("path `%1%' exists") % i->first);
|
||||
{
|
||||
string path = i->first;
|
||||
FSId id;
|
||||
if (queryPathId(path, id))
|
||||
throw Error(format("obstructed build: path `%1%' exists") % path);
|
||||
if (pathExists(path)) {
|
||||
debug(format("removing unregistered path `%1%'") % path);
|
||||
deletePath(path);
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the builder. */
|
||||
msg(lvlChatty, format("building..."));
|
||||
runProgram(fs.derive.builder, env);
|
||||
runProgram(fs.derive.builder, fs.derive.args, env);
|
||||
msg(lvlChatty, format("build completed"));
|
||||
|
||||
} else
|
||||
msg(lvlChatty, format("fast build succesful"));
|
||||
|
||||
/* Check whether the output paths were created, and register each
|
||||
one. */
|
||||
FSIdSet used;
|
||||
/* Check whether the output paths were created, and grep each
|
||||
output path to determine what other paths it references. */
|
||||
StringSet usedPaths;
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
{
|
||||
string path = i->first;
|
||||
if (!pathExists(path))
|
||||
throw Error(format("path `%1%' does not exist") % path);
|
||||
registerPath(path, i->second);
|
||||
fs.slice.roots.push_back(i->second);
|
||||
|
||||
Strings refs = filterReferences(path, inPaths);
|
||||
/* For this output path, find the references to other paths contained
|
||||
in it. */
|
||||
Strings refPaths = filterReferences(path, allPaths);
|
||||
|
||||
/* Construct a slice element for this output path. */
|
||||
SliceElem elem;
|
||||
elem.path = path;
|
||||
elem.id = i->second;
|
||||
|
||||
for (Strings::iterator j = refs.begin(); j != refs.end(); j++) {
|
||||
/* For each path referenced by this output path, add its id to the
|
||||
slice element and add the id to the `used' set (so that the
|
||||
elements referenced by *its* slice are added below). */
|
||||
for (Strings::iterator j = refPaths.begin();
|
||||
j != refPaths.end(); j++)
|
||||
{
|
||||
string path = *j;
|
||||
ElemMap::iterator k;
|
||||
OutPaths::iterator l;
|
||||
if ((k = inMap.find(*j)) != inMap.end()) {
|
||||
|
||||
/* Is it an input path? */
|
||||
if ((k = inMap.find(path)) != inMap.end()) {
|
||||
elem.refs.push_back(k->second.id);
|
||||
used.insert(k->second.id);
|
||||
for (FSIds::iterator m = k->second.refs.begin();
|
||||
m != k->second.refs.end(); m++)
|
||||
used.insert(*m);
|
||||
} else if ((l = outPaths.find(*j)) != outPaths.end()) {
|
||||
usedPaths.insert(k->second.path);
|
||||
}
|
||||
|
||||
/* Or an output path? */
|
||||
else if ((l = outPaths.find(path)) != outPaths.end())
|
||||
elem.refs.push_back(l->second);
|
||||
used.insert(l->second);
|
||||
} else
|
||||
throw Error(format("unknown referenced path `%1%'") % *j);
|
||||
|
||||
/* Can't happen. */
|
||||
else abort();
|
||||
}
|
||||
|
||||
fs.slice.elems.push_back(elem);
|
||||
}
|
||||
|
||||
/* Close the slice. That is, for any referenced path, add the paths
|
||||
referenced by it. */
|
||||
FSIdSet donePaths;
|
||||
|
||||
while (!usedPaths.empty()) {
|
||||
StringSet::iterator i = usedPaths.begin();
|
||||
string path = *i;
|
||||
usedPaths.erase(i);
|
||||
|
||||
ElemMap::iterator j = inMap.find(path);
|
||||
if (j == inMap.end()) abort();
|
||||
|
||||
donePaths.insert(j->second.id);
|
||||
|
||||
fs.slice.elems.push_back(j->second);
|
||||
|
||||
for (FSIds::iterator k = j->second.refs.begin();
|
||||
k != j->second.refs.end(); k++)
|
||||
if (donePaths.find(*k) == donePaths.end()) {
|
||||
/* !!! performance */
|
||||
bool found = false;
|
||||
for (ElemMap::iterator l = inMap.begin();
|
||||
l != inMap.end(); l++)
|
||||
if (l->second.id == *k) {
|
||||
usedPaths.insert(l->first);
|
||||
found = true;
|
||||
}
|
||||
if (!found) abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* For debugging, print out the referenced and unreferenced paths. */
|
||||
for (ElemMap::iterator i = inMap.begin();
|
||||
i != inMap.end(); i++)
|
||||
{
|
||||
FSIdSet::iterator j = used.find(i->second.id);
|
||||
if (j == used.end())
|
||||
FSIdSet::iterator j = donePaths.find(i->second.id);
|
||||
if (j == donePaths.end())
|
||||
debug(format("NOT referenced: `%1%'") % i->second.path);
|
||||
else {
|
||||
else
|
||||
debug(format("referenced: `%1%'") % i->second.path);
|
||||
fs.slice.elems.push_back(i->second);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the normal form. This does not have to occur in the
|
||||
transaction below because writing terms is idem-potent. */
|
||||
fs.type = FState::fsSlice;
|
||||
ATerm nf = unparseFState(fs);
|
||||
msg(lvlVomit, format("normal form: %1%") % printTerm(nf));
|
||||
return storeSuccessor(id, nf);
|
||||
FSId idNF = writeTerm(nf, "-s-" + (string) id);
|
||||
|
||||
/* Register each outpat path, and register the normal form. This
|
||||
is wrapped in one database transaction to ensure that if we
|
||||
crash, either everything is registered or nothing is. This is
|
||||
for recoverability: unregistered paths in the store can be
|
||||
deleted arbitrarily, while registered paths can only be deleted
|
||||
by running the garbage collector. */
|
||||
Transaction txn(nixDB);
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
registerPath(txn, i->first, i->second);
|
||||
registerSuccessor(txn, id, idNF);
|
||||
txn.commit();
|
||||
|
||||
return idNF;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,35 +302,10 @@ void realiseSlice(const FSId & id, FSIdSet pending)
|
||||
if (fs.type != FState::fsSlice)
|
||||
throw Error(format("expected slice in %1%") % (string) id);
|
||||
|
||||
/* Perhaps all paths already contain the right id? */
|
||||
|
||||
bool missing = false;
|
||||
for (SliceElems::const_iterator i = fs.slice.elems.begin();
|
||||
i != fs.slice.elems.end(); i++)
|
||||
{
|
||||
SliceElem elem = *i;
|
||||
string id;
|
||||
if (!queryDB(nixDB, dbPath2Id, elem.path, id)) {
|
||||
if (pathExists(elem.path))
|
||||
throw Error(format("path `%1%' obstructed") % elem.path);
|
||||
missing = true;
|
||||
break;
|
||||
}
|
||||
if (parseHash(id) != elem.id)
|
||||
throw Error(format("path `%1%' obstructed") % elem.path);
|
||||
}
|
||||
|
||||
if (!missing) {
|
||||
debug(format("already installed"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* For each element, expand its id at its path. */
|
||||
for (SliceElems::const_iterator i = fs.slice.elems.begin();
|
||||
i != fs.slice.elems.end(); i++)
|
||||
{
|
||||
SliceElem elem = *i;
|
||||
debug(format("expanding %1% in `%2%'") % (string) elem.id % elem.path);
|
||||
expandId(elem.id, elem.path, "/", pending);
|
||||
}
|
||||
}
|
||||
@@ -269,7 +363,7 @@ static void fstateRequisitesSet(const FSId & id,
|
||||
|
||||
string idSucc;
|
||||
if (includeSuccessors &&
|
||||
queryDB(nixDB, dbSuccessors, id, idSucc))
|
||||
nixDB.queryString(noTxn, dbSuccessors, id, idSucc))
|
||||
fstateRequisitesSet(parseHash(idSucc),
|
||||
includeExprs, includeSuccessors, paths);
|
||||
}
|
||||
@@ -293,13 +387,13 @@ FSIds findGenerators(const FSIds & _ids)
|
||||
mappings, since we know that those are Nix expressions. */
|
||||
|
||||
Strings sucs;
|
||||
enumDB(nixDB, dbSuccessors, sucs);
|
||||
nixDB.enumTable(noTxn, dbSuccessors, sucs);
|
||||
|
||||
for (Strings::iterator i = sucs.begin();
|
||||
i != sucs.end(); i++)
|
||||
{
|
||||
string s;
|
||||
if (!queryDB(nixDB, dbSuccessors, *i, s)) continue;
|
||||
if (!nixDB.queryString(noTxn, dbSuccessors, *i, s)) continue;
|
||||
FSId id = parseHash(s);
|
||||
|
||||
FState fs;
|
||||
|
||||
@@ -29,7 +29,8 @@ Strings fstateRequisites(const FSId & id,
|
||||
FSIds findGenerators(const FSIds & ids);
|
||||
|
||||
/* Register a successor. */
|
||||
void registerSuccessor(const FSId & id1, const FSId & id2);
|
||||
void registerSuccessor(const Transaction & txn,
|
||||
const FSId & id1, const FSId & id2);
|
||||
|
||||
|
||||
#endif /* !__NORMALISE_H */
|
||||
|
||||
67
src/pathlocks.cc
Normal file
67
src/pathlocks.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "pathlocks.hh"
|
||||
|
||||
|
||||
/* This enables us to check whether are not already holding a lock on
|
||||
a file ourselves. POSIX locks (fcntl) suck in this respect: if we
|
||||
close a descriptor, the previous lock will be closed as well. And
|
||||
there is no way to query whether we already have a lock (F_GETLK
|
||||
only works on locks held by other processes). */
|
||||
static StringSet lockedPaths; /* !!! not thread-safe */
|
||||
|
||||
|
||||
PathLocks::PathLocks(const Strings & _paths)
|
||||
{
|
||||
/* Note that `fds' is built incrementally so that the destructor
|
||||
will only release those locks that we have already acquired. */
|
||||
|
||||
/* Sort the paths. This assures that locks are always acquired in
|
||||
the same order, thus preventing deadlocks. */
|
||||
Strings paths(_paths);
|
||||
paths.sort();
|
||||
|
||||
/* Acquire the lock for each path. */
|
||||
for (Strings::iterator i = paths.begin(); i != paths.end(); i++) {
|
||||
string path = *i;
|
||||
string lockPath = path + ".lock";
|
||||
|
||||
debug(format("locking path `%1%'") % path);
|
||||
|
||||
if (lockedPaths.find(lockPath) != lockedPaths.end()) {
|
||||
debug(format("already holding lock on `%1%'") % lockPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Open/create the lock file. */
|
||||
int fd = open(lockPath.c_str(), O_WRONLY | O_CREAT, 0666);
|
||||
if (fd == -1)
|
||||
throw SysError(format("opening lock file `%1%'") % lockPath);
|
||||
|
||||
fds.push_back(fd);
|
||||
this->paths.push_back(lockPath);
|
||||
|
||||
/* Lock it. */
|
||||
struct flock lock;
|
||||
lock.l_type = F_WRLCK; /* exclusive lock */
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0; /* entire file */
|
||||
|
||||
while (fcntl(fd, F_SETLKW, &lock) == -1)
|
||||
if (errno != EINTR)
|
||||
throw SysError(format("acquiring lock on `%1%'") % lockPath);
|
||||
|
||||
lockedPaths.insert(lockPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PathLocks::~PathLocks()
|
||||
{
|
||||
for (list<int>::iterator i = fds.begin(); i != fds.end(); i++)
|
||||
close(*i);
|
||||
|
||||
for (Strings::iterator i = paths.begin(); i != paths.end(); i++)
|
||||
lockedPaths.erase(*i);
|
||||
}
|
||||
19
src/pathlocks.hh
Normal file
19
src/pathlocks.hh
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef __PATHLOCKS_H
|
||||
#define __PATHLOCKS_H
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
class PathLocks
|
||||
{
|
||||
private:
|
||||
list<int> fds;
|
||||
Strings paths;
|
||||
|
||||
public:
|
||||
PathLocks(const Strings & _paths);
|
||||
~PathLocks();
|
||||
};
|
||||
|
||||
|
||||
#endif /* !__PATHLOCKS_H */
|
||||
@@ -19,7 +19,7 @@ static void initAndRun(int argc, char * * argv)
|
||||
nixStore = NIX_STORE_DIR;
|
||||
nixDataDir = NIX_DATA_DIR;
|
||||
nixLogDir = NIX_LOG_DIR;
|
||||
nixDB = (string) NIX_STATE_DIR + "/nixstate.db";
|
||||
nixDBPath = (string) NIX_STATE_DIR + "/db";
|
||||
|
||||
/* Put the arguments in a vector. */
|
||||
Strings args;
|
||||
|
||||
156
src/store.cc
156
src/store.cc
@@ -7,6 +7,7 @@
|
||||
#include "globals.hh"
|
||||
#include "db.hh"
|
||||
#include "archive.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "normalise.hh"
|
||||
|
||||
|
||||
@@ -32,6 +33,8 @@ struct CopySource : RestoreSource
|
||||
|
||||
void copyPath(string src, string dst)
|
||||
{
|
||||
debug(format("copying `%1%' to `%2%'") % src % dst);
|
||||
|
||||
/* Unfortunately C++ doesn't support coprocedures, so we have no
|
||||
nice way to chain CopySink and CopySource together. Instead we
|
||||
fork off a child to run the sink. (Fork-less platforms should
|
||||
@@ -96,62 +99,73 @@ void registerSubstitute(const FSId & srcId, const FSId & subId)
|
||||
/* For now, accept only one substitute per id. */
|
||||
Strings subs;
|
||||
subs.push_back(subId);
|
||||
setListDB(nixDB, dbSubstitutes, srcId, subs);
|
||||
|
||||
Transaction txn(nixDB);
|
||||
nixDB.setStrings(txn, dbSubstitutes, srcId, subs);
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
|
||||
void registerPath(const string & _path, const FSId & id)
|
||||
void registerPath(const Transaction & txn,
|
||||
const string & _path, const FSId & id)
|
||||
{
|
||||
string path(canonPath(_path));
|
||||
|
||||
setDB(nixDB, dbPath2Id, path, id);
|
||||
debug(format("registering path `%1%' with id %2%")
|
||||
% path % (string) id);
|
||||
|
||||
string oldId;
|
||||
if (nixDB.queryString(txn, dbPath2Id, path, oldId)) {
|
||||
if (id != parseHash(oldId))
|
||||
throw Error(format("path `%1%' already contains id %2%")
|
||||
% path % oldId);
|
||||
return;
|
||||
}
|
||||
|
||||
nixDB.setString(txn, dbPath2Id, path, id);
|
||||
|
||||
Strings paths;
|
||||
queryListDB(nixDB, dbId2Paths, id, paths); /* non-existence = ok */
|
||||
|
||||
for (Strings::iterator it = paths.begin();
|
||||
it != paths.end(); it++)
|
||||
if (*it == path) return;
|
||||
nixDB.queryStrings(txn, dbId2Paths, id, paths); /* non-existence = ok */
|
||||
|
||||
paths.push_back(path);
|
||||
|
||||
setListDB(nixDB, dbId2Paths, id, paths);
|
||||
nixDB.setStrings(txn, dbId2Paths, id, paths);
|
||||
}
|
||||
|
||||
|
||||
void unregisterPath(const string & _path)
|
||||
{
|
||||
string path(canonPath(_path));
|
||||
Transaction txn(nixDB);
|
||||
|
||||
debug(format("unregistering path `%1%'") % path);
|
||||
|
||||
string _id;
|
||||
if (!queryDB(nixDB, dbPath2Id, path, _id))
|
||||
if (!nixDB.queryString(txn, dbPath2Id, path, _id)) {
|
||||
txn.abort();
|
||||
return;
|
||||
}
|
||||
FSId id(parseHash(_id));
|
||||
|
||||
delDB(nixDB, dbPath2Id, path);
|
||||
nixDB.delPair(txn, dbPath2Id, path);
|
||||
|
||||
/* begin transaction */
|
||||
|
||||
Strings paths, paths2;
|
||||
queryListDB(nixDB, dbId2Paths, id, paths); /* non-existence = ok */
|
||||
nixDB.queryStrings(txn, dbId2Paths, id, paths); /* non-existence = ok */
|
||||
|
||||
bool changed = false;
|
||||
for (Strings::iterator it = paths.begin();
|
||||
it != paths.end(); it++)
|
||||
if (*it != path) paths2.push_back(*it); else changed = true;
|
||||
if (*it != path) paths2.push_back(*it);
|
||||
|
||||
if (changed)
|
||||
setListDB(nixDB, dbId2Paths, id, paths2);
|
||||
|
||||
/* end transaction */
|
||||
nixDB.setStrings(txn, dbId2Paths, id, paths2);
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
|
||||
bool queryPathId(const string & path, FSId & id)
|
||||
{
|
||||
string s;
|
||||
if (!queryDB(nixDB, dbPath2Id, absPath(path), s)) return false;
|
||||
if (!nixDB.queryString(noTxn, dbPath2Id, absPath(path), s)) return false;
|
||||
id = parseHash(s);
|
||||
return true;
|
||||
}
|
||||
@@ -165,7 +179,7 @@ bool isInPrefix(const string & path, const string & _prefix)
|
||||
|
||||
|
||||
string expandId(const FSId & id, const string & target,
|
||||
const string & prefix, FSIdSet pending)
|
||||
const string & prefix, FSIdSet pending, bool ignoreSubstitutes)
|
||||
{
|
||||
Nest nest(lvlDebug, format("expanding %1%") % (string) id);
|
||||
|
||||
@@ -174,7 +188,7 @@ string expandId(const FSId & id, const string & target,
|
||||
if (!target.empty() && !isInPrefix(target, prefix))
|
||||
abort();
|
||||
|
||||
queryListDB(nixDB, dbId2Paths, id, paths);
|
||||
nixDB.queryStrings(noTxn, dbId2Paths, id, paths);
|
||||
|
||||
/* Pick one equal to `target'. */
|
||||
if (!target.empty()) {
|
||||
@@ -198,30 +212,45 @@ string expandId(const FSId & id, const string & target,
|
||||
if (target.empty())
|
||||
return path;
|
||||
else {
|
||||
/* Acquire a lock on the target path. */
|
||||
Strings lockPaths;
|
||||
lockPaths.push_back(target);
|
||||
PathLocks outputLock(lockPaths);
|
||||
|
||||
/* Copy. */
|
||||
copyPath(path, target);
|
||||
registerPath(target, id);
|
||||
|
||||
/* Register the target path. */
|
||||
Transaction txn(nixDB);
|
||||
registerPath(txn, target, id);
|
||||
txn.commit();
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pending.find(id) != pending.end())
|
||||
throw Error(format("id %1% already being expanded") % (string) id);
|
||||
pending.insert(id);
|
||||
if (!ignoreSubstitutes) {
|
||||
|
||||
if (pending.find(id) != pending.end())
|
||||
throw Error(format("id %1% already being expanded") % (string) id);
|
||||
pending.insert(id);
|
||||
|
||||
/* Try to realise the substitutes, but only if this id is not
|
||||
already being realised by a substitute. */
|
||||
Strings subs;
|
||||
queryListDB(nixDB, dbSubstitutes, id, subs); /* non-existence = ok */
|
||||
/* Try to realise the substitutes, but only if this id is not
|
||||
already being realised by a substitute. */
|
||||
Strings subs;
|
||||
nixDB.queryStrings(noTxn, dbSubstitutes, id, subs); /* non-existence = ok */
|
||||
|
||||
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
|
||||
FSId subId = parseHash(*it);
|
||||
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
|
||||
FSId subId = parseHash(*it);
|
||||
|
||||
debug(format("trying substitute %1%") % (string) subId);
|
||||
debug(format("trying substitute %1%") % (string) subId);
|
||||
|
||||
realiseSlice(normaliseFState(subId, pending), pending);
|
||||
realiseSlice(normaliseFState(subId, pending), pending);
|
||||
|
||||
return expandId(id, target, prefix, pending);
|
||||
}
|
||||
|
||||
return expandId(id, target, prefix, pending);
|
||||
}
|
||||
|
||||
throw Error(format("cannot expand id `%1%'") % (string) id);
|
||||
@@ -231,6 +260,8 @@ string expandId(const FSId & id, const string & target,
|
||||
void addToStore(string srcPath, string & dstPath, FSId & id,
|
||||
bool deterministicName)
|
||||
{
|
||||
debug(format("adding `%1%' to the store") % srcPath);
|
||||
|
||||
srcPath = absPath(srcPath);
|
||||
id = hashPath(srcPath);
|
||||
|
||||
@@ -238,14 +269,21 @@ void addToStore(string srcPath, string & dstPath, FSId & id,
|
||||
dstPath = canonPath(nixStore + "/" + (string) id + "-" + baseName);
|
||||
|
||||
try {
|
||||
/* !!! should not use the substitutes! */
|
||||
dstPath = expandId(id, deterministicName ? dstPath : "", nixStore);
|
||||
dstPath = expandId(id, deterministicName ? dstPath : "",
|
||||
nixStore, FSIdSet(), true);
|
||||
return;
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
Strings lockPaths;
|
||||
lockPaths.push_back(dstPath);
|
||||
PathLocks outputLock(lockPaths);
|
||||
|
||||
copyPath(srcPath, dstPath);
|
||||
registerPath(dstPath, id);
|
||||
|
||||
Transaction txn(nixDB);
|
||||
registerPath(txn, dstPath, id);
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
|
||||
@@ -263,8 +301,12 @@ void deleteFromStore(const string & path)
|
||||
|
||||
void verifyStore()
|
||||
{
|
||||
Transaction txn(nixDB);
|
||||
|
||||
/* !!! verify that the result is consistent */
|
||||
|
||||
Strings paths;
|
||||
enumDB(nixDB, dbPath2Id, paths);
|
||||
nixDB.enumTable(txn, dbPath2Id, paths);
|
||||
|
||||
for (Strings::iterator i = paths.begin();
|
||||
i != paths.end(); i++)
|
||||
@@ -278,10 +320,10 @@ void verifyStore()
|
||||
|
||||
else {
|
||||
string id;
|
||||
if (!queryDB(nixDB, dbPath2Id, path, id)) abort();
|
||||
if (!nixDB.queryString(txn, dbPath2Id, path, id)) abort();
|
||||
|
||||
Strings idPaths;
|
||||
queryListDB(nixDB, dbId2Paths, id, idPaths);
|
||||
nixDB.queryStrings(txn, dbId2Paths, id, idPaths);
|
||||
|
||||
bool found = false;
|
||||
for (Strings::iterator j = idPaths.begin();
|
||||
@@ -298,11 +340,11 @@ void verifyStore()
|
||||
debug(format("reverse mapping for path `%1%' missing") % path);
|
||||
}
|
||||
|
||||
if (erase) delDB(nixDB, dbPath2Id, path);
|
||||
if (erase) nixDB.delPair(txn, dbPath2Id, path);
|
||||
}
|
||||
|
||||
Strings ids;
|
||||
enumDB(nixDB, dbId2Paths, ids);
|
||||
nixDB.enumTable(txn, dbId2Paths, ids);
|
||||
|
||||
for (Strings::iterator i = ids.begin();
|
||||
i != ids.end(); i++)
|
||||
@@ -310,13 +352,13 @@ void verifyStore()
|
||||
FSId id = parseHash(*i);
|
||||
|
||||
Strings idPaths;
|
||||
queryListDB(nixDB, dbId2Paths, id, idPaths);
|
||||
nixDB.queryStrings(txn, dbId2Paths, id, idPaths);
|
||||
|
||||
for (Strings::iterator j = idPaths.begin();
|
||||
j != idPaths.end(); )
|
||||
{
|
||||
string id2;
|
||||
if (!queryDB(nixDB, dbPath2Id, *j, id2) ||
|
||||
if (!nixDB.queryString(txn, dbPath2Id, *j, id2) ||
|
||||
id != parseHash(id2)) {
|
||||
debug(format("erasing path `%1%' from mapping for id %2%")
|
||||
% *j % (string) id);
|
||||
@@ -324,12 +366,12 @@ void verifyStore()
|
||||
} else j++;
|
||||
}
|
||||
|
||||
setListDB(nixDB, dbId2Paths, id, idPaths);
|
||||
nixDB.setStrings(txn, dbId2Paths, id, idPaths);
|
||||
}
|
||||
|
||||
|
||||
Strings subs;
|
||||
enumDB(nixDB, dbSubstitutes, subs);
|
||||
nixDB.enumTable(txn, dbSubstitutes, subs);
|
||||
|
||||
for (Strings::iterator i = subs.begin();
|
||||
i != subs.end(); i++)
|
||||
@@ -337,7 +379,7 @@ void verifyStore()
|
||||
FSId srcId = parseHash(*i);
|
||||
|
||||
Strings subIds;
|
||||
queryListDB(nixDB, dbSubstitutes, srcId, subIds);
|
||||
nixDB.queryStrings(txn, dbSubstitutes, srcId, subIds);
|
||||
|
||||
for (Strings::iterator j = subIds.begin();
|
||||
j != subIds.end(); )
|
||||
@@ -345,7 +387,7 @@ void verifyStore()
|
||||
FSId subId = parseHash(*j);
|
||||
|
||||
Strings subPaths;
|
||||
queryListDB(nixDB, dbId2Paths, subId, subPaths);
|
||||
nixDB.queryStrings(txn, dbId2Paths, subId, subPaths);
|
||||
if (subPaths.size() == 0) {
|
||||
debug(format("erasing substitute %1% for %2%")
|
||||
% (string) subId % (string) srcId);
|
||||
@@ -353,11 +395,11 @@ void verifyStore()
|
||||
} else j++;
|
||||
}
|
||||
|
||||
setListDB(nixDB, dbSubstitutes, srcId, subIds);
|
||||
nixDB.setStrings(txn, dbSubstitutes, srcId, subIds);
|
||||
}
|
||||
|
||||
Strings sucs;
|
||||
enumDB(nixDB, dbSuccessors, sucs);
|
||||
nixDB.enumTable(txn, dbSuccessors, sucs);
|
||||
|
||||
for (Strings::iterator i = sucs.begin();
|
||||
i != sucs.end(); i++)
|
||||
@@ -365,18 +407,20 @@ void verifyStore()
|
||||
FSId id1 = parseHash(*i);
|
||||
|
||||
string id2;
|
||||
if (!queryDB(nixDB, dbSuccessors, id1, id2)) abort();
|
||||
if (!nixDB.queryString(txn, dbSuccessors, id1, id2)) abort();
|
||||
|
||||
Strings id2Paths;
|
||||
queryListDB(nixDB, dbId2Paths, id2, id2Paths);
|
||||
nixDB.queryStrings(txn, dbId2Paths, id2, id2Paths);
|
||||
if (id2Paths.size() == 0) {
|
||||
Strings id2Subs;
|
||||
queryListDB(nixDB, dbSubstitutes, id2, id2Subs);
|
||||
nixDB.queryStrings(txn, dbSubstitutes, id2, id2Subs);
|
||||
if (id2Subs.size() == 0) {
|
||||
debug(format("successor %1% for %2% missing")
|
||||
% id2 % (string) id1);
|
||||
delDB(nixDB, dbSuccessors, (string) id1);
|
||||
nixDB.delPair(txn, dbSuccessors, (string) id1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "hash.hh"
|
||||
#include "db.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -20,7 +21,8 @@ void copyPath(string src, string dst);
|
||||
void registerSubstitute(const FSId & srcId, const FSId & subId);
|
||||
|
||||
/* Register a path keyed on its id. */
|
||||
void registerPath(const string & path, const FSId & id);
|
||||
void registerPath(const Transaction & txn,
|
||||
const string & path, const FSId & id);
|
||||
|
||||
/* Query the id of a path. */
|
||||
bool queryPathId(const string & path, FSId & id);
|
||||
@@ -35,7 +37,8 @@ bool queryPathId(const string & path, FSId & id);
|
||||
substitute (since when we build the substitute, we would first try
|
||||
to expand the id... kaboom!). */
|
||||
string expandId(const FSId & id, const string & target = "",
|
||||
const string & prefix = "/", FSIdSet pending = FSIdSet());
|
||||
const string & prefix = "/", FSIdSet pending = FSIdSet(),
|
||||
bool ignoreSubstitutes = false);
|
||||
|
||||
/* Copy a file to the nixStore directory and register it in dbRefs.
|
||||
Return the hash code of the value. */
|
||||
|
||||
0
src/test-builder-1.sh
Normal file → Executable file
0
src/test-builder-1.sh
Normal file → Executable file
0
src/test-builder-2.sh
Normal file → Executable file
0
src/test-builder-2.sh
Normal file → Executable file
@@ -51,6 +51,8 @@ struct MySource : RestoreSource
|
||||
|
||||
void runTests()
|
||||
{
|
||||
verbosity = (Verbosity) 100;
|
||||
|
||||
/* Hashing. */
|
||||
string s = "0b0ffd0538622bfe20b92c4aa57254d9";
|
||||
Hash h = parseHash(s);
|
||||
@@ -94,14 +96,16 @@ void runTests()
|
||||
/* Set up the test environment. */
|
||||
|
||||
mkdir("scratch", 0777);
|
||||
mkdir("scratch/db", 0777);
|
||||
|
||||
string testDir = absPath("scratch");
|
||||
cout << testDir << endl;
|
||||
|
||||
nixStore = testDir;
|
||||
nixLogDir = testDir;
|
||||
nixDB = testDir + "/db";
|
||||
nixDBPath = testDir + "/db";
|
||||
|
||||
openDB();
|
||||
initDB();
|
||||
|
||||
/* Expression evaluation. */
|
||||
|
||||
@@ -108,21 +108,28 @@ bool pathExists(const string & path)
|
||||
|
||||
void deletePath(string path)
|
||||
{
|
||||
msg(lvlVomit, format("deleting path `%1%'") % path);
|
||||
|
||||
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;
|
||||
|
||||
DIR * dir = opendir(path.c_str());
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) {
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
deletePath(path + "/" + name);
|
||||
names.push_back(name);
|
||||
}
|
||||
|
||||
closedir(dir); /* !!! close on exception */
|
||||
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); i++)
|
||||
deletePath(path + "/" + *i);
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
-e s^@bindir\@^$(bindir)^g \
|
||||
-e s^@sysconfdir\@^$(sysconfdir)^g \
|
||||
-e s^@localstatedir\@^$(localstatedir)^g \
|
||||
-e s^@wget\@^$(wget)^g \
|
||||
< $< > $@ || rm $@
|
||||
chmod +x $@
|
||||
|
||||
11
testpkgs/args/args-build.sh
Executable file
11
testpkgs/args/args-build.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#! /bin/sh
|
||||
|
||||
IFS=
|
||||
|
||||
echo "printing list of args"
|
||||
|
||||
for i in $@; do
|
||||
echo "arg: $i"
|
||||
done
|
||||
|
||||
touch $out
|
||||
7
testpkgs/args/args.fix
Normal file
7
testpkgs/args/args.fix
Normal file
@@ -0,0 +1,7 @@
|
||||
Package(
|
||||
[ ("name", "args")
|
||||
, ("build", Relative("args/args-build.sh"))
|
||||
|
||||
, ("args", ["1", "2", "3", IncludeFix("slow2/slow.fix")])
|
||||
]
|
||||
)
|
||||
9
testpkgs/fun/fun1.fix
Normal file
9
testpkgs/fun/fun1.fix
Normal file
@@ -0,0 +1,9 @@
|
||||
Call(
|
||||
Function(["x"],
|
||||
Call(
|
||||
Function(["x"], Var("x")),
|
||||
[ ("x", Var("x")) ]
|
||||
)
|
||||
),
|
||||
[ ("x", True) ]
|
||||
)
|
||||
9
testpkgs/fun/fun2.fix
Normal file
9
testpkgs/fun/fun2.fix
Normal file
@@ -0,0 +1,9 @@
|
||||
Call(
|
||||
Function(["x"],
|
||||
Call(
|
||||
Function(["y", "z"], Var("y")),
|
||||
[ ("y", Var("x")) ]
|
||||
)
|
||||
),
|
||||
[ ("x", True) ]
|
||||
)
|
||||
9
testpkgs/fun/fun3.fix
Normal file
9
testpkgs/fun/fun3.fix
Normal file
@@ -0,0 +1,9 @@
|
||||
Call(
|
||||
Function(["x"],
|
||||
Call(
|
||||
Function(["x"], Var("x")),
|
||||
[ ("x", False) ]
|
||||
)
|
||||
),
|
||||
[ ("x", True) ]
|
||||
)
|
||||
1
testpkgs/infrec/infrec.fix
Normal file
1
testpkgs/infrec/infrec.fix
Normal file
@@ -0,0 +1 @@
|
||||
IncludeFix("infrec/infrec.fix")
|
||||
14
testpkgs/slow/slow-build.sh
Executable file
14
testpkgs/slow/slow-build.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "builder started..."
|
||||
|
||||
mkdir $out
|
||||
|
||||
for i in $(seq 1 30); do
|
||||
echo $i
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "done" > $out/bla
|
||||
|
||||
echo "builder finished"
|
||||
5
testpkgs/slow/slow.fix
Normal file
5
testpkgs/slow/slow.fix
Normal file
@@ -0,0 +1,5 @@
|
||||
Package(
|
||||
[ ("name", "slow")
|
||||
, ("build", Relative("slow/slow-build.sh"))
|
||||
]
|
||||
)
|
||||
14
testpkgs/slow2/slow-build.sh
Executable file
14
testpkgs/slow2/slow-build.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "builder started..."
|
||||
|
||||
for i in $(seq 1 10); do
|
||||
echo $i
|
||||
sleep 1
|
||||
done
|
||||
|
||||
mkdir $out
|
||||
|
||||
echo "done" >> $out/bla
|
||||
|
||||
echo "builder finished"
|
||||
5
testpkgs/slow2/slow.fix
Normal file
5
testpkgs/slow2/slow.fix
Normal file
@@ -0,0 +1,5 @@
|
||||
Package(
|
||||
[ ("name", "slow")
|
||||
, ("build", Relative("slow2/slow-build.sh"))
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user