about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--corepkgs/buildenv.nix4
-rw-r--r--corepkgs/nar.nix5
-rw-r--r--corepkgs/unpack-channel.nix6
-rw-r--r--corepkgs/unpack-channel.sh32
-rw-r--r--doc/manual/Makefile.am40
-rw-r--r--doc/manual/env-common.xml2
-rw-r--r--doc/manual/installation.xml8
-rw-r--r--doc/manual/manual.xml3
-rw-r--r--doc/manual/nix-build.xml23
-rw-r--r--doc/manual/nix-channel.xml48
-rw-r--r--doc/manual/nix-copy-closure.xml8
-rw-r--r--doc/manual/nix-env.xml26
-rw-r--r--doc/manual/nix-instantiate.xml36
-rw-r--r--doc/manual/nix-store.xml36
-rw-r--r--doc/manual/opt-common.xml2
-rw-r--r--perl/lib/Nix/CopyClosure.pm7
-rw-r--r--perl/lib/Nix/Manifest.pm4
-rw-r--r--release.nix2
-rwxr-xr-xscripts/nix-build.in28
-rwxr-xr-xscripts/nix-channel.in143
-rwxr-xr-xscripts/nix-copy-closure.in10
-rwxr-xr-xscripts/nix-generate-patches.in10
-rwxr-xr-xscripts/nix-pull.in4
-rwxr-xr-xscripts/nix-push.in2
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--src/libstore/build.cc14
-rw-r--r--src/libstore/gc.cc115
-rw-r--r--src/libstore/local-store.cc7
-rw-r--r--src/libstore/local-store.hh9
-rw-r--r--src/libstore/remote-store.cc2
-rw-r--r--src/libstore/store-api.cc1
-rw-r--r--src/libstore/store-api.hh4
-rw-r--r--src/nix-env/help.txt2
-rw-r--r--src/nix-env/user-env.cc1
-rw-r--r--src/nix-instantiate/nix-instantiate.cc15
-rw-r--r--src/nix-store/help.txt2
-rw-r--r--src/nix-store/nix-store.cc1
-rw-r--r--src/nix-worker/nix-worker.cc6
-rw-r--r--substitute.mk1
-rw-r--r--tests/common.sh.in2
-rw-r--r--tests/nix-channel.sh4
42 files changed, 428 insertions, 250 deletions
diff --git a/configure.ac b/configure.ac
index c37f76870c55..fba53e81603c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -171,6 +171,7 @@ NEED_PROG(bzip2, bzip2)
 AC_PATH_PROG(dot, dot)
 AC_PATH_PROG(dblatex, dblatex)
 AC_PATH_PROG(gzip, gzip)
+AC_PATH_PROG(pv, pv, pv)
 
 
 # Test that Perl has the open/fork feature (Perl 5.8.0 and beyond).
diff --git a/corepkgs/buildenv.nix b/corepkgs/buildenv.nix
index 6e2bee10b1e7..641231dd9ee1 100644
--- a/corepkgs/buildenv.nix
+++ b/corepkgs/buildenv.nix
@@ -1,10 +1,10 @@
 with import <nix/config.nix>;
 
-{ system, derivations, manifest }:
+{ derivations, manifest }:
 
 derivation { 
   name = "user-environment";
-  system = system;
+  system = builtins.currentSystem;
   builder = perl;
   args = [ "-w" ./buildenv.pl ];
   
diff --git a/corepkgs/nar.nix b/corepkgs/nar.nix
index 70a4af2f9ddb..5be8be10c166 100644
--- a/corepkgs/nar.nix
+++ b/corepkgs/nar.nix
@@ -20,11 +20,12 @@ let
 
 in
 
-{ system, storePath, hashAlgo }:
+{ storePath, hashAlgo }:
 
 derivation {
   name = "nar";
+  system = builtins.currentSystem;
   builder = shell;
   args = [ "-e" builder ];
-  inherit system storePath hashAlgo;
+  inherit storePath hashAlgo;
 }
diff --git a/corepkgs/unpack-channel.nix b/corepkgs/unpack-channel.nix
index 5e6ccf23fd47..170f3ab07c77 100644
--- a/corepkgs/unpack-channel.nix
+++ b/corepkgs/unpack-channel.nix
@@ -1,11 +1,11 @@
 with import <nix/config.nix>;
 
-{ system, inputs }:
+{ name, channelName, src }:
 
 derivation {
-  name = "channels";
+  system = builtins.currentSystem;
   builder = shell;
   args = [ "-e" ./unpack-channel.sh ];
-  inherit system inputs bzip2 tar tr;
+  inherit name channelName src bzip2 tar tr;
   PATH = "${nixBinDir}:${coreutils}";
 }
diff --git a/corepkgs/unpack-channel.sh b/corepkgs/unpack-channel.sh
index 7c244a6a8552..f42b0870ae18 100644
--- a/corepkgs/unpack-channel.sh
+++ b/corepkgs/unpack-channel.sh
@@ -1,30 +1,4 @@
 mkdir $out
-mkdir $out/tmp
-cd $out/tmp
-
-inputs=($inputs)
-for ((n = 0; n < ${#inputs[*]}; n += 2)); do
-    channelName=${inputs[n]}
-    channelTarball=${inputs[n+1]}
-    
-    echo "unpacking channel $channelName"
-    
-    $bzip2 -d < $channelTarball | $tar xf -
-
-    if test -e */channel-name; then
-        channelName="$(cat */channel-name)"
-    fi
-
-    nr=1
-    attrName=$(echo $channelName | $tr -- '- ' '__')
-    dirName=$attrName
-    while test -e ../$dirName; do
-        nr=$((nr+1))
-        dirName=$attrName-$nr
-    done
-
-    mv * ../$dirName # !!! hacky
-done
-
-cd ..
-rmdir tmp
+cd $out
+$bzip2 -d < $src | $tar xf -
+mv * $out/$channelName
diff --git a/doc/manual/Makefile.am b/doc/manual/Makefile.am
index 55450c9a5990..33cb27e054bd 100644
--- a/doc/manual/Makefile.am
+++ b/doc/manual/Makefile.am
@@ -1,5 +1,5 @@
-XMLLINT = $(xmllint) $(xmlflags)
-XSLTPROC = $(xsltproc) $(xmlflags) \
+XMLLINT = $(xmllint) --nonet $(xmlflags)
+XSLTPROC = $(xsltproc) --nonet $(xmlflags) \
  --param section.autolabel 1 \
  --param section.label.includes.component.label 1 \
  --param html.stylesheet \'style.css\' \
@@ -29,29 +29,41 @@ MANUAL_SRCS = manual.xml introduction.xml installation.xml \
  package-management.xml writing-nix-expressions.xml builtins.xml \
  build-farm.xml \
  $(man1_MANS:.1=.xml) $(man8_MANS:.8=.xml) \
- troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
+ troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml opt-inst-syn.xml \
  env-common.xml quick-start.xml nix-lang-ref.xml glossary.xml \
  conf-file.xml release-notes.xml \
  style.css images
 
+# Do XInclude processing.
+manual.xmli: $(MANUAL_SRCS) version.txt
+	$(XMLLINT) --xinclude $< -o $@.tmp
+	mv $@.tmp $@
+
 # Note: RelaxNG validation requires xmllint >= 2.7.4.
-manual.is-valid: $(MANUAL_SRCS) version.txt
-	$(XMLLINT) --noout --nonet --xinclude --noxincludenode --relaxng $(docbookrng)/docbook.rng $<
+manual.is-valid: manual.xmli
+	$(XSLTPROC) --novalid --stringparam profile.condition manual \
+	  $(docbookxsl)/profiling/profile.xsl $< 2> /dev/null | \
+	  $(XMLLINT) --noout --relaxng $(docbookrng)/docbook.rng -
 	touch $@
 
 version.txt:
 	echo -n $(VERSION) > version.txt
 
-man $(MANS): $(MANUAL_SRCS) manual.is-valid
-	$(XSLTPROC) --nonet --xinclude $(docbookxsl)/manpages/docbook.xsl manual.xml
+man $(MANS): manual.is-valid
+	$(XSLTPROC) --stringparam profile.condition manpage \
+	  $(docbookxsl)/profiling/profile.xsl manual.xmli 2> /dev/null | \
+	  $(XSLTPROC) $(docbookxsl)/manpages/docbook.xsl -
 
 manual.html: $(MANUAL_SRCS) manual.is-valid images
-	$(XSLTPROC) --nonet --xinclude --output manual.html \
-	  $(docbookxsl)/html/docbook.xsl manual.xml
+	$(XSLTPROC) --xinclude --stringparam profile.condition manual \
+	  $(docbookxsl)/profiling/profile.xsl manual.xml | \
+	  $(XSLTPROC) --output manual.html $(docbookxsl)/html/docbook.xsl -
 
 manual.pdf: $(MANUAL_SRCS) manual.is-valid images
 	if test "$(dblatex)" != ""; then \
-		$(dblatex) $(dblatex_opts) manual.xml; \
+		$(XSLTPROC) --xinclude --stringparam profile.condition manual \
+		  $(docbookxsl)/profiling/profile.xsl manual.xml | \
+		  $(dblatex) -o manual.pdf $(dblatex_opts) -; \
 	else \
 		echo "Please install dblatex and rerun configure."; \
 		exit 1; \
@@ -64,12 +76,12 @@ NEWS_OPTS = \
  --stringparam header.rule 0
 
 NEWS.html: release-notes.xml
-	$(XSLTPROC) --nonet --xinclude --output $@ $(NEWS_OPTS) \
+	$(XSLTPROC) --xinclude --output $@ $(NEWS_OPTS) \
 	  $(docbookxsl)/html/docbook.xsl release-notes.xml
 
 NEWS.txt: release-notes.xml
-	$(XSLTPROC) --nonet --xinclude quote-literals.xsl release-notes.xml | \
-	  $(XSLTPROC) --nonet --output $@.tmp.html $(NEWS_OPTS) \
+	$(XSLTPROC) --xinclude quote-literals.xsl release-notes.xml | \
+	  $(XSLTPROC) --output $@.tmp.html $(NEWS_OPTS) \
 	  $(docbookxsl)/html/docbook.xsl -
 	LANG=en_US $(w3m) -dump $@.tmp.html > $@
 	rm $@.tmp.html
@@ -96,7 +108,7 @@ images:
 	cp $(docbookxsl)/images/callouts/*.gif images/callouts
 	chmod -R +w images
 
-KEEP = manual.html manual.is-valid version.txt $(MANS) NEWS.html NEWS.txt
+KEEP = manual.html manual.xmli manual.is-valid version.txt $(MANS) NEWS.html NEWS.txt
 
 EXTRA_DIST = $(MANUAL_SRCS) $(FIGURES) $(KEEP)
 
diff --git a/doc/manual/env-common.xml b/doc/manual/env-common.xml
index 99acc5949044..f52e2633de78 100644
--- a/doc/manual/env-common.xml
+++ b/doc/manual/env-common.xml
@@ -7,7 +7,7 @@
 
 <para>Most Nix commands interpret the following environment variables:</para>
 
-<variablelist>
+<variablelist xml:id="env-common">
 
   
 <varlistentry><term><envar>NIX_IGNORE_SYMLINK_STORE</envar></term>
diff --git a/doc/manual/installation.xml b/doc/manual/installation.xml
index 15c156dab5e7..5e29abe9f5f8 100644
--- a/doc/manual/installation.xml
+++ b/doc/manual/installation.xml
@@ -47,17 +47,17 @@ for Red Hat, SuSE, and Fedora Core are also available.</para>
 
 <para>Alternatively, the most recent sources of Nix can be obtained
 from its <link
-xlink:href="https://svn.nixos.org/repos/nix/nix/trunk">Subversion
+xlink:href="https://github.com/NixOS/nix">git
 repository</link>.  For example, the following command will check out
 the latest revision into a directory called
 <filename>nix</filename>:</para>
 
 <screen>
-$ svn checkout https://svn.nixos.org/repos/nix/nix/trunk nix</screen>
+$ git clone https://github.com/NixOS/nix</screen>
 
 <para>Likewise, specific releases can be obtained from the <link
-xlink:href="https://svn.nixos.org/repos/nix/nix/tags">tags
-directory</link> of the repository.</para>
+xlink:href="https://github.com/NixOS/nix/tags">tags
+</link> of the repository.</para>
 
 </section>
 
diff --git a/doc/manual/manual.xml b/doc/manual/manual.xml
index 95f80686008d..fa80a3651dcf 100644
--- a/doc/manual/manual.xml
+++ b/doc/manual/manual.xml
@@ -14,8 +14,7 @@
         <surname>Dolstra</surname>
       </personname>
       <affiliation>
-        <orgname>Delft University of Technology</orgname>
-        <orgdiv>Department of Software Technology</orgdiv>
+        <orgname>LogicBlox</orgname>
       </affiliation>
       <contrib>Author</contrib>
     </author>
diff --git a/doc/manual/nix-build.xml b/doc/manual/nix-build.xml
index 465635f67125..e3508d92ce0e 100644
--- a/doc/manual/nix-build.xml
+++ b/doc/manual/nix-build.xml
@@ -75,11 +75,11 @@ or renamed.  So don’t rename the symlink.</para></warning>
 
 <refsection><title>Options</title>
 
-<para>See also <xref linkend="sec-common-options" />.  All options not
-listed here are passed to <command>nix-store --realise</command>,
-except for <option>--arg</option> and <option>--attr</option> /
-<option>-A</option> which are passed to
-<command>nix-instantiate</command>.</para>
+<para>All options not listed here are passed to <command>nix-store
+--realise</command>, except for <option>--arg</option> and
+<option>--attr</option> / <option>-A</option> which are passed to
+<command>nix-instantiate</command>.  <phrase condition="manual">See
+also <xref linkend="sec-common-options" />.</phrase></para>
 
 <variablelist>
 
@@ -122,6 +122,10 @@ except for <option>--arg</option> and <option>--attr</option> /
 
 </variablelist>
 
+<variablelist condition="manpage">
+  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
+</variablelist>
+
 </refsection>
 
 
@@ -141,4 +145,13 @@ firefox  firefox-config</screen>
 </refsection>
 
 
+<refsection condition="manpage"><title>Environment variables</title>
+
+<variablelist>
+  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
+</variablelist>
+
+</refsection>
+  
+
 </refentry>
diff --git a/doc/manual/nix-channel.xml b/doc/manual/nix-channel.xml
index 024add8601e8..22d8900d8f1c 100644
--- a/doc/manual/nix-channel.xml
+++ b/doc/manual/nix-channel.xml
@@ -19,7 +19,7 @@
   <cmdsynopsis>
     <command>nix-channel</command>
     <group choice='req'>
-      <arg choice='plain'><option>--add</option> <replaceable>url</replaceable></arg>
+      <arg choice='plain'><option>--add</option> <replaceable>url</replaceable> <arg choice='opt'><replaceable>name</replaceable></arg></arg>
       <arg choice='plain'><option>--remove</option> <replaceable>url</replaceable></arg>
       <arg choice='plain'><option>--list</option></arg>
       <arg choice='plain'><option>--update</option></arg>
@@ -31,32 +31,39 @@
 
 <para>A Nix channel is mechanism that allows you to automatically stay
 up-to-date with a set of pre-built Nix expressions.  A Nix channel is
-just a URL that points to a place that contains a set of Nix
-expressions, as well as a <command>nix-push</command> manifest.  See
-also <xref linkend="sec-channels" />.</para>
+just a URL that points to a place containing a set of Nix expressions
+and a <command>nix-push</command> manifest.  <phrase
+condition="manual">See also <xref linkend="sec-channels"
+/>.</phrase></para>
 
 <para>This command has the following operations:
 
 <variablelist>
 
-  <varlistentry><term><option>--add</option> <replaceable>url</replaceable></term>
+  <varlistentry><term><option>--add</option> <replaceable>url</replaceable> [<replaceable>name</replaceable>]</term>
 
-    <listitem><para>Adds <replaceable>url</replaceable> to the list of
-    subscribed channels.</para></listitem>
+    <listitem><para>Adds a channel named
+    <replaceable>name</replaceable> with URL
+    <replaceable>url</replaceable> to the list of subscribed channels.
+    If <replaceable>name</replaceable> is omitted, it defaults to the
+    last component of <replaceable>url</replaceable>, with the
+    suffixes <literal>-stable</literal> or
+    <literal>-unstable</literal> removed.</para></listitem>
 
   </varlistentry>
 
-  <varlistentry><term><option>--remove</option> <replaceable>url</replaceable></term>
+  <varlistentry><term><option>--remove</option> <replaceable>name</replaceable></term>
 
-    <listitem><para>Removes <replaceable>url</replaceable> from the
-    list of subscribed channels.</para></listitem>
+    <listitem><para>Removes the channel named
+    <replaceable>name</replaceable> from the list of subscribed
+    channels.</para></listitem>
 
   </varlistentry>
 
   <varlistentry><term><option>--list</option></term>
 
-    <listitem><para>Prints the URLs of all subscribed channels on
-    standard output.</para></listitem>
+    <listitem><para>Prints the names and URLs of all subscribed
+    channels on standard output.</para></listitem>
 
   </varlistentry>
 
@@ -64,7 +71,7 @@ also <xref linkend="sec-channels" />.</para>
 
     <listitem><para>Downloads the Nix expressions of all subscribed
     channels, makes them the default for <command>nix-env</command>
-    operations (by symlinking them in the directory
+    operations (by symlinking them from the directory
     <filename>~/.nix-defexpr</filename>), and performs a
     <command>nix-pull</command> on the manifests of all channels to
     make pre-built binaries available.</para></listitem>
@@ -75,8 +82,8 @@ also <xref linkend="sec-channels" />.</para>
 
 </para>
 
-<para>Note that <option>--add</option> and <option>--remove</option>
-do not automatically perform an update.</para>
+<para>Note that <option>--add</option> does not automatically perform
+an update.</para>
 
 <para>The list of subscribed channels is stored in
 <filename>~/.nix-channels</filename>.</para>
@@ -90,4 +97,15 @@ respectively.</para>
 
 </refsection>
 
+<refsection><title>Examples</title>
+
+<para>To subscribe to the Nixpkgs channel and install the GNU Hello package:</para>
+
+<screen>
+$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable
+$ nix-channel --update
+$ nix-env -iA nixpkgs.hello</screen>
+
+</refsection>
+
 </refentry>
diff --git a/doc/manual/nix-copy-closure.xml b/doc/manual/nix-copy-closure.xml
index 45cfc0f34245..4b5ce755c67d 100644
--- a/doc/manual/nix-copy-closure.xml
+++ b/doc/manual/nix-copy-closure.xml
@@ -26,6 +26,7 @@
     <arg><option>--gzip</option></arg>
     <arg><option>--bzip2</option></arg>
     <arg><option>--xz</option></arg>
+    <arg><option>--show-progress</option></arg>
     <arg><option>--include-outputs</option></arg>
     <arg choice='plain'>
       <replaceable>user@</replaceable><replaceable>machine</replaceable>
@@ -110,6 +111,13 @@ those paths.  If this bothers you, use
 
   </varlistentry>
 
+  <varlistentry><term><option>--show-progress</option></term>
+
+    <listitem><para>Show the progress of each path's transfer as it's made.
+    This requires the <command>pv</command> utility to be in <envar>PATH</envar>.</para></listitem>
+
+  </varlistentry>
+
   <varlistentry><term><option>--include-outputs</option></term>
   
   <listitem><para>Also copy the outputs of store derivations included
diff --git a/doc/manual/nix-env.xml b/doc/manual/nix-env.xml
index a03afaf5c007..02a51c6e9006 100644
--- a/doc/manual/nix-env.xml
+++ b/doc/manual/nix-env.xml
@@ -18,7 +18,7 @@
 <refsynopsisdiv>
   <cmdsynopsis>
     <command>nix-env</command>
-    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
+    <xi:include href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
     <arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
     <arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
     <arg>
@@ -71,8 +71,8 @@ be performed.  These are documented below.</para>
 
 <para>This section lists the options that are common to all
 operations.  These options are allowed for every subcommand, though
-they may not always have an effect.  See also <xref
-linkend="sec-common-options" />.</para>
+they may not always have an effect.  <phrase condition="manual">See
+also <xref linkend="sec-common-options" />.</phrase></para>
 
 <variablelist>
 
@@ -121,16 +121,18 @@ linkend="sec-common-options" />.</para>
   <varlistentry><term><option>--system-filter</option> <replaceable>system</replaceable></term>
 
     <listitem><para>By default, operations such as <option>--query
-    --available</option> only include derivations matching the current
-    platform.  This option allows you to use derivations for the
-    specified platform <replaceable>system</replaceable>.  The special
-    value <literal>*</literal> causes derivations for any platform to
-    be included.</para></listitem>
+    --available</option> show derivations matching any platform.  This
+    option allows you to use derivations for the specified platform
+    <replaceable>system</replaceable>.</para></listitem>
 
   </varlistentry>
 
 </variablelist>
 
+<variablelist condition="manpage">
+  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
+</variablelist>
+
 </refsection>
 
 
@@ -1250,5 +1252,13 @@ error: no generation older than the current (91) exists</screen>
 </refsection>
 
 
+<refsection condition="manpage"><title>Environment variables</title>
+
+<variablelist>
+  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
+</variablelist>
+
+</refsection>
+  
 
 </refentry>
diff --git a/doc/manual/nix-instantiate.xml b/doc/manual/nix-instantiate.xml
index ba89af425586..618b61f159dc 100644
--- a/doc/manual/nix-instantiate.xml
+++ b/doc/manual/nix-instantiate.xml
@@ -36,6 +36,7 @@
           <option>--eval-only</option>
           <arg><option>--strict</option></arg>
         </arg>
+        <arg choice='plain'><option>--find-file</option></arg>
       </group>
       <arg><option>--xml</option></arg>
     </arg>
@@ -49,8 +50,9 @@
 <para>The command <command>nix-instantiate</command> generates <link
 linkend="gloss-derivation">store derivations</link> from (high-level)
 Nix expressions.  It loads and evaluates the Nix expressions in each
-of <replaceable>files</replaceable>.  Each top-level expression should
-evaluate to a derivation, a list of derivations, or a set of
+of <replaceable>files</replaceable> (which defaults to
+<replaceable>./default.nix</replaceable>).  Each top-level expression
+should evaluate to a derivation, a list of derivations, or a set of
 derivations.  The paths of the resulting store derivations are printed
 on standard output.</para>
 
@@ -64,8 +66,8 @@ store derivation instantiation from Nix expressions automatically).
 It is most commonly used for implementing new deployment
 policies.</para>
 
-<para>See also <xref linkend="sec-common-options" /> for a list of
-common options.</para>
+<para condition="manual">See also <xref linkend="sec-common-options"
+/> for a list of common options.</para>
 
 </refsection>
 
@@ -100,6 +102,19 @@ common options.</para>
     
   </varlistentry>
 
+  <varlistentry><term><option>--find-file</option></term>
+  
+    <listitem><para>Look up the given files in Nix’s search path (as
+    specified by the <envar>NIX_PATH</envar> environment variable).
+    If found, print the corresponding absolute paths on standard
+    output.  For instance, if <envar>NIX_PATH</envar> is
+    <literal>nixpkgs=/home/alice/nixpkgs</literal>, then
+    <literal>nix-instantiate --find-file nixpkgs/default.nix</literal>
+    will print
+    <literal>/home/alice/nixpkgs/default.nix</literal>.</para></listitem>
+    
+  </varlistentry>
+      
   <varlistentry><term><option>--xml</option></term>
 
     <listitem><para>When used with <option>--parse-only</option> and
@@ -127,6 +142,10 @@ common options.</para>
 
 </variablelist>
 
+<variablelist condition="manpage">
+  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
+</variablelist>
+
 </refsection>
 
 
@@ -198,4 +217,13 @@ $ echo 'rec { x = "foo"; y = x; }' | nix-instantiate --eval-only --xml --strict
 </refsection>
 
 
+<refsection condition="manpage"><title>Environment variables</title>
+
+<variablelist>
+  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
+</variablelist>
+
+</refsection>
+  
+
 </refentry>
diff --git a/doc/manual/nix-store.xml b/doc/manual/nix-store.xml
index a32559c033f4..6cc765bf27ca 100644
--- a/doc/manual/nix-store.xml
+++ b/doc/manual/nix-store.xml
@@ -48,8 +48,9 @@ be performed.  These are documented below.</para>
 
 <para>This section lists the options that are common to all
 operations.  These options are allowed for every subcommand, though
-they may not always have an effect.  See also <xref
-linkend="sec-common-options" /> for a list of common options.</para>
+they may not always have an effect.  <phrase condition="manual">See
+also <xref linkend="sec-common-options" /> for a list of common
+options.</phrase></para>
 
 <variablelist>
 
@@ -57,8 +58,9 @@ linkend="sec-common-options" /> for a list of common options.</para>
 
     <listitem><para>Causes the result of a realisation
     (<option>--realise</option> and <option>--force-realise</option>)
-    to be registered as a root of the garbage collector (see <xref
-    linkend="ssec-gc-roots" />).  The root is stored in
+    to be registered as a root of the garbage collector<phrase
+    condition="manual"> (see <xref linkend="ssec-gc-roots"
+    />)</phrase>.  The root is stored in
     <replaceable>path</replaceable>, which must be inside a directory
     that is scanned for roots by the garbage collector (i.e.,
     typically in a subdirectory of
@@ -112,6 +114,10 @@ lrwxrwxrwx    1 ... 2005-03-13 21:10 /home/eelco/bla/result -> /nix/store/1r1134
     
 </variablelist>
 
+<variablelist condition="manpage">
+  <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
+</variablelist>
+
 </refsection>
 
   
@@ -212,7 +218,6 @@ linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
     <arg choice='plain'><option>--delete</option></arg>
   </group>
   <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
-  <arg><option>--max-links</option> <replaceable>nrlinks</replaceable></arg>
 </cmdsynopsis>
 
 </refsection>
@@ -280,16 +285,6 @@ options control what gets deleted and in what order:
     
   </varlistentry>
 
-  <varlistentry><term><option>--max-links</option> <replaceable>nrlinks</replaceable></term>
-  
-    <listitem><para>Keep deleting paths until the hard link count on
-    <filename>/nix/store</filename> is less than
-    <replaceable>nrlinks</replaceable>, then stop.  This is useful for
-    very large Nix stores on filesystems with a 32000 subdirectories
-    limit (like <literal>ext3</literal>).</para></listitem>
-    
-  </varlistentry>
-
 </variablelist>
 
 </para>
@@ -1053,7 +1048,7 @@ backups and when migrating to different database schemas.</para>
 
 <!--######################################################################-->
 
-<refsection><title>Operation <option>--dump-db</option></title>
+<refsection><title>Operation <option>--load-db</option></title>
 
 <refsection>
   <title>Synopsis</title>
@@ -1074,4 +1069,13 @@ loads it into the Nix database.</para>
 </refsection>
 
 
+<refsection condition="manpage"><title>Environment variables</title>
+
+<variablelist>
+  <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
+</variablelist>
+
+</refsection>
+  
+
 </refentry>
diff --git a/doc/manual/opt-common.xml b/doc/manual/opt-common.xml
index c36c6df41003..92b76c01f103 100644
--- a/doc/manual/opt-common.xml
+++ b/doc/manual/opt-common.xml
@@ -5,7 +5,7 @@
 
 <para>Most Nix commands accept the following command-line options:</para>
 
-<variablelist>
+<variablelist xml:id="opt-common">
 
 <varlistentry><term><option>--help</option></term>
   
diff --git a/perl/lib/Nix/CopyClosure.pm b/perl/lib/Nix/CopyClosure.pm
index 08573b2ab1f1..79c6dfcccbf4 100644
--- a/perl/lib/Nix/CopyClosure.pm
+++ b/perl/lib/Nix/CopyClosure.pm
@@ -6,11 +6,12 @@ use Nix::Store;
 
 
 sub copyTo {
-    my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor, $includeOutputs, $dryRun, $sign) = @_;
+    my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer) = @_;
 
     $compressor = "$compressor |" if $compressor ne "";
     $decompressor = "$decompressor |" if $decompressor ne "";
-    
+    $progressViewer = "$progressViewer |" if $progressViewer ne "";
+
     # Get the closure of this path.
     my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
         map { followLinksToStorePath $_ } @{$storePaths})));
@@ -34,7 +35,7 @@ sub copyTo {
     if (scalar @missing > 0) {
         print STDERR "copying ", scalar @missing, " missing paths to ‘$sshHost’...\n";
         unless ($dryRun) {
-            open SSH, "| $compressor ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
+            open SSH, "| $compressor $progressViewer ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
             exportPaths(fileno(SSH), $sign, @missing);
             close SSH or die "copying store paths to remote machine `$sshHost' failed: $?";
         }
diff --git a/perl/lib/Nix/Manifest.pm b/perl/lib/Nix/Manifest.pm
index 909604a44938..532a900978f2 100644
--- a/perl/lib/Nix/Manifest.pm
+++ b/perl/lib/Nix/Manifest.pm
@@ -343,10 +343,10 @@ EOF
         my $version = readManifest_($manifest, \&addNARToDB, \&addPatchToDB);
         
         if ($version < 3) {
-            die "you have an old-style or corrupt manifest `$manifestLink'; please delete it";
+            die "you have an old-style or corrupt manifest `$manifestLink'; please delete it\n";
         }
         if ($version >= 10) {
-            die "manifest `$manifestLink' is too new; please delete it or upgrade Nix";
+            die "manifest `$manifestLink' is too new; please delete it or upgrade Nix\n";
         }
     }
 
diff --git a/release.nix b/release.nix
index 6b571644d994..3f4c4876123f 100644
--- a/release.nix
+++ b/release.nix
@@ -19,7 +19,7 @@ let
         inherit officialRelease;
 
         buildInputs =
-          [ curl bison24 flex2535 perl libxml2 libxslt w3m bzip2
+          [ curl bison25 flex2535 perl libxml2 libxslt w3m bzip2
             tetex dblatex nukeReferences pkgconfig sqlite git
           ];
 
diff --git a/scripts/nix-build.in b/scripts/nix-build.in
index b36ec0208b41..35b186bb715d 100755
--- a/scripts/nix-build.in
+++ b/scripts/nix-build.in
@@ -14,6 +14,10 @@ my @instArgs = ();
 my @buildArgs = ();
 my @exprs = ();
 
+my $shell = $ENV{SHELL} || "/bin/sh";
+my $envCommand = "p=\$PATH; source \$stdenv/setup; PATH=\$PATH:\$p; exec $shell";
+my @envExclude = ();
+
 
 my $tmpDir = tempdir("nix-build.XXXXXX", CLEANUP => 1, TMPDIR => 1)
     or die "cannot create a temporary directory";
@@ -41,7 +45,12 @@ Flags:
   --drv-link NAME: create symlink NAME instead of `derivation'
   --no-out-link: do not create the `result' symlink
   --out-link / -o NAME: create symlink NAME instead of `result'
-  --attr ATTR: select a specific attribution from the Nix expression
+  --attr / -A ATTR: select a specific attribute from the Nix expression
+  
+  --run-env: build dependencies of the specified derivation, then start a
+      shell with the environment of the derivation
+  --command: command to run with `--run-env'
+  --exclude: regexp specifying dependencies to be excluded by `--run-env'
 
 Any additional flags are passed to `nix-store'.
 EOF
@@ -125,6 +134,18 @@ EOF
         $runEnv = 1;
     }
     
+    elsif ($arg eq "--command") {
+        $n++;
+        die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
+        $envCommand = $ARGV[$n];
+    }
+
+    elsif ($arg eq "--exclude") {
+        $n++;
+        die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
+        push @envExclude, $ARGV[$n];
+    }
+
     elsif (substr($arg, 0, 1) eq "-") {
         push @buildArgs, $arg;
     }
@@ -155,7 +176,8 @@ foreach my $expr (@exprs) {
         my $drv = derivationFromPath($drvPath);
 
         # Build or fetch all dependencies of the derivation.
-        system("$Nix::Config::binDir/nix-store -r @buildArgs @{$drv->{inputDrvs}} @{$drv->{inputSrcs}} > /dev/null") == 0
+        my @inputDrvs = grep { my $x = $_; (grep { $x =~ $_ } @envExclude) == 0 } @{$drv->{inputDrvs}};
+        system("$Nix::Config::binDir/nix-store -r @buildArgs @inputDrvs @{$drv->{inputSrcs}} > /dev/null") == 0
             or die "$0: failed to build all dependencies\n";
 
         # Set the environment.
@@ -168,7 +190,7 @@ foreach my $expr (@exprs) {
         # convenience, source $stdenv/setup to setup additional
         # environment variables.  Also don't lose the current $PATH
         # directories.
-        exec($ENV{SHELL}, "-c", "p=\$PATH; source \$stdenv/setup; PATH=\$PATH:\$p; exec $ENV{SHELL}");
+        exec($ENV{SHELL}, "-c", $envCommand);
         die;
     }
 
diff --git a/scripts/nix-channel.in b/scripts/nix-channel.in
index ebfc246cfa30..524ffa656451 100755
--- a/scripts/nix-channel.in
+++ b/scripts/nix-channel.in
@@ -1,6 +1,8 @@
 #! @perl@ -w @perlFlags@
 
 use strict;
+use File::Basename;
+use File::Path qw(make_path);
 use Nix::Config;
 
 my $manifestDir = $Nix::Config::manifestDir;
@@ -11,67 +13,67 @@ my $channelCache = "$Nix::Config::stateDir/channel-cache";
 mkdir $channelCache, 0755 unless -e $channelCache;
 $ENV{'NIX_DOWNLOAD_CACHE'} = $channelCache if -W $channelCache;
 
-
 # Figure out the name of the `.nix-channels' file to use.
-my $home = $ENV{"HOME"};
-die '$HOME not set' unless defined $home;
+my $home = $ENV{"HOME"} or die '$HOME not set\n';
 my $channelsList = "$home/.nix-channels";
-
 my $nixDefExpr = "$home/.nix-defexpr";
-    
 
-my @channels;
+# Figure out the name of the channels profile.
+my $userName = getpwuid($<) or die "cannot figure out user name";
+my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels";
+make_path(dirname $profile, mode => 0755);
+    
+my %channels;
 
 
-# Reads the list of channels from the file $channelsList;
+# Reads the list of channels.
 sub readChannels {
     return if (!-f $channelsList);
     open CHANNELS, "<$channelsList" or die "cannot open `$channelsList': $!";
     while (<CHANNELS>) {
         chomp;
         next if /^\s*\#/;
-        push @channels, $_;
+        my ($url, $name) = split ' ', $_;
+        $url =~ s/\/*$//; # remove trailing slashes
+        $name = basename $url unless defined $name;
+        $channels{$name} = $url;
     }
     close CHANNELS;
 }
 
 
-# Writes the list of channels to the file $channelsList;
+# Writes the list of channels.
 sub writeChannels {
     open CHANNELS, ">$channelsList" or die "cannot open `$channelsList': $!";
-    foreach my $url (@channels) {
-        print CHANNELS "$url\n";
+    foreach my $name (keys %channels) {
+        print CHANNELS "$channels{$name} $name\n";
     }
     close CHANNELS;
 }
 
 
-# Adds a channel to the file $channelsList;
+# Adds a channel.
 sub addChannel {
-    my $url = shift;
+    my ($url, $name) = @_;
     readChannels;
-    foreach my $url2 (@channels) {
-        return if $url eq $url2;
-    }
-    push @channels, $url;
+    $channels{$name} = $url;
     writeChannels;
 }
 
 
-# Remove a channel from the file $channelsList;
+# Remove a channel.
 sub removeChannel {
-    my $url = shift;
-    my @left = ();
+    my ($name) = @_;
     readChannels;
-    foreach my $url2 (@channels) {
-        push @left, $url2 if $url ne $url2;
-    }
-    @channels = @left;
+    delete $channels{$name};
     writeChannels;
+
+    system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0
+        or die "cannot remove channel `$name'";
 }
 
 
-# Fetch Nix expressions and pull cache manifests from the subscribed
+# Fetch Nix expressions and pull manifests from the subscribed
 # channels.
 sub update {
     readChannels;
@@ -82,64 +84,62 @@ sub update {
     # Do we have write permission to the manifests directory?
     die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
 
-    # Pull cache manifests.
-    foreach my $url (@channels) {
-        #print "pulling cache manifest from `$url'\n";
+    # Download each channel.
+    my $exprs = "";
+    foreach my $name (keys %channels) {
+        my $url = $channels{$name};
+        my $origUrl = "$url/MANIFEST";
+
+        # Check if $url is a redirect.  If so, follow it now to ensure
+        # consistency if the redirection is changed between
+        # downloading the manifest and the tarball.
+        my $headers = `$Nix::Config::curl --silent --head '$url'`;
+        die "$0: unable to check `$url'\n" if $? != 0;
+        $headers =~ s/\r//g;
+        $url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m;
+        
+        # Pull the channel manifest.
+        $ENV{'NIX_ORIG_URL'} = $origUrl;
         system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
-            or die "cannot pull cache manifest from `$url'";
-    }
-
-    # Create a Nix expression that fetches and unpacks the channel Nix
-    # expressions.
-
-    my $inputs = "[";
-    foreach my $url (@channels) {
-        $url =~ /\/([^\/]+)\/?$/;
-        my $channelName = $1;
-        $channelName = "unnamed" unless defined $channelName;
+            or die "cannot pull manifest from `$url'\n";
 
+        # Download the channel tarball.
         my $fullURL = "$url/nixexprs.tar.bz2";
-        print "downloading Nix expressions from `$fullURL'...\n";
-        $ENV{"PRINT_PATH"} = 1;
-        $ENV{"QUIET"} = 1;
-        my ($hash, $path) = `$Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
+        print STDERR "downloading Nix expressions from `$fullURL'...\n";
+        my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
         die "cannot fetch `$fullURL'" if $? != 0;
         chomp $path;
-        $inputs .= '"' . $channelName . '"' . " " . $path . " ";
-    }
-    $inputs .= "]";
 
-    # Figure out a name for the GC root.
-    my $userName = getpwuid($<);
-    die "who ARE you? go away" unless defined $userName;
+        # If the URL contains a version number, append it to the name
+        # attribute (so that "nix-env -q" on the channels profile
+        # shows something useful).
+        my $cname = $name;
+        $cname .= $1 if basename($url) =~ /(-\d.*)$/;
 
-    my $rootFile = "$Nix::Config::stateDir/gcroots/per-user/$userName/channels";
-    
-    # Build the Nix expression.
-    print "unpacking channel Nix expressions...\n";
-    my $outPath = `\\
-        $Nix::Config::binDir/nix-build --out-link '$rootFile' --drv-link '$rootFile'.tmp \\
-        '<nix/unpack-channel.nix>' \\
-        --argstr system @system@ --arg inputs '$inputs'`
-        or die "cannot unpack the channels";
-    chomp $outPath;
+        $exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }' ";
+    }
 
-    unlink "$rootFile.tmp";
+    # Unpack the channel tarballs into the Nix store and install them
+    # into the channels profile.
+    print STDERR "unpacking channels...\n";
+    system("$Nix::Config::binDir/nix-env --profile '$profile' " .
+           "-f '<nix/unpack-channel.nix>' -i -E $exprs --quiet") == 0
+           or die "cannot unpack the channels";
 
     # Make the channels appear in nix-env.
     unlink $nixDefExpr if -l $nixDefExpr; # old-skool ~/.nix-defexpr
     mkdir $nixDefExpr or die "cannot create directory `$nixDefExpr'" if !-e $nixDefExpr;
     my $channelLink = "$nixDefExpr/channels";
     unlink $channelLink; # !!! not atomic
-    symlink($outPath, $channelLink) or die "cannot symlink `$channelLink' to `$outPath'";
+    symlink($profile, $channelLink) or die "cannot symlink `$channelLink' to `$profile'";
 }
 
 
 sub usageError {
     print STDERR <<EOF;
 Usage:
-  nix-channel --add URL
-  nix-channel --remove URL
+  nix-channel --add URL [CHANNEL-NAME]
+  nix-channel --remove CHANNEL-NAME
   nix-channel --list
   nix-channel --update
 EOF
@@ -154,22 +154,29 @@ while (scalar @ARGV) {
     my $arg = shift @ARGV;
 
     if ($arg eq "--add") {
-        usageError if scalar @ARGV != 1;
-        addChannel (shift @ARGV);
+        usageError if scalar @ARGV < 1 || scalar @ARGV > 2;
+        my $url = shift @ARGV;
+        my $name = shift @ARGV;
+        unless (defined $name) {
+            $name = basename $url;
+            $name =~ s/-unstable//;
+            $name =~ s/-stable//;
+        }
+        addChannel($url, $name);
         last;
     }
 
     if ($arg eq "--remove") {
         usageError if scalar @ARGV != 1;
-        removeChannel (shift @ARGV);
+        removeChannel(shift @ARGV);
         last;
     }
 
     if ($arg eq "--list") {
         usageError if scalar @ARGV != 0;
         readChannels;
-        foreach my $url (@channels) {
-            print "$url\n";
+        foreach my $name (keys %channels) {
+            print "$name $channels{$name}\n";
         }
         last;
     }
diff --git a/scripts/nix-copy-closure.in b/scripts/nix-copy-closure.in
index 1ed24d285208..12a83cff979c 100755
--- a/scripts/nix-copy-closure.in
+++ b/scripts/nix-copy-closure.in
@@ -23,6 +23,8 @@ my $sign = 0;
 my $compressor = "";
 my $decompressor = "";
 
+my $progressViewer = "";
+
 my $toMode = 1;
 
 my $includeOutputs = 0;
@@ -60,6 +62,9 @@ while (@ARGV) {
     elsif ($arg eq "--include-outputs") {
         $includeOutputs = 1;
     }
+    elsif ($arg eq "--show-progress") {
+        $progressViewer = "@pv@";
+    }
     elsif ($arg eq "--dry-run") {
         $dryRun = 1;
     }
@@ -76,7 +81,7 @@ openSSHConnection $sshHost or die "$0: unable to start SSH\n";
 
 
 if ($toMode) { # Copy TO the remote machine.
-    Nix::CopyClosure::copyTo($sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor, $includeOutputs, $dryRun, $sign);
+    Nix::CopyClosure::copyTo($sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer);
 }
 
 else { # Copy FROM the remote machine.
@@ -101,9 +106,10 @@ else { # Copy FROM the remote machine.
         print STDERR "copying ", scalar @missing, " missing paths from ‘$sshHost’...\n";
         $compressor = "| $compressor" if $compressor ne "";
         $decompressor = "$decompressor |" if $decompressor ne "";
+	$progressViewer = "$progressViewer |" if $progressViewer ne "";
         unless ($dryRun) {
             my $extraOpts = $sign ? "--sign" : "";
-            system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0
+            system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $progressViewer $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0
                 or die "copying store paths from remote machine `$sshHost' failed: $?";
         }
     }
diff --git a/scripts/nix-generate-patches.in b/scripts/nix-generate-patches.in
index 4cb843382b52..969af916d8e6 100755
--- a/scripts/nix-generate-patches.in
+++ b/scripts/nix-generate-patches.in
@@ -39,4 +39,14 @@ generatePatches \%srcNarFiles, \%dstNarFiles, \%srcPatches, \%dstPatches,
 
 propagatePatches \%srcPatches, \%dstNarFiles, \%dstPatches;
 
+# Optionally add all new patches to the manifest in $NIX_ALL_PATCHES.
+my $allPatchesFile = $ENV{"NIX_ALL_PATCHES"};
+if (defined $allPatchesFile) {
+    my (%dummy, %allPatches);
+    readManifest("$patchesPath/all-patches", \%dummy, \%allPatches)
+        if -f $allPatchesFile;
+    copyPatches \%dstPatches, \%allPatches;
+    writeManifest($allPatchesFile, {}, \%allPatches, 0);
+}
+
 writeManifest $dstManifest, \%dstNarFiles, \%dstPatches;
diff --git a/scripts/nix-pull.in b/scripts/nix-pull.in
index 136a5c9fa4bd..8d5db2f57c68 100755
--- a/scripts/nix-pull.in
+++ b/scripts/nix-pull.in
@@ -50,7 +50,7 @@ sub processURL {
     my $manifest;
 
     # First see if a bzipped manifest is available.
-    if (system("$Nix::Config::curl --fail --silent --head '$url'.bz2 > /dev/null") == 0) {
+    if (system("$Nix::Config::curl --fail --silent --location --head '$url'.bz2 > /dev/null") == 0) {
         print "fetching list of Nix archives at `$url.bz2'...\n";
         $manifest = downloadFile "$url.bz2";
     }
@@ -72,7 +72,7 @@ sub processURL {
 
     my $urlFile = "$manifestDir/$baseName-$hash.url";
     open URL, ">$urlFile" or die "cannot create `$urlFile'";
-    print URL "$url";
+    print URL ($ENV{'NIX_ORIG_URL'} || $url);
     close URL;
     
     my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
diff --git a/scripts/nix-push.in b/scripts/nix-push.in
index 1e0918abd6ac..a1c02190bd6c 100755
--- a/scripts/nix-push.in
+++ b/scripts/nix-push.in
@@ -103,7 +103,7 @@ foreach my $storePath (@storePaths) {
     # Construct a Nix expression that creates a Nix archive.
     my $nixexpr = 
         "(import <nix/nar.nix> " .
-        "{ storePath = builtins.storePath \"$storePath\"; system = \"@system@\"; hashAlgo = \"$hashAlgo\"; }) ";
+        "{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"$hashAlgo\"; }) ";
     
     print NIX $nixexpr;
 }
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 449123a1f82c..095e288430f6 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -8,6 +8,8 @@
 %parse-param { ParseData * data }
 %lex-param { yyscan_t scanner }
 %lex-param { ParseData * data }
+%expect 1
+%expect-rr 1
 
 %code requires {
     
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 467f16597440..77e968ddd08e 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -1793,11 +1793,25 @@ void DerivationGoal::startBuilder()
             if (chdir(tmpDir.c_str()) == -1)
                 throw SysError(format("changing into `%1%'") % tmpDir);
 
+            /* Close all other file descriptors. */
+            closeMostFDs(set<int>());
+
 #ifdef CAN_DO_LINUX32_BUILDS
+            /* Change the personality to 32-bit if we're doing an
+               i686-linux build on an x86_64-linux machine. */
             if (drv.platform == "i686-linux" && thisSystem == "x86_64-linux") {
                 if (personality(0x0008 | 0x8000000 /* == PER_LINUX32_3GB */) == -1)
                     throw SysError("cannot set i686-linux personality");
             }
+
+            /* Impersonate a Linux 2.6 machine to get some determinism
+               in builds that depend on the kernel version. */
+            if ((drv.platform == "i686-linux" || drv.platform == "x86_64-linux") &&
+                queryBoolSetting("build-impersonate-linux-26", true))
+            {
+                int cur = personality(0xffffffff);
+                if (cur != -1) personality(cur | 0x0020000 /* == UNAME26 */);
+            }
 #endif
 
             /* Fill in the environment. */
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 95c7154110b3..f6ed7dd2264e 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -1,6 +1,7 @@
 #include "globals.hh"
 #include "misc.hh"
 #include "local-store.hh"
+#include "immutable.hh"
 
 #include <boost/shared_ptr.hpp>
 
@@ -55,24 +56,22 @@ int LocalStore::openGCLock(LockType lockType)
 }
 
 
-void createSymlink(const Path & link, const Path & target, bool careful)
+void createSymlink(const Path & link, const Path & target)
 {
     /* Create directories up to `gcRoot'. */
     createDirs(dirOf(link));
 
-    /* !!! shouldn't removing and creating the symlink be atomic? */
-
-    /* Remove the old symlink. */
-    if (pathExists(link)) {
-        if (careful && (!isLink(link) || !isInStore(readLink(link))))
-            throw Error(format("cannot create symlink `%1%'; already exists") % link);
-        unlink(link.c_str());
-    }
-
-    /* And create the new one. */
-    if (symlink(target.c_str(), link.c_str()) == -1)
+    /* Create the new symlink. */
+    Path tempLink = (format("%1%.tmp-%2%-%3%")
+        % link % getpid() % rand()).str();
+    if (symlink(target.c_str(), tempLink.c_str()) == -1)
         throw SysError(format("symlinking `%1%' to `%2%'")
-            % link % target);
+            % tempLink % target);
+
+    /* Atomically replace the old one. */
+    if (rename(tempLink.c_str(), link.c_str()) == -1)
+        throw SysError(format("cannot rename `%1%' to `%2%'")
+            % tempLink % link);
 }
 
 
@@ -87,7 +86,7 @@ void LocalStore::addIndirectRoot(const Path & path)
     string hash = printHash32(hashString(htSHA1, path));
     Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
         % nixStateDir % gcRootsDir % hash).str());
-    createSymlink(realRoot, path, false);
+    createSymlink(realRoot, path);
 }
 
 
@@ -104,7 +103,11 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
                 "(are you running nix-build inside the store?)") % gcRoot);
 
     if (indirect) {
-        createSymlink(gcRoot, storePath, true);
+        /* Don't clobber the the link if it already exists and doesn't
+           point to the Nix store. */
+        if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
+            throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot);
+        createSymlink(gcRoot, storePath);
         store.addIndirectRoot(gcRoot);
     }
 
@@ -119,7 +122,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
                     % gcRoot % rootsDir);
         }
             
-        createSymlink(gcRoot, storePath, false);
+        createSymlink(gcRoot, storePath);
     }
 
     /* Check that the root can be found by the garbage collector.
@@ -396,11 +399,11 @@ struct LocalStore::GCState
     PathSet deleted;
     PathSet live;
     PathSet busy;
+    PathSet invalidated;
     bool gcKeepOutputs;
     bool gcKeepDerivations;
-    GCState(GCResults & results_) : results(results_)
-    {
-    }
+    unsigned long long bytesInvalidated;
+    GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
 };
 
 
@@ -418,12 +421,27 @@ bool LocalStore::isActiveTempFile(const GCState & state,
         && state.tempRoots.find(string(path, 0, path.size() - suffix.size())) != state.tempRoots.end();
 }
 
-    
+
+void LocalStore::deleteGarbage(GCState & state, const Path & path)
+{
+    printMsg(lvlInfo, format("deleting `%1%'") % path);
+    unsigned long long bytesFreed, blocksFreed;
+    deletePathWrapped(path, bytesFreed, blocksFreed);
+    state.results.bytesFreed += bytesFreed;
+    state.results.blocksFreed += blocksFreed;
+}
+
+
 bool LocalStore::tryToDelete(GCState & state, const Path & path)
 {
     checkInterrupt();
-    
-    if (!pathExists(path)) return true;
+
+    struct stat st;
+    if (lstat(path.c_str(), &st)) {
+        if (errno == ENOENT) return true;
+        throw SysError(format("getting status of %1%") % path);
+    }
+
     if (state.deleted.find(path) != state.deleted.end()) return true;
     if (state.live.find(path) != state.live.end()) return false;
 
@@ -502,28 +520,39 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
 
     /* The path is garbage, so delete it. */
     if (shouldDelete(state.options.action)) {
-        printMsg(lvlInfo, format("deleting `%1%'") % path);
 
-        unsigned long long bytesFreed, blocksFreed;
-        deleteFromStore(path, bytesFreed, blocksFreed);
-        state.results.bytesFreed += bytesFreed;
-        state.results.blocksFreed += blocksFreed;
+        /* If it's a valid path that's not a regular file or symlink,
+           invalidate it, rename it, and schedule it for deletion.
+           The renaming is to ensure that later (when we're not
+           holding the global GC lock) we can delete the path without
+           being afraid that the path has become alive again.
+           Otherwise delete it right away. */
+        if (isValidPath(path)) {
+            if (S_ISDIR(st.st_mode)) {
+                printMsg(lvlInfo, format("invalidating `%1%'") % path);
+                // Estimate the amount freed using the narSize field.
+                state.bytesInvalidated += queryPathInfo(path).narSize;
+                invalidatePathChecked(path);
+                makeMutable(path.c_str());
+                // Mac OS X cannot rename directories if they are read-only.
+                if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
+                    throw SysError(format("making `%1%' writable") % path);
+                Path tmp = (format("%1%-gc-%2%") % path % getpid()).str();
+                if (rename(path.c_str(), tmp.c_str()))
+                    throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
+                state.invalidated.insert(tmp);
+            } else {
+                invalidatePathChecked(path);
+                deleteGarbage(state, path);
+            }
+        } else
+            deleteGarbage(state, path);
 
-        if (state.options.maxFreed && state.results.bytesFreed > state.options.maxFreed) {
-            printMsg(lvlInfo, format("deleted more than %1% bytes; stopping") % state.options.maxFreed);
+        if (state.options.maxFreed && state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
+            printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
             throw GCLimitReached();
         }
 
-        if (state.options.maxLinks) {
-            struct stat st;
-            if (stat(nixStore.c_str(), &st) == -1)
-                throw SysError(format("statting `%1%'") % nixStore);
-            if (st.st_nlink < state.options.maxLinks) {
-                printMsg(lvlInfo, format("link count on the store has dropped below %1%; stopping") % state.options.maxLinks);
-                throw GCLimitReached();
-            }
-        }
-        
     } else
         printMsg(lvlTalkative, format("would delete `%1%'") % path);
     
@@ -645,6 +674,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
         } catch (GCLimitReached & e) {
         }
     }
+
+    /* Allow other processes to add to the store from here on. */
+    fdGCLock.close();
+
+    /* Delete the invalidated paths now that the lock has been
+       released. */
+    foreach (PathSet::iterator, i, state.invalidated)
+        deleteGarbage(state, *i);
 }
 
 
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index ca541e1cce2e..f04436b7f659 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -1386,11 +1386,8 @@ Paths LocalStore::importPaths(bool requireSignature, Source & source)
 }
 
 
-void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFreed,
-    unsigned long long & blocksFreed)
+void LocalStore::invalidatePathChecked(const Path & path)
 {
-    bytesFreed = 0;
-
     assertStorePath(path);
 
     while (1) {
@@ -1410,8 +1407,6 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr
             break;
         } catch (SQLiteBusy & e) { };
     }
-    
-    deletePathWrapped(path, bytesFreed, blocksFreed);
 }
 
 
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 2739c4eea69d..8e3cbe5ce11b 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -164,10 +164,6 @@ public:
 
     void collectGarbage(const GCOptions & options, GCResults & results);
 
-    /* Delete a path from the Nix store. */
-    void deleteFromStore(const Path & path, unsigned long long & bytesFreed,
-        unsigned long long & blocksFreed);
-    
     /* Optimise the disk space usage of the Nix store by hard-linking
        files with the same contents. */
     void optimiseStore(bool dryRun, OptimiseStats & stats);
@@ -238,6 +234,9 @@ private:
 
     void invalidatePath(const Path & path);
 
+    /* Delete a path from the Nix store. */
+    void invalidatePathChecked(const Path & path);
+    
     void verifyPath(const Path & path, const PathSet & store,
         PathSet & done, PathSet & validPaths);
 
@@ -249,6 +248,8 @@ private:
 
     struct GCState;
 
+    void deleteGarbage(GCState & state, const Path & path);
+    
     bool tryToDelete(GCState & state, const Path & path);
     
     bool isActiveTempFile(const GCState & state,
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index c77a12870513..0b8fa36f6df4 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -466,7 +466,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
     writeStrings(options.pathsToDelete, to);
     writeInt(options.ignoreLiveness, to);
     writeLongLong(options.maxFreed, to);
-    writeInt(options.maxLinks, to);
+    writeInt(0, to);
     if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) {
         /* removed options */
         writeInt(0, to);
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 36ade2170876..19bc048abd02 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -13,7 +13,6 @@ GCOptions::GCOptions()
     action = gcDeleteDead;
     ignoreLiveness = false;
     maxFreed = 0;
-    maxLinks = 0;
 }
 
 
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 61bcaf50507f..a62a648168f9 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -53,10 +53,6 @@ struct GCOptions
        no limit. */
     unsigned long long maxFreed;
 
-    /* Stop after the number of hard links to the Nix store directory
-       has dropped below `maxLinks'. */
-    unsigned int maxLinks;
-
     GCOptions();
 };
 
diff --git a/src/nix-env/help.txt b/src/nix-env/help.txt
index 605bf2810a42..0ebdca9b22a5 100644
--- a/src/nix-env/help.txt
+++ b/src/nix-env/help.txt
@@ -69,6 +69,8 @@ Options:
   --file / -f FILE: use Nix expression FILE for installation, etc.
   --verbose / -v: verbose operation (may be repeated)
   --keep-failed / -K: keep temporary directories of failed builds
+  --keep-going / -k: build as many dependencies as possible, even if
+      some dependencies fail to build
   --preserve-installed: do not replace currently installed versions in `-i'
   --system-filter SYSTEM: only use derivations for specified platform
   --prebuilt-only / -b: only use derivations whose prebuilt binaries are
diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc
index 0dcdcc0d186f..4480a17aa923 100644
--- a/src/nix-env/user-env.cc
+++ b/src/nix-env/user-env.cc
@@ -118,7 +118,6 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
        builder with the manifest as argument. */
     Value args, topLevel;
     state.mkAttrs(args, 3);
-    mkString(*state.allocAttr(args, state.sSystem), thisSystem);
     mkString(*state.allocAttr(args, state.symbols.create("manifest")),
         manifestFile, singleton<PathSet>(manifestFile));
     args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc
index 8f3a290f3a6a..d86c9fc84572 100644
--- a/src/nix-instantiate/nix-instantiate.cc
+++ b/src/nix-instantiate/nix-instantiate.cc
@@ -79,6 +79,7 @@ void run(Strings args)
     EvalState state;
     Strings files;
     bool readStdin = false;
+    bool findFile = false;
     bool evalOnly = false;
     bool parseOnly = false;
     bool xmlOutput = false;
@@ -100,6 +101,8 @@ void run(Strings args)
             readOnlyMode = true;
             parseOnly = evalOnly = true;
         }
+        else if (arg == "--find-file")
+            findFile = true;
         else if (arg == "--attr" || arg == "-A") {
             if (i == args.end())
                 throw UsageError("`--attr' requires an argument");
@@ -130,13 +133,23 @@ void run(Strings args)
 
     if (attrPaths.empty()) attrPaths.push_back("");
 
+    if (findFile) {
+        foreach (Strings::iterator, i, files) {
+            Path p = state.findFile(*i);
+            if (p == "") throw Error(format("unable to find `%1%'") % *i);
+            std::cout << p << std::endl;
+        }
+        return;
+    }
+
     store = openStore();
 
     if (readStdin) {
         Expr * e = parseStdin(state);
         processExpr(state, attrPaths, parseOnly, strict, autoArgs,
             evalOnly, xmlOutput, xmlOutputSourceLocation, e);
-    }
+    } else if (files.empty())
+        files.push_back("./default.nix");
 
     foreach (Strings::iterator, i, files) {
         Expr * e = state.parseExprFromFile(lookupFileArg(state, *i));
diff --git a/src/nix-store/help.txt b/src/nix-store/help.txt
index f3e65a5283b9..ed4a29a67e17 100644
--- a/src/nix-store/help.txt
+++ b/src/nix-store/help.txt
@@ -65,5 +65,7 @@ Options:
 
   --verbose / -v: verbose operation (may be repeated)
   --keep-failed / -K: keep temporary directories of failed builds
+  --keep-going / -k: build as many dependencies as possible, even if
+      some dependencies fail to build
 
   --add-root: add garbage collector roots for the result
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 404da2c51f51..fa96725b1b48 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -562,7 +562,6 @@ static void opGC(Strings opFlags, Strings opArgs)
             long long maxFreed = getIntArg<long long>(*i, i, opFlags.end());
             options.maxFreed = maxFreed >= 1 ? maxFreed : 1;
         }
-        else if (*i == "--max-links") options.maxLinks = getIntArg<unsigned int>(*i, i, opFlags.end());
         else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
 
     if (!opArgs.empty()) throw UsageError("no arguments expected");
diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc
index f5201ab6a561..4b0c9e319130 100644
--- a/src/nix-worker/nix-worker.cc
+++ b/src/nix-worker/nix-worker.cc
@@ -477,7 +477,7 @@ static void performOp(unsigned int clientVersion,
         options.pathsToDelete = readStorePaths<PathSet>(from);
         options.ignoreLiveness = readInt(from);
         options.maxFreed = readLongLong(from);
-        options.maxLinks = readInt(from);
+        readInt(from); // obsolete field
         if (GET_PROTOCOL_MINOR(clientVersion) >= 5) {
             /* removed options */
             readInt(from);
@@ -697,6 +697,8 @@ static void daemonLoop()
     if (fdSocket == -1)
         throw SysError("cannot create Unix domain socket");
 
+    closeOnExec(fdSocket);
+    
     string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
 
     createDirs(dirOf(socketPath));
@@ -751,6 +753,8 @@ static void daemonLoop()
 		    throw SysError("accepting connection");
             }
 
+            closeOnExec(remote);
+            
             /* Get the identity of the caller, if possible. */
             uid_t clientUid = -1;
             pid_t clientPid = -1;
diff --git a/substitute.mk b/substitute.mk
index 9009b4249485..d28b330a673d 100644
--- a/substitute.mk
+++ b/substitute.mk
@@ -22,6 +22,7 @@
 	 -e "s^@sed\@^$(sed)^g" \
 	 -e "s^@tar\@^$(tar)^g" \
 	 -e "s^@gzip\@^$(gzip)^g" \
+	 -e "s^@pv\@^$(pv)^g" \
 	 -e "s^@tr\@^$(tr)^g" \
 	 -e "s^@dot\@^$(dot)^g" \
 	 -e "s^@xmllint\@^$(xmllint)^g" \
diff --git a/tests/common.sh.in b/tests/common.sh.in
index 0f46e5f4ec21..031a0d66d1b8 100644
--- a/tests/common.sh.in
+++ b/tests/common.sh.in
@@ -35,7 +35,7 @@ readLink() {
 
 clearProfiles() {
     profiles="$NIX_STATE_DIR"/profiles
-    rm -f $profiles/*
+    rm -rf $profiles
 }
 
 clearStore() {
diff --git a/tests/nix-channel.sh b/tests/nix-channel.sh
index 4819b57c91c0..eb1d572953d7 100644
--- a/tests/nix-channel.sh
+++ b/tests/nix-channel.sh
@@ -9,9 +9,9 @@ rm -f $TEST_ROOT/.nix-channels
 export HOME=$TEST_ROOT
 
 # Test add/list/remove.
-nix-channel --add http://foo/bar
+nix-channel --add http://foo/bar xyzzy
 nix-channel --list | grep -q http://foo/bar
-nix-channel --remove http://foo/bar
+nix-channel --remove xyzzy
 
 [ -e $TEST_ROOT/.nix-channels ]
 [ "$(cat $TEST_ROOT/.nix-channels)" = '' ]