diff options
94 files changed, 1170 insertions, 709 deletions
diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 000000000000..2d1117f4bda6 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,16 @@ +((c++-mode . ( + (c-file-style . "k&r") + (c-basic-offset . 4) + (indent-tabs-mode . nil) + (tab-width . 4) + (show-trailing-whitespace . t) + (indicate-empty-lines . t) + (eval . (c-set-offset 'innamespace 0)) + (eval . (c-set-offset 'defun-open 0)) + (eval . (c-set-offset 'inline-open 0)) + (eval . (c-set-offset 'arglist-intro '+)) + (eval . (c-set-offset 'arglist-cont 0)) + (eval . (c-set-offset 'arglist-cont-nonempty '+)) + (eval . (c-set-offset 'substatement-open 0)) + (eval . (c-set-offset 'access-label '-)) + ))) diff --git a/configure.ac b/configure.ac index df92950ff0cc..3a24053bb6a1 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,7 @@ test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var # Solaris-specific stuff. +AC_STRUCT_DIRENT_D_TYPE if test "$sys_name" = sunos; then # Solaris requires -lsocket -lnsl for network functions LIBS="-lsocket -lnsl $LIBS" diff --git a/corepkgs/buildenv.nix b/corepkgs/buildenv.nix index ab1ce13f2cf6..70981a752c3c 100644 --- a/corepkgs/buildenv.nix +++ b/corepkgs/buildenv.nix @@ -8,7 +8,7 @@ derivation { builder = perl; args = [ "-w" ./buildenv.pl ]; - manifest = manifest; + inherit manifest; # !!! grmbl, need structured data for passing this in a clean way. derivations = @@ -23,6 +23,9 @@ derivation { # network traffic, so don't do that. preferLocalBuild = true; + # Also don't bother substituting. + allowSubstitutes = false; + __sandboxProfile = '' (allow sysctl-read) (allow file-read* diff --git a/corepkgs/buildenv.pl b/corepkgs/buildenv.pl index 264442104320..dacc53701a01 100644 --- a/corepkgs/buildenv.pl +++ b/corepkgs/buildenv.pl @@ -149,7 +149,7 @@ foreach my $pkg (@pkgs) { # Symlink to the packages that have been "propagated" by packages -# installed by the user (i.e., package X declares that it want Y +# installed by the user (i.e., package X declares that it wants Y # installed as well). We do these later because they have a lower # priority in case of collisions. my $priorityCounter = 1000; # don't care about collisions diff --git a/doc/manual/command-ref/nix-collect-garbage.xml b/doc/manual/command-ref/nix-collect-garbage.xml index c88851299152..35a78c5b2015 100644 --- a/doc/manual/command-ref/nix-collect-garbage.xml +++ b/doc/manual/command-ref/nix-collect-garbage.xml @@ -28,6 +28,7 @@ <arg choice='plain'><option>--print-dead</option></arg> <arg choice='plain'><option>--delete</option></arg> </group> + <arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg> <arg><option>--dry-run</option></arg> </cmdsynopsis> </refsynopsisdiv> diff --git a/doc/manual/command-ref/nix-shell.xml b/doc/manual/command-ref/nix-shell.xml index 9e3e6d1882ba..6f00e28cacaa 100644 --- a/doc/manual/command-ref/nix-shell.xml +++ b/doc/manual/command-ref/nix-shell.xml @@ -176,6 +176,22 @@ also <xref linkend="sec-common-options" />.</phrase></para> </refsection> +<refsection><title>Environment variables</title> + +<variablelist> + + <varlistentry><term><envar>NIX_BUILD_SHELL</envar></term> + + <listitem><para>Shell used to start the interactive environment. + Defaults to the <command>bash</command> found in <envar>PATH</envar>.</para></listitem> + + </varlistentry> + +</variablelist> + +</refsection> + + <refsection><title>Examples</title> <para>To build the dependencies of the package Pan, and start an @@ -252,8 +268,8 @@ dependencies in Nixpkgs.</para> <para>The lines starting with <literal>#! nix-shell</literal> specify <command>nix-shell</command> options (see above). Note that you cannot write <literal>#1 /usr/bin/env nix-shell -i ...</literal> because -<command>/usr/bin/env</command> does not support passing options to -the interpreter.</para> +many operating systems only allow one argument in +<literal>#!</literal> lines.</para> <para>For example, here is a Python script that depends on Python and the <literal>prettytable</literal> package: diff --git a/doc/manual/command-ref/nix-store.xml b/doc/manual/command-ref/nix-store.xml index e21d53d8b9f0..bf03c802b87d 100644 --- a/doc/manual/command-ref/nix-store.xml +++ b/doc/manual/command-ref/nix-store.xml @@ -194,6 +194,25 @@ printed.)</para> </varlistentry> + <varlistentry><term><option>--check</option></term> + + <listitem><para>This option allows you to check whether a + derivation is deterministic. It rebuilds the specified derivation + and checks whether the result is bitwise-identical with the + existing outputs, printing an error if that’s not the case. The + outputs of the specified derivation must already exist. When used + with <option>-K</option>, if an output path is not identical to + the corresponding output from the previous build, the new output + path is left in + <filename>/nix/store/<replaceable>name</replaceable>-check.</filename></para> + + <para>See also the <option>build-repeat</option> configuration + option, which repeats a derivation a number of times and prevents + its outputs from being registered as “valid” in the Nix store + unless they are identical.</para></listitem> + + </varlistentry> + </variablelist> </refsection> @@ -212,6 +231,14 @@ $ nix-store -r $(nix-instantiate ./test.nix) This is essentially what <link linkend="sec-nix-build"><command>nix-build</command></link> does.</para> +<para>To test whether a previously-built derivation is deterministic: + +<screen> +$ nix-build -r '<nixpkgs>' -A hello --check -K +</screen> + +</para> + </refsection> diff --git a/doc/manual/expressions/builtins.xml b/doc/manual/expressions/builtins.xml index 13cc2221e1b5..eae5f5a029bf 100644 --- a/doc/manual/expressions/builtins.xml +++ b/doc/manual/expressions/builtins.xml @@ -348,6 +348,24 @@ stdenv.mkDerivation { </varlistentry> + <varlistentry><term><function>builtins.functionArgs</function> + <replaceable>f</replaceable></term> + + <listitem><para> + Return a set containing the names of the formal arguments expected + by the function <replaceable>f</replaceable>. + The value of each attribute is a Boolean denoting whether the corresponding + argument has a default value. For instance, + <literal>functionArgs ({ x, y ? 123}: ...) = { x = false; y = true; }</literal>. + </para> + + <para>"Formal argument" here refers to the attributes pattern-matched by + the function. Plain lambdas are not included, e.g. + <literal>functionArgs (x: ...) = { }</literal>. + </para></listitem> + </varlistentry> + + <varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term> <listitem><para>Convert a JSON string to a Nix diff --git a/doc/manual/installation/prerequisites-source.xml b/doc/manual/installation/prerequisites-source.xml index 01d52c74030a..49036d940bd4 100644 --- a/doc/manual/installation/prerequisites-source.xml +++ b/doc/manual/installation/prerequisites-source.xml @@ -36,7 +36,7 @@ distribution does not provide them.</para></listitem> <listitem><para>The <link - xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm + xlink:href="http://www.hboehm.info/gc/">Boehm garbage collector</link> to reduce the evaluator’s memory consumption (optional). To enable it, install <literal>pkgconfig</literal> and the Boehm garbage collector, and diff --git a/doc/manual/release-notes/release-notes.xml b/doc/manual/release-notes/release-notes.xml index 0aa3e8717aa1..8c2deb394183 100644 --- a/doc/manual/release-notes/release-notes.xml +++ b/doc/manual/release-notes/release-notes.xml @@ -12,6 +12,7 @@ </partintro> --> +<xi:include href="rl-1.12.xml" /> <xi:include href="rl-1.11.xml" /> <xi:include href="rl-1.10.xml" /> <xi:include href="rl-1.9.xml" /> diff --git a/doc/manual/release-notes/rl-0.10.1.xml b/doc/manual/release-notes/rl-0.10.1.xml index 05cd2f654353..95829323d4fb 100644 --- a/doc/manual/release-notes/rl-0.10.1.xml +++ b/doc/manual/release-notes/rl-0.10.1.xml @@ -4,10 +4,10 @@ version="5.0" xml:id="ch-relnotes-0.10.1"> -<title>Release 0.10.1 (October 11, 2006)</title> +<title>Release 0.10.1 (2006-10-11)</title> <para>This release fixes two somewhat obscure bugs that occur when evaluating Nix expressions that are stored inside the Nix store (<literal>NIX-67</literal>). These do not affect most users.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.10.xml b/doc/manual/release-notes/rl-0.10.xml index 7815ae75b1b8..9afec4de94de 100644 --- a/doc/manual/release-notes/rl-0.10.xml +++ b/doc/manual/release-notes/rl-0.10.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.10"> -<title>Release 0.10 (October 6, 2006)</title> +<title>Release 0.10 (2006-10-06)</title> <note><para>This version of Nix uses Berkeley DB 4.4 instead of 4.3. The database is upgraded automatically, but you should be careful not @@ -320,4 +320,4 @@ irreversible.</para></warning> </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.11.xml b/doc/manual/release-notes/rl-0.11.xml index 9c5d8b8beb4b..7ad0ab5b71ad 100644 --- a/doc/manual/release-notes/rl-0.11.xml +++ b/doc/manual/release-notes/rl-0.11.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-0.11"> -<title>Release 0.11 (December 31, 2007)</title> +<title>Release 0.11 (2007-12-31)</title> <para>Nix 0.11 has many improvements over the previous stable release. The most important improvement is secure multi-user support. It also @@ -258,4 +258,4 @@ on Nix. Here is an (incomplete) list:</para> </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.12.xml b/doc/manual/release-notes/rl-0.12.xml index 1f04609b1654..fdba8c4d577f 100644 --- a/doc/manual/release-notes/rl-0.12.xml +++ b/doc/manual/release-notes/rl-0.12.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-0.12"> -<title>Release 0.12 (November 20, 2008)</title> +<title>Release 0.12 (2008-11-20)</title> <itemizedlist> @@ -172,4 +172,4 @@ the following paths will be downloaded/copied (30.02 MiB): </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.13.xml b/doc/manual/release-notes/rl-0.13.xml index 9cf144e4ea23..cce2e4a26b05 100644 --- a/doc/manual/release-notes/rl-0.13.xml +++ b/doc/manual/release-notes/rl-0.13.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-0.13"> -<title>Release 0.13 (November 5, 2009)</title> +<title>Release 0.13 (2009-11-05)</title> <para>This is primarily a bug fix release. It has some new features:</para> @@ -103,4 +103,4 @@ features:</para> </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.14.xml b/doc/manual/release-notes/rl-0.14.xml index a671db3b8588..e5fe9da78e7e 100644 --- a/doc/manual/release-notes/rl-0.14.xml +++ b/doc/manual/release-notes/rl-0.14.xml @@ -2,7 +2,9 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0" - xml:id="ssec-relnotes-0.14"><title>Release 0.14 (February 4, 2010)</title> + xml:id="ssec-relnotes-0.14"> + +<title>Release 0.14 (2010-02-04)</title> <para>This release has the following improvements:</para> @@ -41,4 +43,4 @@ </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.15.xml b/doc/manual/release-notes/rl-0.15.xml index b76f674ba123..9f58a8efc5d4 100644 --- a/doc/manual/release-notes/rl-0.15.xml +++ b/doc/manual/release-notes/rl-0.15.xml @@ -4,11 +4,11 @@ version="5.0" xml:id="ssec-relnotes-0.15"> -<title>Release 0.15 (March 17, 2010)</title> +<title>Release 0.15 (2010-03-17)</title> <para>This is a bug-fix release. Among other things, it fixes building on Mac OS X (Snow Leopard), and improves the contents of <filename>/etc/passwd</filename> and <filename>/etc/group</filename> in <literal>chroot</literal> builds.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.16.xml b/doc/manual/release-notes/rl-0.16.xml index 5254e5f9d658..af1edc0ebbe0 100644 --- a/doc/manual/release-notes/rl-0.16.xml +++ b/doc/manual/release-notes/rl-0.16.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-0.16"> -<title>Release 0.16 (August 17, 2010)</title> +<title>Release 0.16 (2010-08-17)</title> <para>This release has the following improvements:</para> @@ -52,4 +52,4 @@ </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.6.xml b/doc/manual/release-notes/rl-0.6.xml index 83d9dc897c42..6dc6521d3c2a 100644 --- a/doc/manual/release-notes/rl-0.6.xml +++ b/doc/manual/release-notes/rl-0.6.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.6"> -<title>Release 0.6 (November 14, 2004)</title> +<title>Release 0.6 (2004-11-14)</title> <itemizedlist> @@ -119,4 +119,4 @@ </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.7.xml b/doc/manual/release-notes/rl-0.7.xml index fc8997fc1e36..6f95db4367db 100644 --- a/doc/manual/release-notes/rl-0.7.xml +++ b/doc/manual/release-notes/rl-0.7.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.7"> -<title>Release 0.7 (January 12, 2005)</title> +<title>Release 0.7 (2005-01-12)</title> <itemizedlist> @@ -32,4 +32,4 @@ </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.8.1.xml b/doc/manual/release-notes/rl-0.8.1.xml index b4a855b553a5..f7ffca0f8d59 100644 --- a/doc/manual/release-notes/rl-0.8.1.xml +++ b/doc/manual/release-notes/rl-0.8.1.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.8.1"> -<title>Release 0.8.1 (April 13, 2005)</title> +<title>Release 0.8.1 (2005-04-13)</title> <para>This is a bug fix release.</para> @@ -18,4 +18,4 @@ </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.8.xml b/doc/manual/release-notes/rl-0.8.xml index 970abb6e85e0..784b26c6b7d3 100644 --- a/doc/manual/release-notes/rl-0.8.xml +++ b/doc/manual/release-notes/rl-0.8.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.8"> -<title>Release 0.8 (April 11, 2005)</title> +<title>Release 0.8 (2005-04-11)</title> <para>NOTE: the hashing scheme in Nix 0.8 changed (as detailed below). As a result, <command>nix-pull</command> manifests and channels built @@ -243,4 +243,4 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen> </para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.9.1.xml b/doc/manual/release-notes/rl-0.9.1.xml index a7d093aa8e02..85d11f416877 100644 --- a/doc/manual/release-notes/rl-0.9.1.xml +++ b/doc/manual/release-notes/rl-0.9.1.xml @@ -4,10 +4,10 @@ version="5.0" xml:id="ch-relnotes-0.9.1"> -<title>Release 0.9.1 (September 20, 2005)</title> +<title>Release 0.9.1 (2005-09-20)</title> <para>This bug fix release addresses a problem with the ATerm library when the <option>--with-aterm</option> flag in <command>configure</command> was <emphasis>not</emphasis> used.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.9.2.xml b/doc/manual/release-notes/rl-0.9.2.xml index 33141c8e94ff..cb705e98ac25 100644 --- a/doc/manual/release-notes/rl-0.9.2.xml +++ b/doc/manual/release-notes/rl-0.9.2.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.9.2"> -<title>Release 0.9.2 (September 21, 2005)</title> +<title>Release 0.9.2 (2005-09-21)</title> <para>This bug fix release fixes two problems on Mac OS X: @@ -25,4 +25,4 @@ </para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-0.9.xml b/doc/manual/release-notes/rl-0.9.xml index 07dd87cd9f61..fd1e633f78ea 100644 --- a/doc/manual/release-notes/rl-0.9.xml +++ b/doc/manual/release-notes/rl-0.9.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ch-relnotes-0.9"> -<title>Release 0.9 (September 16, 2005)</title> +<title>Release 0.9 (2005-09-16)</title> <para>NOTE: this version of Nix uses Berkeley DB 4.3 instead of 4.2. The database is upgraded automatically, but you should be careful not @@ -95,4 +95,4 @@ svnService = derivation { </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.0.xml b/doc/manual/release-notes/rl-1.0.xml index d000014eb311..ff11168d0932 100644 --- a/doc/manual/release-notes/rl-1.0.xml +++ b/doc/manual/release-notes/rl-1.0.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.0"> -<title>Release 1.0 (May 11, 2012)</title> +<title>Release 1.0 (2012-05-11)</title> <para>There have been numerous improvements and bug fixes since the previous release. Here are the most significant:</para> @@ -116,4 +116,4 @@ previous release. Here are the most significant:</para> </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.1.xml b/doc/manual/release-notes/rl-1.1.xml index 7ee076f193e9..2f26e7a24273 100644 --- a/doc/manual/release-notes/rl-1.1.xml +++ b/doc/manual/release-notes/rl-1.1.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.1"> -<title>Release 1.1 (July 18, 2012)</title> +<title>Release 1.1 (2012-07-18)</title> <para>This release has the following improvements:</para> @@ -97,4 +97,4 @@ </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.10.xml b/doc/manual/release-notes/rl-1.10.xml index 7a90087487e9..689a95466343 100644 --- a/doc/manual/release-notes/rl-1.10.xml +++ b/doc/manual/release-notes/rl-1.10.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.10"> -<title>Release 1.10 (September 3, 2015)</title> +<title>Release 1.10 (2015-09-03)</title> <para>This is primarily a bug fix release. It also has a number of new features:</para> diff --git a/doc/manual/release-notes/rl-1.11.xml b/doc/manual/release-notes/rl-1.11.xml index fb6e4abfef52..efb03d61393f 100644 --- a/doc/manual/release-notes/rl-1.11.xml +++ b/doc/manual/release-notes/rl-1.11.xml @@ -4,12 +4,123 @@ version="5.0" xml:id="ssec-relnotes-1.11"> -<title>Release 1.11 (TODO: date, 2015)</title> +<title>Release 1.11 (2016-01-19)</title> -<para>TODO: This is primarily a bug fix release. It also has a number of new +<para>This is primarily a bug fix release. It also has a number of new features:</para> <itemizedlist> + + <listitem> + <para><command>nix-prefetch-url</command> can now download URLs + specified in a Nix expression. For example, + +<screen> +$ nix-prefetch-url -A hello.src +</screen> + + will prefetch the file specified by the + <function>fetchurl</function> call in the attribute + <literal>hello.src</literal> from the Nix expression in the + current directory, and print the cryptographic hash of the + resulting file on stdout. This differs from <literal>nix-build -A + hello.src</literal> in that it doesn't verify the hash, and is + thus useful when you’re updating a Nix expression.</para> + + <para>You can also prefetch the result of functions that unpack a + tarball, such as <function>fetchFromGitHub</function>. For example: + +<screen> +$ nix-prefetch-url --unpack https://github.com/NixOS/patchelf/archive/0.8.tar.gz +</screen> + + or from a Nix expression: + +<screen> +$ nix-prefetch-url -A nix-repl.src +</screen> + + </para> + + </listitem> + + <listitem> + <para>The builtin function + <function><nix/fetchurl.nix></function> now supports + downloading and unpacking NARs. This removes the need to have + multiple downloads in the Nixpkgs stdenv bootstrap process (like a + separate busybox binary for Linux, or curl/mkdir/sh/bzip2 for + Darwin). Now all those files can be combined into a single NAR, + optionally compressed using <command>xz</command>.</para> + </listitem> + + <listitem> + <para>Nix now supports SHA-512 hashes for verifying fixed-output + derivations, and in <function>builtins.hashString</function>.</para> + </listitem> + + <listitem> + <para> + The new flag <option>--option build-repeat + <replaceable>N</replaceable></option> will cause every build to + be executed <replaceable>N</replaceable>+1 times. If the build + output differs between any round, the build is rejected, and the + output paths are not registered as valid. This is primarily + useful to verify build determinism. (We already had a + <option>--check</option> option to repeat a previously succeeded + build. However, with <option>--check</option>, non-deterministic + builds are registered in the DB. Preventing that is useful for + Hydra to ensure that non-deterministic builds don't end up + getting published to the binary cache.) + </para> + </listitem> + + <listitem> + <para> + The options <option>--check</option> and <option>--option + build-repeat <replaceable>N</replaceable></option>, if they + detect a difference between two runs of the same derivation and + <option>-K</option> is given, will make the output of the other + run available under + <filename><replaceable>store-path</replaceable>-check</filename>. This + makes it easier to investigate the non-determinism using tools + like <command>diffoscope</command>, e.g., + +<screen> +$ nix-build pkgs/stdenv/linux -A stage1.pkgs.zlib --check -K +error: derivation ‘/nix/store/l54i8wlw2265…-zlib-1.2.8.drv’ may not +be deterministic: output ‘/nix/store/11a27shh6n2i…-zlib-1.2.8’ +differs from ‘/nix/store/11a27shh6n2i…-zlib-1.2.8-check’ + +$ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zlib-1.2.8-check +… +├── lib/libz.a +│ ├── metadata +│ │ @@ -1,15 +1,15 @@ +│ │ -rw-r--r-- 30001/30000 3096 Jan 12 15:20 2016 adler32.o +… +│ │ +rw-r--r-- 30001/30000 3096 Jan 12 15:28 2016 adler32.o +… +</screen> + + </para></listitem> + + <listitem> + <para>Improved FreeBSD support.</para> + </listitem> + + <listitem> + <para><command>nix-env -qa --xml --meta</command> now prints + license information.</para> + </listitem> + + <listitem> + <para>The maximum number of parallel TCP connections that the + binary cache substituter will use has been decreased from 150 to + 25. This should prevent upsetting some broken NAT routers, and + also improves performance.</para> + </listitem> + <listitem> <para>The Nix language now supports floating point numbers. They are based on regular C++ <literal>float</literal> and compatible with @@ -19,12 +130,19 @@ features:</para> </listitem> <listitem> <para>All "chroot"-containing strings got renamed to "sandbox". - In particular, some nix options got renamed, but the old names + In particular, some Nix options got renamed, but the old names are still accepted as lower-priority aliases. </para> </listitem> + </itemizedlist> -<para>This release has contributions from TODO.</para> +<para>This release has contributions from Anders Claesson, Anthony +Cowley, Bjørn Forsman, Brian McKenna, Danny Wilson, davidak, Eelco Dolstra, +Fabian Schmitthenner, FrankHB, Ilya Novoselov, janus, Jim Garrison, John +Ericson, Jude Taylor, Ludovic Courtès, Manuel Jacob, Mathnerd314, +Pascal Wittmann, Peter Simons, Philip Potter, Preston Bennes, Rommel +M. Martinez, Sander van der Burg, Shea Levy, Tim Cuthbertson, Tuomas +Tynkkynen, Utku Demir and Vladimír Čunát.</para> </section> diff --git a/doc/manual/release-notes/rl-1.12.xml b/doc/manual/release-notes/rl-1.12.xml new file mode 100644 index 000000000000..d6864b3f55d1 --- /dev/null +++ b/doc/manual/release-notes/rl-1.12.xml @@ -0,0 +1,24 @@ +<section xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="ssec-relnotes-1.12"> + +<title>Release 1.12 (TBA)</title> + +<para>This release has the following new features:</para> + +<itemizedlist> + + <listitem> + <para>It is no longer necessary to set the + <envar>NIX_REMOTE</envar> environment variable if you need to use + the Nix daemon. Nix will use the daemon automatically if you don’t + have write access to the Nix database.</para> + </listitem> + +</itemizedlist> + +<para>This release has contributions from TBD.</para> + +</section> diff --git a/doc/manual/release-notes/rl-1.2.xml b/doc/manual/release-notes/rl-1.2.xml index 6c05444ff76e..dc272c420ddb 100644 --- a/doc/manual/release-notes/rl-1.2.xml +++ b/doc/manual/release-notes/rl-1.2.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.2"> -<title>Release 1.2 (December 6, 2012)</title> +<title>Release 1.2 (2012-12-06)</title> <para>This release has the following improvements and changes:</para> @@ -154,4 +154,4 @@ $ mount -o remount,ro,bind /nix/store <para>This release has contributions from Eelco Dolstra, Florian Friesdorf, Mats Erik Andersson and Shea Levy.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.3.xml b/doc/manual/release-notes/rl-1.3.xml index 04169ed01b82..e2009ee3ba4b 100644 --- a/doc/manual/release-notes/rl-1.3.xml +++ b/doc/manual/release-notes/rl-1.3.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.3"> -<title>Release 1.3 (January 4, 2013)</title> +<title>Release 1.3 (2013-01-04)</title> <para>This is primarily a bug fix release. When this version is first run on Linux, it removes any immutable bits from the Nix store and @@ -16,4 +16,4 @@ efficient.)</para> <para>This release has contributions from Eelco Dolstra and Stuart Pernsteiner.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.4.xml b/doc/manual/release-notes/rl-1.4.xml index d48e43f21c1b..aefb22f2b934 100644 --- a/doc/manual/release-notes/rl-1.4.xml +++ b/doc/manual/release-notes/rl-1.4.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.4"> -<title>Release 1.4 (February 26, 2013)</title> +<title>Release 1.4 (2013-02-26)</title> <para>This release fixes a security bug in multi-user operation. It was possible for derivations to cause the mode of files outside of the @@ -36,4 +36,4 @@ xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.5.1.xml b/doc/manual/release-notes/rl-1.5.1.xml index 046960313ffd..035c8dbcbb16 100644 --- a/doc/manual/release-notes/rl-1.5.1.xml +++ b/doc/manual/release-notes/rl-1.5.1.xml @@ -4,9 +4,9 @@ version="5.0" xml:id="ssec-relnotes-1.5.1"> -<title>Release 1.5.1 (February 28, 2013)</title> +<title>Release 1.5.1 (2013-02-28)</title> <para>The bug fix to the bug fix had a bug itself, of course. But this time it will work for sure!</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.5.2.xml b/doc/manual/release-notes/rl-1.5.2.xml index d2f53bbdc0bd..7e81dd243284 100644 --- a/doc/manual/release-notes/rl-1.5.2.xml +++ b/doc/manual/release-notes/rl-1.5.2.xml @@ -4,9 +4,9 @@ version="5.0" xml:id="ssec-relnotes-1.5.2"> -<title>Release 1.5.2 (May 13, 2013)</title> +<title>Release 1.5.2 (2013-05-13)</title> <para>This is primarily a bug fix release. It has contributions from Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.5.xml b/doc/manual/release-notes/rl-1.5.xml index 84e0e9024946..8e279d7693e0 100644 --- a/doc/manual/release-notes/rl-1.5.xml +++ b/doc/manual/release-notes/rl-1.5.xml @@ -4,9 +4,9 @@ version="5.0" xml:id="ssec-relnotes-1.5"> -<title>Release 1.5 (February 27, 2013)</title> +<title>Release 1.5 (2013-02-27)</title> <para>This is a brown paper bag release to fix a regression introduced by the hard link security fix in 1.4.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.6.1.xml b/doc/manual/release-notes/rl-1.6.1.xml index 1ca4dfebacdc..9ecc52734737 100644 --- a/doc/manual/release-notes/rl-1.6.1.xml +++ b/doc/manual/release-notes/rl-1.6.1.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.6.1"> -<title>Release 1.6.1 (October 28, 2013)</title> +<title>Release 1.6.1 (2013-10-28)</title> <para>This is primarily a bug fix release. Changes of interest are:</para> @@ -66,4 +66,4 @@ are:</para> </itemizedlist> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.6.xml b/doc/manual/release-notes/rl-1.6.xml index a3d61faf2b73..580563420949 100644 --- a/doc/manual/release-notes/rl-1.6.xml +++ b/doc/manual/release-notes/rl-1.6.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.6.0"> -<title>Release 1.6 (September 10, 2013)</title> +<title>Release 1.6 (2013-09-10)</title> <para>In addition to the usual bug fixes, this release has several new features:</para> @@ -124,4 +124,4 @@ in pkgs.bar Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea Levy.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.7.xml b/doc/manual/release-notes/rl-1.7.xml index a9863b99cba0..44ecaa78da5f 100644 --- a/doc/manual/release-notes/rl-1.7.xml +++ b/doc/manual/release-notes/rl-1.7.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.7"> -<title>Release 1.7 (April 11, 2014)</title> +<title>Release 1.7 (2014-04-11)</title> <para>In addition to the usual bug fixes, this release has the following new features:</para> @@ -260,4 +260,4 @@ error: attribute `nixUnstabl' missing, at /etc/nixos/configurations/misc/eelco/m Eelco Dolstra, Ian-Woo Kim, Ludovic Courtès, Maxim Ivanov, Petr Rockai, Ricardo M. Correia and Shea Levy.</para> -</section> \ No newline at end of file +</section> diff --git a/doc/manual/release-notes/rl-1.8.xml b/doc/manual/release-notes/rl-1.8.xml index b7acc1773baa..48caac2c6b60 100644 --- a/doc/manual/release-notes/rl-1.8.xml +++ b/doc/manual/release-notes/rl-1.8.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.8"> -<title>Release 1.8 (December 14, 2014)</title> +<title>Release 1.8 (2014-12-14)</title> <itemizedlist> diff --git a/doc/manual/release-notes/rl-1.9.xml b/doc/manual/release-notes/rl-1.9.xml index e0c79e751ed7..c8406bd2077c 100644 --- a/doc/manual/release-notes/rl-1.9.xml +++ b/doc/manual/release-notes/rl-1.9.xml @@ -4,7 +4,7 @@ version="5.0" xml:id="ssec-relnotes-1.9"> -<title>Release 1.9 (June 12, 2015)</title> +<title>Release 1.9 (2015-06-12)</title> <para>In addition to the usual bug fixes, this release has the following new features:</para> diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index 07d81aa3ab05..5a1e8424f036 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -6,10 +6,10 @@ #undef do_open #undef do_close -#include <store-api.hh> -#include <globals.hh> -#include <misc.hh> -#include <util.hh> +#include "derivations.hh" +#include "globals.hh" +#include "store-api.hh" +#include "util.hh" #if HAVE_SODIUM #include <sodium.h> @@ -19,19 +19,21 @@ using namespace nix; -void doInit() +static ref<Store> store() { - if (!store) { + static std::shared_ptr<Store> _store; + if (!_store) { try { settings.processEnvironment(); settings.loadConfFile(); settings.update(); settings.lockCPU = false; - store = openStore(); + _store = openStore(); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } } + return ref<Store>(_store); } @@ -45,7 +47,7 @@ PROTOTYPES: ENABLE void init() CODE: - doInit(); + store(); void setVerbosity(int level) @@ -56,10 +58,9 @@ void setVerbosity(int level) int isValidPath(char * path) CODE: try { - doInit(); - RETVAL = store->isValidPath(path); + RETVAL = store()->isValidPath(path); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } OUTPUT: RETVAL @@ -68,45 +69,41 @@ int isValidPath(char * path) SV * queryReferences(char * path) PPCODE: try { - doInit(); PathSet paths; - store->queryReferences(path, paths); + store()->queryReferences(path, paths); for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * queryPathHash(char * path) PPCODE: try { - doInit(); - Hash hash = store->queryPathHash(path); + Hash hash = store()->queryPathHash(path); string s = "sha256:" + printHash32(hash); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * queryDeriver(char * path) PPCODE: try { - doInit(); - Path deriver = store->queryDeriver(path); + Path deriver = store()->queryDeriver(path); if (deriver == "") XSRETURN_UNDEF; XPUSHs(sv_2mortal(newSVpv(deriver.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * queryPathInfo(char * path, int base32) PPCODE: try { - doInit(); - ValidPathInfo info = store->queryPathInfo(path); + ValidPathInfo info = store()->queryPathInfo(path); if (info.deriver == "") XPUSHs(&PL_sv_undef); else @@ -120,56 +117,52 @@ SV * queryPathInfo(char * path, int base32) av_push(arr, newSVpv(i->c_str(), 0)); XPUSHs(sv_2mortal(newRV((SV *) arr))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * queryPathFromHashPart(char * hashPart) PPCODE: try { - doInit(); - Path path = store->queryPathFromHashPart(hashPart); + Path path = store()->queryPathFromHashPart(hashPart); XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * computeFSClosure(int flipDirection, int includeOutputs, ...) PPCODE: try { - doInit(); PathSet paths; for (int n = 2; n < items; ++n) - computeFSClosure(*store, SvPV_nolen(ST(n)), paths, flipDirection, includeOutputs); + store()->computeFSClosure(SvPV_nolen(ST(n)), paths, flipDirection, includeOutputs); for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * topoSortPaths(...) PPCODE: try { - doInit(); PathSet paths; for (int n = 0; n < items; ++n) paths.insert(SvPV_nolen(ST(n))); - Paths sorted = topoSortPaths(*store, paths); + Paths sorted = store()->topoSortPaths(paths); for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i) XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * followLinksToStorePath(char * path) CODE: try { - doInit(); RETVAL = newSVpv(followLinksToStorePath(path).c_str(), 0); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } OUTPUT: RETVAL @@ -178,24 +171,22 @@ SV * followLinksToStorePath(char * path) void exportPaths(int fd, int sign, ...) PPCODE: try { - doInit(); Paths paths; for (int n = 2; n < items; ++n) paths.push_back(SvPV_nolen(ST(n))); FdSink sink(fd); - exportPaths(*store, paths, sign, sink); + store()->exportPaths(paths, sign, sink); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } void importPaths(int fd) PPCODE: try { - doInit(); FdSource source(fd); - store->importPaths(false, source); + store()->importPaths(false, source); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -206,7 +197,7 @@ SV * hashPath(char * algo, int base32, char * path) string s = base32 ? printHash32(h) : printHash(h); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -217,7 +208,7 @@ SV * hashFile(char * algo, int base32, char * path) string s = base32 ? printHash32(h) : printHash(h); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -228,7 +219,7 @@ SV * hashString(char * algo, int base32, char * s) string s = base32 ? printHash32(h) : printHash(h); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -239,7 +230,7 @@ SV * convertHash(char * algo, char * s, int toBase32) string s = toBase32 ? printHash32(h) : printHash(h); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -260,7 +251,7 @@ SV * signString(SV * secretKey_, char * msg) throw Error("Nix was not compiled with libsodium, required for signed binary cache support"); #endif } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -283,7 +274,7 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg) throw Error("Nix was not compiled with libsodium, required for signed binary cache support"); #endif } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } OUTPUT: RETVAL @@ -292,24 +283,22 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg) SV * addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { - doInit(); - Path path = store->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo)); + Path path = store()->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo)); XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name) PPCODE: try { - doInit(); HashType ht = parseHashType(algo); Path path = makeFixedOutputPath(recursive, ht, parseHash16or32(ht, hash), name); XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0))); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } @@ -318,8 +307,7 @@ SV * derivationFromPath(char * drvPath) HV *hash; CODE: try { - doInit(); - Derivation drv = derivationFromPath(*store, drvPath); + Derivation drv = store()->derivationFromPath(drvPath); hash = newHV(); HV * outputs = newHV(); @@ -352,7 +340,7 @@ SV * derivationFromPath(char * drvPath) RETVAL = newRV_noinc((SV *)hash); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } OUTPUT: RETVAL @@ -361,8 +349,7 @@ SV * derivationFromPath(char * drvPath) void addTempRoot(char * storePath) PPCODE: try { - doInit(); - store->addTempRoot(storePath); + store()->addTempRoot(storePath); } catch (Error & e) { - croak(e.what()); + croak("%s", e.what()); } diff --git a/release.nix b/release.nix index a6a745e27548..e27355c59365 100644 --- a/release.nix +++ b/release.nix @@ -1,4 +1,5 @@ { nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; } +, nixpkgs ? { outPath = <nixpkgs>; revCount = 1234; shortRev = "abcdef"; } , officialRelease ? false }: @@ -76,7 +77,8 @@ let build = pkgs.lib.genAttrs systems (system: - with import <nixpkgs> { inherit system; }; + # FIXME: temporarily use a different branch for the Darwin build. + with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; }; releaseTools.nixBuild { name = "nix"; @@ -110,7 +112,8 @@ let binaryTarball = pkgs.lib.genAttrs systems (system: - with import <nixpkgs> { inherit system; }; + # FIXME: temporarily use a different branch for the Darwin build. + with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; }; let toplevel = builtins.getAttr system jobs.build; @@ -232,6 +235,26 @@ let touch $out/nix-support/hydra-build-products ''); # */ + tests.evalNixpkgs = + import <nixpkgs/pkgs/top-level/make-tarball.nix> { + inherit nixpkgs; + inherit pkgs; + nix = build.x86_64-linux; + officialRelease = false; + }; + + tests.evalNixOS = + pkgs.runCommand "eval-nixos" { buildInputs = [ build.x86_64-linux ]; } + '' + export NIX_DB_DIR=$TMPDIR + export NIX_STATE_DIR=$TMPDIR + nix-store --init + + nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run + + touch $out + ''; + # Aggregate job containing the release-critical jobs. release = pkgs.releaseTools.aggregate { @@ -262,6 +285,8 @@ let tests.remoteBuilds tests.nix-copy-closure tests.binaryTarball + tests.evalNixpkgs + tests.evalNixOS ]; }; diff --git a/scripts/download-from-binary-cache.pl.in b/scripts/download-from-binary-cache.pl.in index 60f7f9aef3d2..a4f858610aca 100644 --- a/scripts/download-from-binary-cache.pl.in +++ b/scripts/download-from-binary-cache.pl.in @@ -566,7 +566,7 @@ sub downloadBinary { die if $requireSignedBinaryCaches && !defined $info->{signedBy}; print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n"; checkURL $url; - if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) { + if (system("$Nix::Config::curl --fail --location --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) { warn "download of ‘$url’ failed" . ($! ? ": $!" : "") . "\n"; next; } diff --git a/scripts/download-using-manifests.pl.in b/scripts/download-using-manifests.pl.in index 591cd6b43a3a..ffc49f8fffde 100755 --- a/scripts/download-using-manifests.pl.in +++ b/scripts/download-using-manifests.pl.in @@ -17,8 +17,7 @@ my $logFile = "$Nix::Config::logDir/downloads"; # estimating the expected download size. my $fast = 1; -# ‘--insecure’ is fine because Nix verifies the hash of the result. -my $curl = "$Nix::Config::curl --fail --location --insecure"; +my $curl = "$Nix::Config::curl --fail --location"; # Open the manifest cache and update it if necessary. diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh index 3efe7b38435e..465cc1013cc1 100644 --- a/scripts/install-nix-from-closure.sh +++ b/scripts/install-nix-from-closure.sh @@ -92,7 +92,7 @@ p=$NIX_LINK/etc/profile.d/nix.sh added= for i in .bash_profile .bash_login .profile; do fn="$HOME/$i" - if [ -e "$fn" ]; then + if [ -w "$fn" ]; then if ! grep -q "$p" "$fn"; then echo "modifying $fn..." >&2 echo "if [ -e $p ]; then . $p; fi # added by Nix installer" >> $fn diff --git a/scripts/nix-build.in b/scripts/nix-build.in index ea099532b7cb..b93e5ab1390a 100755 --- a/scripts/nix-build.in +++ b/scripts/nix-build.in @@ -57,7 +57,7 @@ if ($runEnv && defined $ARGV[0] && $ARGV[0] !~ /nix-shell/) { while (<SCRIPT>) { chomp; if (/^\#\!\s*nix-shell (.*)$/) { - push @ARGV, shellwords(/ /, $1); + push @ARGV, shellwords($1); } } } @@ -270,7 +270,7 @@ foreach my $expr (@exprs) { my $tmp = $ENV{"TMPDIR"} // $ENV{"XDG_RUNTIME_DIR"} // "/tmp"; if ($pure) { foreach my $name (keys %ENV) { - next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER"); + next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL"); delete $ENV{$name}; } # NixOS hack: prevent /etc/bashrc from sourcing /etc/profile. diff --git a/src/libexpr/common-opts.cc b/src/libexpr/common-opts.cc index 13760490d9c4..68ab4b5cdcbf 100644 --- a/src/libexpr/common-opts.cc +++ b/src/libexpr/common-opts.cc @@ -55,7 +55,7 @@ bool parseSearchPathArg(Strings::iterator & i, Path lookupFileArg(EvalState & state, string s) { if (isUri(s)) - return downloadFileCached(s, true); + return downloadFileCached(state.store, s, true); else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { Path p = s.substr(1, s.size() - 2); return state.findFile(p); diff --git a/src/libexpr/common-opts.hh b/src/libexpr/common-opts.hh index be0f40202430..cb2732d6fe7e 100644 --- a/src/libexpr/common-opts.hh +++ b/src/libexpr/common-opts.hh @@ -4,6 +4,8 @@ namespace nix { +class Store; + /* Some common option parsing between nix-env and nix-instantiate. */ bool parseAutoArgs(Strings::iterator & i, const Strings::iterator & argsEnd, std::map<string, string> & res); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b951daacaf0c..8ce2f3dfa6af 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -248,7 +248,7 @@ static Strings parseNixPath(const string & in) } -EvalState::EvalState(const Strings & _searchPath) +EvalState::EvalState(const Strings & _searchPath, ref<Store> store) : sWith(symbols.create("<with>")) , sOutPath(symbols.create("outPath")) , sDrvPath(symbols.create("drvPath")) @@ -265,6 +265,8 @@ EvalState::EvalState(const Strings & _searchPath) , sLine(symbols.create("line")) , sColumn(symbols.create("column")) , sFunctor(symbols.create("__functor")) + , sToString(symbols.create("__toString")) + , store(store) , baseEnv(allocEnv(128)) , staticBaseEnv(false, 0) { @@ -1431,7 +1433,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, } if (v.type == tAttrs) { - Bindings::iterator i = v.attrs->find(sOutPath); + auto i = v.attrs->find(sToString); + if (i != v.attrs->end()) { + forceValue(*i->value, pos); + Value v1; + callFunction(*i->value, v, v1, pos); + return coerceToString(pos, v1, context, coerceMore, copyToStore); + } + i = v.attrs->find(sOutPath); if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos); return coerceToString(pos, *i->value, context, coerceMore, copyToStore); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 13353a43f4e0..40e05712bab1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -16,6 +16,7 @@ namespace nix { +class Store; class EvalState; @@ -69,7 +70,7 @@ public: const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, - sFile, sLine, sColumn, sFunctor; + sFile, sLine, sColumn, sFunctor, sToString; Symbol sDerivationNix; /* If set, force copying files to the Nix store even if they @@ -82,6 +83,8 @@ public: Value vEmptySet; + const ref<Store> store; + private: SrcToStore srcToStore; @@ -97,7 +100,7 @@ private: public: - EvalState(const Strings & _searchPath); + EvalState(const Strings & _searchPath, ref<Store> store); ~EvalState(); void addToSearchPath(const string & s, bool warn = false); @@ -241,6 +244,8 @@ public: /* Print statistics. */ void printStats(); + void realiseContext(const PathSet & context); + private: unsigned long nrEnvs = 0; @@ -291,7 +296,4 @@ struct InvalidPathError : EvalError #endif }; -/* Realise all paths in `context' */ -void realiseContext(const PathSet & context); - } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 7a6e48215b34..701c01aff973 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -8,6 +8,7 @@ %x STRING %x IND_STRING +%x INSIDE_DOLLAR_CURLY %{ @@ -94,6 +95,8 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~ %% +<INITIAL,INSIDE_DOLLAR_CURLY>{ + if { return IF; } then { return THEN; } @@ -131,11 +134,15 @@ or { return OR_KW; } return FLOAT; } -\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; } -\{ { PUSH_STATE(INITIAL); return '{'; } -\} { POP_STATE(); return '}'; } +\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; } +} + +\} { return '}'; } +<INSIDE_DOLLAR_CURLY>\} { POP_STATE(); return '}'; } +\{ { return '{'; } +<INSIDE_DOLLAR_CURLY>\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return '{'; } -\" { PUSH_STATE(STRING); return '"'; } +<INITIAL,INSIDE_DOLLAR_CURLY>\" { PUSH_STATE(STRING); return '"'; } <STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" | <STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ { /* It is impossible to match strings ending with '$' with one @@ -144,11 +151,11 @@ or { return OR_KW; } yylval->e = unescapeStr(data->symbols, yytext); return STR; } -<STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; } +<STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; } <STRING>\" { POP_STATE(); return '"'; } <STRING>. return yytext[0]; /* just in case: shouldn't be reached */ -\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } +<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } <IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ { yylval->e = new ExprIndStr(yytext); return IND_STR; @@ -165,7 +172,7 @@ or { return OR_KW; } yylval->e = unescapeStr(data->symbols, yytext + 2); return IND_STR; } -<IND_STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; } +<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; } <IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; } <IND_STRING>\' { yylval->e = new ExprIndStr("'"); @@ -173,6 +180,8 @@ or { return OR_KW; } } <IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */ +<INITIAL,INSIDE_DOLLAR_CURLY>{ + {PATH} { yylval->path = strdup(yytext); return PATH; } {HPATH} { yylval->path = strdup(yytext); return HPATH; } {SPATH} { yylval->path = strdup(yytext); return SPATH; } @@ -184,6 +193,7 @@ or { return OR_KW; } . return yytext[0]; +} %% diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 32c66c783328..f87aa261935b 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -606,7 +606,7 @@ void EvalState::addToSearchPath(const string & s, bool warn) } if (isUri(path)) - path = downloadFileCached(path, true); + path = downloadFileCached(store, path, true); path = absPath(path); if (pathExists(path)) { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ebc5ae143143..3c899d769253 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1,15 +1,15 @@ +#include "archive.hh" +#include "derivations.hh" +#include "download.hh" +#include "eval-inline.hh" #include "eval.hh" -#include "misc.hh" #include "globals.hh" +#include "json-to-value.hh" +#include "names.hh" #include "store-api.hh" #include "util.hh" -#include "archive.hh" -#include "value-to-xml.hh" #include "value-to-json.hh" -#include "json-to-value.hh" -#include "names.hh" -#include "eval-inline.hh" -#include "download.hh" +#include "value-to-xml.hh" #include <sys/types.h> #include <sys/stat.h> @@ -43,7 +43,7 @@ std::pair<string, string> decodeContext(const string & s) InvalidPathError::InvalidPathError(const Path & path) : EvalError(format("path ‘%1%’ is not valid") % path), path(path) {} -void realiseContext(const PathSet & context) +void EvalState::realiseContext(const PathSet & context) { PathSet drvs; for (auto & i : context) { @@ -52,16 +52,14 @@ void realiseContext(const PathSet & context) assert(isStorePath(ctx)); if (!store->isValidPath(ctx)) throw InvalidPathError(ctx); - if (!decoded.second.empty() && isDerivation(ctx)) + if (!decoded.second.empty() && nix::isDerivation(ctx)) drvs.insert(decoded.first + "!" + decoded.second); } if (!drvs.empty()) { /* For performance, prefetch all substitute info. */ PathSet willBuild, willSubstitute, unknown; unsigned long long downloadSize, narSize; - queryMissing(*store, drvs, - willBuild, willSubstitute, unknown, downloadSize, narSize); - + store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize); store->buildPaths(drvs); } } @@ -75,7 +73,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args Path path = state.coerceToPath(pos, *args[1], context); try { - realiseContext(context); + state.realiseContext(context); } catch (InvalidPathError & e) { throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%") % path % e.path % pos); @@ -83,7 +81,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args path = state.checkSourcePath(path); - if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) { + if (isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) { Derivation drv = readDerivation(path); Value & w = *state.allocValue(); state.mkAttrs(w, 3 + drv.outputs.size()); @@ -145,7 +143,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args Path path = state.coerceToPath(pos, *args[0], context); try { - realiseContext(context); + state.realiseContext(context); } catch (InvalidPathError & e) { throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%") % path % e.path % pos); @@ -259,7 +257,7 @@ struct CompareValues if (v1->type == tInt && v2->type == tFloat) return v1->integer < v2->fpoint; if (v1->type != v2->type) - throw EvalError("cannot compare values of different types"); + throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2)); switch (v1->type) { case tInt: return v1->integer < v2->integer; @@ -573,11 +571,12 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * runs. */ if (path.at(0) == '=') { /* !!! This doesn't work if readOnlyMode is set. */ - PathSet refs; computeFSClosure(*store, string(path, 1), refs); + PathSet refs; + state.store->computeFSClosure(string(path, 1), refs); for (auto & j : refs) { drv.inputSrcs.insert(j); if (isDerivation(j)) - drv.inputDrvs[j] = store->queryDerivationOutputNames(j); + drv.inputDrvs[j] = state.store->queryDerivationOutputNames(j); } } @@ -594,7 +593,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Handle derivation contexts returned by ‘builtins.storePath’. */ else if (isDerivation(path)) - drv.inputDrvs[path] = store->queryDerivationOutputNames(path); + drv.inputDrvs[path] = state.store->queryDerivationOutputNames(path); /* Otherwise it's a source file. */ else @@ -643,7 +642,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Use the masked derivation expression to compute the output path. */ - Hash h = hashDerivationModulo(*store, drv); + Hash h = hashDerivationModulo(*state.store, drv); for (auto & i : drv.outputs) if (i.second.path == "") { @@ -654,7 +653,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } /* Write the resulting term into the Nix store directory. */ - Path drvPath = writeDerivation(*store, drv, drvName, state.repair); + Path drvPath = writeDerivation(state.store, drv, drvName, state.repair); printMsg(lvlChatty, format("instantiated ‘%1%’ -> ‘%2%’") % drvName % drvPath); @@ -662,7 +661,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Optimisation, but required in read-only mode! because in that case we don't actually write store derivations, so we can't read them later. */ - drvHashes[drvPath] = hashDerivationModulo(*store, drv); + drvHashes[drvPath] = hashDerivationModulo(*state.store, drv); state.mkAttrs(v, 1 + drv.outputs.size()); mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath)); @@ -708,7 +707,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V throw EvalError(format("path ‘%1%’ is not in the Nix store, at %2%") % path % pos); Path path2 = toStorePath(path); if (!settings.readOnlyMode) - store->ensurePath(path2); + state.store->ensurePath(path2); context.insert(path2); mkString(v, path, context); } @@ -758,7 +757,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va PathSet context; Path path = state.coerceToPath(pos, *args[0], context); try { - realiseContext(context); + state.realiseContext(context); } catch (InvalidPathError & e) { throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%") % path % e.path % pos); @@ -799,7 +798,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va string path = state.forceStringNoCtx(*args[1], pos); try { - realiseContext(context); + state.realiseContext(context); } catch (InvalidPathError & e) { throw EvalError(format("cannot find ‘%1%’, since path ‘%2%’ is not valid, at %3%") % path % e.path % pos); @@ -814,7 +813,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val PathSet ctx; Path path = state.coerceToPath(pos, *args[0], ctx); try { - realiseContext(ctx); + state.realiseContext(ctx); } catch (InvalidPathError & e) { throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%") % path % e.path % pos); @@ -892,13 +891,13 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu if (path.at(0) != '~') throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos); path = string(path, 1); - } + } refs.insert(path); } Path storePath = settings.readOnlyMode ? computeStorePathForText(name, contents, refs) - : store->addTextToStore(name, contents, refs, state.repair); + : state.store->addTextToStore(name, contents, refs, state.repair); /* Note: we don't need to add `context' to the context of the result, since `storePath' itself has references to the paths @@ -964,7 +963,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args Path dstPath = settings.readOnlyMode ? computeStorePathForPath(path, true, htSHA256, filter).first - : store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair); + : state.store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair); mkString(v, dstPath, singleton<PathSet>(dstPath)); } @@ -1167,7 +1166,7 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va /* Return a set containing the names of the formal arguments expected by the function `f'. The value of each attribute is a Boolean - denoting whether has a default value. For instance, + denoting whether the corresponding argument has a default value. For instance, functionArgs ({ x, y ? 123}: ...) => { x = false; y = true; } @@ -1387,7 +1386,7 @@ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Val state.mkList(v, len); - for (unsigned int n = 0; n < len; ++n) { + for (unsigned int n = 0; n < (unsigned int) len; ++n) { Value * arg = state.allocValue(); mkInt(*arg, n); mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg); @@ -1704,7 +1703,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, } else url = state.forceStringNoCtx(*args[0], pos); - Path res = downloadFileCached(url, unpack); + Path res = downloadFileCached(state.store, url, unpack); mkString(v, res, PathSet({res})); } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index a6e8f352a0ab..88ed52497fb9 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -4,7 +4,6 @@ #include "globals.hh" #include "store-api.hh" #include "util.hh" -#include "misc.hh" #include <iostream> #include <cctype> @@ -47,22 +46,22 @@ void printGCWarning() } -void printMissing(StoreAPI & store, const PathSet & paths) +void printMissing(ref<Store> store, const PathSet & paths) { unsigned long long downloadSize, narSize; PathSet willBuild, willSubstitute, unknown; - queryMissing(store, paths, willBuild, willSubstitute, unknown, downloadSize, narSize); - printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize); + store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize); + printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize); } -void printMissing(const PathSet & willBuild, +void printMissing(ref<Store> store, const PathSet & willBuild, const PathSet & willSubstitute, const PathSet & unknown, unsigned long long downloadSize, unsigned long long narSize) { if (!willBuild.empty()) { printMsg(lvlInfo, format("these derivations will be built:")); - Paths sorted = topoSortPaths(*store, willBuild); + Paths sorted = store->topoSortPaths(willBuild); reverse(sorted.begin(), sorted.end()); for (auto & i : sorted) printMsg(lvlInfo, format(" %1%") % i); diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 32183d6a6cb0..3f3f6f7232e0 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -19,8 +19,6 @@ public: Exit(int status) : status(status) { } }; -class StoreAPI; - int handleExceptions(const string & programName, std::function<void()> fun); void initNix(); @@ -33,9 +31,11 @@ void printVersion(const string & programName); /* Ugh. No better place to put this. */ void printGCWarning(); -void printMissing(StoreAPI & store, const PathSet & paths); +class Store; + +void printMissing(ref<Store> store, const PathSet & paths); -void printMissing(const PathSet & willBuild, +void printMissing(ref<Store> store, const PathSet & willBuild, const PathSet & willSubstitute, const PathSet & unknown, unsigned long long downloadSize, unsigned long long narSize); diff --git a/src/libstore/build.cc b/src/libstore/build.cc index e00a07d3b1a0..180a558dc2e9 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -2,7 +2,6 @@ #include "references.hh" #include "pathlocks.hh" -#include "misc.hh" #include "globals.hh" #include "local-store.hh" #include "util.hh" @@ -621,11 +620,15 @@ HookInstance::HookInstance() if (dup2(builderOut.writeSide, 4) == -1) throw SysError("dupping builder's stdout/stderr"); - execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(), - (format("%1%") % settings.maxSilentTime).str().c_str(), - (format("%1%") % settings.printBuildTrace).str().c_str(), - (format("%1%") % settings.buildTimeout).str().c_str(), - NULL); + Strings args = { + baseNameOf(buildHook), + settings.thisSystem, + (format("%1%") % settings.maxSilentTime).str(), + (format("%1%") % settings.printBuildTrace).str(), + (format("%1%") % settings.buildTimeout).str() + }; + + execv(buildHook.c_str(), stringsToCharPtrs(args).data()); throw SysError(format("executing ‘%1%’") % buildHook); }); @@ -902,7 +905,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const BasicDerivation & drv { this->drv = std::unique_ptr<BasicDerivation>(new BasicDerivation(drv)); state = &DerivationGoal::haveDerivation; - name = (format("building of %1%") % showPaths(outputPaths(drv))).str(); + name = (format("building of %1%") % showPaths(drv.outputPaths())).str(); trace("created"); /* Prevent the .chroot directory from being @@ -1014,7 +1017,7 @@ void DerivationGoal::loadDerivation() assert(worker.store.isValidPath(drvPath)); /* Get the derivation. */ - drv = std::unique_ptr<BasicDerivation>(new Derivation(derivationFromPath(worker.store, drvPath))); + drv = std::unique_ptr<BasicDerivation>(new Derivation(worker.store.derivationFromPath(drvPath))); haveDerivation(); } @@ -1041,10 +1044,19 @@ void DerivationGoal::haveDerivation() for (auto & i : invalidOutputs) if (pathFailed(i)) return; + /* Reject doing a hash build of anything other than a fixed-output + derivation. */ + if (buildMode == bmHash) { + if (drv->outputs.size() != 1 || + drv->outputs.find("out") == drv->outputs.end() || + drv->outputs["out"].hashAlgo == "") + throw Error(format("cannot do a hash build of non-fixed-output derivation ‘%1%’") % drvPath); + } + /* We are first going to try to create the invalid output paths through substitutes. If that doesn't work, we'll build them. */ - if (settings.useSubstitutes && substitutesAllowed(*drv)) + if (settings.useSubstitutes && drv->substitutesAllowed()) for (auto & i : invalidOutputs) addWaitee(worker.makeSubstitutionGoal(i, buildMode == bmRepair)); @@ -1123,8 +1135,10 @@ void DerivationGoal::repairClosure() /* Get the output closure. */ PathSet outputClosure; - for (auto & i : drv->outputs) - computeFSClosure(worker.store, i.second.path, outputClosure); + for (auto & i : drv->outputs) { + if (!wantOutput(i.first, wantedOutputs)) continue; + worker.store.computeFSClosure(i.second.path, outputClosure); + } /* Filter out our own outputs (which we have already checked). */ for (auto & i : drv->outputs) @@ -1134,11 +1148,11 @@ void DerivationGoal::repairClosure() derivation is responsible for which path in the output closure. */ PathSet inputClosure; - if (useDerivation) computeFSClosure(worker.store, drvPath, inputClosure); + if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure); std::map<Path, Path> outputsToDrv; for (auto & i : inputClosure) if (isDerivation(i)) { - Derivation drv = derivationFromPath(worker.store, i); + Derivation drv = worker.store.derivationFromPath(i); for (auto & j : drv.outputs) outputsToDrv[j.second.path] = i; } @@ -1210,10 +1224,10 @@ void DerivationGoal::inputsRealised() `i' as input paths. Only add the closures of output paths that are specified as inputs. */ assert(worker.store.isValidPath(i.first)); - Derivation inDrv = derivationFromPath(worker.store, i.first); + Derivation inDrv = worker.store.derivationFromPath(i.first); for (auto & j : i.second) if (inDrv.outputs.find(j) != inDrv.outputs.end()) - computeFSClosure(worker.store, inDrv.outputs[j].path, inputPaths); + worker.store.computeFSClosure(inDrv.outputs[j].path, inputPaths); else throw Error( format("derivation ‘%1%’ requires non-existent output ‘%2%’ from input derivation ‘%3%’") @@ -1222,7 +1236,7 @@ void DerivationGoal::inputsRealised() /* Second, the input sources. */ for (auto & i : drv->inputSrcs) - computeFSClosure(worker.store, i, inputPaths); + worker.store.computeFSClosure(i, inputPaths); debug(format("added input paths %1%") % showPaths(inputPaths)); @@ -1245,46 +1259,6 @@ void DerivationGoal::inputsRealised() } -static bool isBuiltin(const BasicDerivation & drv) -{ - return string(drv.builder, 0, 8) == "builtin:"; -} - - -static bool canBuildLocally(const BasicDerivation & drv) -{ - return drv.platform == settings.thisSystem - || isBuiltin(drv) -#if __linux__ - || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux") - || (drv.platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux") -#elif __FreeBSD__ - || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd") - || (drv.platform == "i686-linux" && settings.thisSystem == "i686-freebsd") -#endif - ; -} - - -static string get(const StringPairs & map, const string & key, const string & def = "") -{ - StringPairs::const_iterator i = map.find(key); - return i == map.end() ? def : i->second; -} - - -bool willBuildLocally(const BasicDerivation & drv) -{ - return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv); -} - - -bool substitutesAllowed(const BasicDerivation & drv) -{ - return get(drv.env, "allowSubstitutes", "1") == "1"; -} - - void DerivationGoal::tryToBuild() { trace("trying to build"); @@ -1307,7 +1281,7 @@ void DerivationGoal::tryToBuild() can't acquire the lock, then continue; hopefully some other goal can start a build, and if not, the main loop will sleep a few seconds and then retry this goal. */ - if (!outputLocks.lockPaths(outputPaths(*drv), "", false)) { + if (!outputLocks.lockPaths(drv->outputPaths(), "", false)) { worker.waitForAWhile(shared_from_this()); return; } @@ -1320,7 +1294,6 @@ void DerivationGoal::tryToBuild() now hold the locks on the output paths, no other process can build this derivation, so no further checks are necessary. */ validPaths = checkPathValidity(true, buildMode == bmRepair); - assert(buildMode != bmCheck || validPaths.size() == drv->outputs.size()); if (buildMode != bmCheck && validPaths.size() == drv->outputs.size()) { debug(format("skipping build of derivation ‘%1%’, someone beat us to it") % drvPath); outputLocks.setDeletion(true); @@ -1328,7 +1301,7 @@ void DerivationGoal::tryToBuild() return; } - missingPaths = outputPaths(*drv); + missingPaths = drv->outputPaths(); if (buildMode != bmCheck) for (auto & i : validPaths) missingPaths.erase(i); @@ -1351,7 +1324,7 @@ void DerivationGoal::tryToBuild() /* Don't do a remote build if the derivation has the attribute `preferLocalBuild' set. Also, check and repair modes are only supported for local builds. */ - bool buildLocally = buildMode != bmNormal || willBuildLocally(*drv); + bool buildLocally = buildMode != bmNormal || drv->willBuildLocally(); /* Is the build hook willing to accept this job? */ if (!buildLocally) { @@ -1647,7 +1620,7 @@ HookReply DerivationGoal::tryBuildHook() list it since the remote system *probably* already has it.) */ PathSet allInputs; allInputs.insert(inputPaths.begin(), inputPaths.end()); - computeFSClosure(worker.store, drvPath, allInputs); + worker.store.computeFSClosure(drvPath, allInputs); string s; for (auto & i : allInputs) { s += i; s += ' '; } @@ -1702,7 +1675,7 @@ void DerivationGoal::startBuilder() startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds); /* Right platform? */ - if (!canBuildLocally(*drv)) { + if (!drv->canBuildLocally()) { if (settings.printBuildTrace) printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv->platform); throw Error( @@ -1710,6 +1683,10 @@ void DerivationGoal::startBuilder() % drv->platform % settings.thisSystem % drvPath); } +#if __APPLE__ + additionalSandboxProfile = get(drv->env, "__sandboxProfile"); +#endif + /* Are we doing a chroot build? Note that fixed-output derivations are never done in a chroot, mainly so that functions like fetchurl (which needs a proper /etc/resolv.conf) @@ -1723,7 +1700,13 @@ void DerivationGoal::startBuilder() throw Error("option ‘build-use-sandbox’ must be set to one of ‘true’, ‘false’ or ‘relaxed’"); if (x == "true") { if (get(drv->env, "__noChroot") == "1") - throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath); + throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, " + "but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath); +#if __APPLE__ + if (additionalSandboxProfile != "") + throw Error(format("derivation ‘%1%’ specifies a sandbox profile, " + "but this is only allowed when ‘build-use-sandbox’ is ‘relaxed’") % drvPath); +#endif useChroot = true; } else if (x == "false") @@ -1849,14 +1832,14 @@ void DerivationGoal::startBuilder() like passing all build-time dependencies of some path to a derivation that builds a NixOS DVD image. */ PathSet paths, paths2; - computeFSClosure(worker.store, storePath, paths); + worker.store.computeFSClosure(storePath, paths); paths2 = paths; for (auto & j : paths2) { if (isDerivation(j)) { - Derivation drv = derivationFromPath(worker.store, j); + Derivation drv = worker.store.derivationFromPath(j); for (auto & k : drv.outputs) - computeFSClosure(worker.store, k.second.path, paths); + worker.store.computeFSClosure(k.second.path, paths); } } @@ -1920,13 +1903,10 @@ void DerivationGoal::startBuilder() PathSet closure; for (auto & i : dirsInChroot) if (isInStore(i.second)) - computeFSClosure(worker.store, toStorePath(i.second), closure); + worker.store.computeFSClosure(toStorePath(i.second), closure); for (auto & i : closure) dirsInChroot[i] = i; -#if __APPLE__ - additionalSandboxProfile = get(drv->env, "__sandboxProfile"); -#endif string allowed = settings.get("allowed-impure-host-deps", string(DEFAULT_ALLOWED_IMPURE_PREFIXES)); PathSet allowedPaths = tokenizeString<StringSet>(allowed); @@ -1948,7 +1928,7 @@ void DerivationGoal::startBuilder() } } if (!found) - throw Error(format("derivation '%1%' requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed); + throw Error(format("derivation ‘%1%’ requested impure path ‘%2%’, but it was not in allowed-impure-host-deps (‘%3%’)") % drvPath % i % allowed); dirsInChroot[i] = i; } @@ -2170,7 +2150,7 @@ void DerivationGoal::startBuilder() size_t stackSize = 1 * 1024 * 1024; char * stack = (char *) mmap(0, stackSize, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); - if (!stack) throw SysError("allocating stack"); + if (stack == MAP_FAILED) throw SysError("allocating stack"); int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; if (!fixedOutput) flags |= CLONE_NEWNET; pid_t child = clone(childEntry, stack + stackSize, flags, this); @@ -2191,7 +2171,7 @@ void DerivationGoal::startBuilder() #endif { ProcessOptions options; - options.allowVfork = !buildUser.enabled() && !isBuiltin(*drv); + options.allowVfork = !buildUser.enabled() && !drv->isBuiltin(); pid = startProcess([&]() { runChild(); }, options); @@ -2445,7 +2425,7 @@ void DerivationGoal::runChild() const char *builder = "invalid"; string sandboxProfile; - if (isBuiltin(*drv)) { + if (drv->isBuiltin()) { ; #if __APPLE__ } else if (useChroot) { @@ -2562,7 +2542,7 @@ void DerivationGoal::runChild() writeFull(STDERR_FILENO, string("\1\n")); /* Execute the program. This should not return. */ - if (isBuiltin(*drv)) { + if (drv->isBuiltin()) { try { logType = ltFlat; if (drv->builder == "builtin:fetchurl") @@ -2625,6 +2605,8 @@ void DerivationGoal::registerOutputs() outputs to allow hard links between outputs. */ InodesSeen inodesSeen; + Path checkSuffix = "-check"; + /* Check whether the output paths were created, and grep each output path to determine what other paths it references. Also make all output paths read-only. */ @@ -2650,7 +2632,7 @@ void DerivationGoal::registerOutputs() && redirectedBadOutputs.find(path) != redirectedBadOutputs.end() && pathExists(redirected)) replaceValidPath(path, redirected); - if (buildMode == bmCheck) + if (buildMode == bmCheck && redirected != "") actualPath = redirected; } @@ -2713,12 +2695,29 @@ void DerivationGoal::registerOutputs() format("output path ‘%1%’ should be a non-executable regular file") % path); } - /* Check the hash. */ + /* Check the hash. In hash mode, move the path produced by + the derivation to its content-addressed location. */ Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath); - if (h != h2) - throw BuildError( - format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected") - % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h)); + if (buildMode == bmHash) { + Path dest = makeFixedOutputPath(recursive, ht, h2, drv->env["name"]); + printMsg(lvlError, format("build produced path ‘%1%’ with %2% hash ‘%3%’") + % dest % printHashType(ht) % printHash16or32(h2)); + if (worker.store.isValidPath(dest)) + return; + if (actualPath != dest) { + PathLocks outputLocks({dest}); + if (pathExists(dest)) + deletePath(dest); + if (rename(actualPath.c_str(), dest.c_str()) == -1) + throw SysError(format("moving ‘%1%’ to ‘%2%’") % actualPath % dest); + } + path = actualPath = dest; + } else { + if (h != h2) + throw BuildError( + format("output path ‘%1%’ has %2% hash ‘%3%’ when ‘%4%’ was expected") + % path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h)); + } } /* Get rid of all weird permissions. This also checks that @@ -2734,9 +2733,20 @@ void DerivationGoal::registerOutputs() PathSet references = scanForReferences(actualPath, allPaths, hash); if (buildMode == bmCheck) { + if (!worker.store.isValidPath(path)) continue; ValidPathInfo info = worker.store.queryPathInfo(path); - if (hash.first != info.hash) - throw Error(format("derivation ‘%1%’ may not be deterministic: hash mismatch in output ‘%2%’") % drvPath % path); + if (hash.first != info.hash) { + if (settings.keepFailed) { + Path dst = path + checkSuffix; + if (pathExists(dst)) deletePath(dst); + if (rename(actualPath.c_str(), dst.c_str())) + throw SysError(format("renaming ‘%1%’ to ‘%2%’") % actualPath % dst); + throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs from ‘%3%’") + % drvPath % path % dst); + } else + throw Error(format("derivation ‘%1%’ may not be deterministic: output ‘%2%’ differs") + % drvPath % path); + } continue; } @@ -2762,7 +2772,7 @@ void DerivationGoal::registerOutputs() for (auto & i : references) /* Don't call computeFSClosure on ourselves. */ if (actualPath != i) - computeFSClosure(worker.store, i, used); + worker.store.computeFSClosure(i, used); } else used = references; @@ -2781,9 +2791,11 @@ void DerivationGoal::registerOutputs() checkRefs("disallowedReferences", false, false); checkRefs("disallowedRequisites", false, true); - worker.store.optimisePath(path); // FIXME: combine with scanForReferences() + if (curRound == nrRounds) { + worker.store.optimisePath(path); // FIXME: combine with scanForReferences() - worker.store.markContentsGood(path); + worker.store.markContentsGood(path); + } ValidPathInfo info; info.path = path; @@ -2796,10 +2808,37 @@ void DerivationGoal::registerOutputs() if (buildMode == bmCheck) return; - if (curRound > 1 && prevInfos != infos) - throw NotDeterministic( - format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic") - % drvPath); + /* Compare the result with the previous round, and report which + path is different, if any.*/ + if (curRound > 1 && prevInfos != infos) { + assert(prevInfos.size() == infos.size()); + for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j) + if (!(*i == *j)) { + Path prev = i->path + checkSuffix; + if (pathExists(prev)) + throw NotDeterministic( + format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round") + % i->path % drvPath % prev); + else + throw NotDeterministic( + format("output ‘%1%’ of ‘%2%’ differs from previous round") + % i->path % drvPath); + } + assert(false); // shouldn't happen + } + + if (settings.keepFailed) { + for (auto & i : drv->outputs) { + Path prev = i.second.path + checkSuffix; + if (pathExists(prev)) deletePath(prev); + if (curRound < nrRounds) { + Path dst = i.second.path + checkSuffix; + if (rename(i.second.path.c_str(), dst.c_str())) + throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst); + } + } + + } if (curRound < nrRounds) { prevInfos = infos; @@ -3122,6 +3161,7 @@ void SubstitutionGoal::tryNext() /* None left. Terminate this goal and let someone else deal with it. */ debug(format("path ‘%1%’ is required, but there is no substituter that can build it") % storePath); + /* Hack: don't indicate failure if there were no substituters. In that case the calling derivation should just do a build. */ @@ -3721,7 +3761,7 @@ void Worker::waitForInput() } } - if (!waitingForAWhile.empty() && lastWokenUp + settings.pollInterval <= after) { + if (!waitingForAWhile.empty() && lastWokenUp + (time_t) settings.pollInterval <= after) { lastWokenUp = after; for (auto & i : waitingForAWhile) { GoalPtr goal = i.lock(); @@ -3817,8 +3857,17 @@ void LocalStore::repairPath(const Path & path) worker.run(goals); - if (goal->getExitCode() != Goal::ecSuccess) - throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus()); + if (goal->getExitCode() != Goal::ecSuccess) { + /* Since substituting the path didn't work, if we have a valid + deriver, then rebuild the deriver. */ + Path deriver = queryDeriver(path); + if (deriver != "" && isValidPath(deriver)) { + goals.clear(); + goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair)); + worker.run(goals); + } else + throw Error(format("cannot repair path ‘%1%’") % path, worker.exitStatus()); + } } diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 7959d5bfc3d5..d9b009d40322 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -2,7 +2,6 @@ #include "store-api.hh" #include "globals.hh" #include "util.hh" -#include "misc.hh" #include "worker-protocol.hh" @@ -27,7 +26,49 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash } -Path writeDerivation(StoreAPI & store, +Path BasicDerivation::findOutput(const string & id) const +{ + auto i = outputs.find(id); + if (i == outputs.end()) + throw Error(format("derivation has no output ‘%1%’") % id); + return i->second.path; +} + + +bool BasicDerivation::willBuildLocally() const +{ + return get(env, "preferLocalBuild") == "1" && canBuildLocally(); +} + + +bool BasicDerivation::substitutesAllowed() const +{ + return get(env, "allowSubstitutes", "1") == "1"; +} + + +bool BasicDerivation::isBuiltin() const +{ + return string(builder, 0, 8) == "builtin:"; +} + + +bool BasicDerivation::canBuildLocally() const +{ + return platform == settings.thisSystem + || isBuiltin() +#if __linux__ + || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux") + || (platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux") +#elif __FreeBSD__ + || (platform == "i686-linux" && settings.thisSystem == "x86_64-freebsd") + || (platform == "i686-linux" && settings.thisSystem == "i686-freebsd") +#endif + ; +} + + +Path writeDerivation(ref<Store> store, const Derivation & drv, const string & name, bool repair) { PathSet references; @@ -38,10 +79,10 @@ Path writeDerivation(StoreAPI & store, (that can be missing (of course) and should not necessarily be held during a garbage collection). */ string suffix = name + drvExtension; - string contents = unparseDerivation(drv); + string contents = drv.unparse(); return settings.readOnlyMode ? computeStorePathForText(suffix, contents, references) - : store.addTextToStore(suffix, contents, references, repair); + : store->addTextToStore(suffix, contents, references, repair); } @@ -149,14 +190,14 @@ static void printStrings(string & res, ForwardIterator i, ForwardIterator j) } -string unparseDerivation(const Derivation & drv) +string Derivation::unparse() const { string s; s.reserve(65536); s += "Derive(["; bool first = true; - for (auto & i : drv.outputs) { + for (auto & i : outputs) { if (first) first = false; else s += ','; s += '('; printString(s, i.first); s += ','; printString(s, i.second.path); @@ -167,7 +208,7 @@ string unparseDerivation(const Derivation & drv) s += "],["; first = true; - for (auto & i : drv.inputDrvs) { + for (auto & i : inputDrvs) { if (first) first = false; else s += ','; s += '('; printString(s, i.first); s += ','; printStrings(s, i.second.begin(), i.second.end()); @@ -175,15 +216,15 @@ string unparseDerivation(const Derivation & drv) } s += "],"; - printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end()); + printStrings(s, inputSrcs.begin(), inputSrcs.end()); - s += ','; printString(s, drv.platform); - s += ','; printString(s, drv.builder); - s += ','; printStrings(s, drv.args.begin(), drv.args.end()); + s += ','; printString(s, platform); + s += ','; printString(s, builder); + s += ','; printStrings(s, args.begin(), args.end()); s += ",["; first = true; - for (auto & i : drv.env) { + for (auto & i : env) { if (first) first = false; else s += ','; s += '('; printString(s, i.first); s += ','; printString(s, i.second); @@ -202,11 +243,11 @@ bool isDerivation(const string & fileName) } -bool isFixedOutputDrv(const Derivation & drv) +bool BasicDerivation::isFixedOutput() const { - return drv.outputs.size() == 1 && - drv.outputs.begin()->first == "out" && - drv.outputs.begin()->second.hash != ""; + return outputs.size() == 1 && + outputs.begin()->first == "out" && + outputs.begin()->second.hash != ""; } @@ -233,10 +274,10 @@ DrvHashes drvHashes; paths have been replaced by the result of a recursive call to this function, and that for fixed-output derivations we return a hash of its output path. */ -Hash hashDerivationModulo(StoreAPI & store, Derivation drv) +Hash hashDerivationModulo(Store & store, Derivation drv) { /* Return a fixed hash for fixed-output derivations. */ - if (isFixedOutputDrv(drv)) { + if (drv.isFixedOutput()) { DerivationOutputs::const_iterator i = drv.outputs.begin(); return hashString(htSHA256, "fixed:out:" + i->second.hashAlgo + ":" @@ -259,7 +300,7 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv) } drv.inputDrvs = inputs2; - return hashString(htSHA256, unparseDerivation(drv)); + return hashString(htSHA256, drv.unparse()); } @@ -286,10 +327,10 @@ bool wantOutput(const string & output, const std::set<string> & wanted) } -PathSet outputPaths(const BasicDerivation & drv) +PathSet BasicDerivation::outputPaths() const { PathSet paths; - for (auto & i : drv.outputs) + for (auto & i : outputs) paths.insert(i.second.path); return paths; } diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index f0842045f86b..6f98869b0fe0 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -50,40 +50,56 @@ struct BasicDerivation StringPairs env; virtual ~BasicDerivation() { }; + + /* Return the path corresponding to the output identifier `id' in + the given derivation. */ + Path findOutput(const string & id) const; + + bool willBuildLocally() const; + + bool substitutesAllowed() const; + + bool isBuiltin() const; + + bool canBuildLocally() const; + + /* Return true iff this is a fixed-output derivation. */ + bool isFixedOutput() const; + + /* Return the output paths of a derivation. */ + PathSet outputPaths() const; + }; struct Derivation : BasicDerivation { DerivationInputs inputDrvs; /* inputs that are sub-derivations */ + + /* Print a derivation. */ + std::string unparse() const; }; -class StoreAPI; +class Store; /* Write a derivation to the Nix store, and return its path. */ -Path writeDerivation(StoreAPI & store, +Path writeDerivation(ref<Store> store, const Derivation & drv, const string & name, bool repair = false); /* Read a derivation from a file. */ Derivation readDerivation(const Path & drvPath); -/* Print a derivation. */ -string unparseDerivation(const Derivation & drv); - -/* Check whether a file name ends with the extensions for +/* Check whether a file name ends with the extension for derivations. */ bool isDerivation(const string & fileName); -/* Return true iff this is a fixed-output derivation. */ -bool isFixedOutputDrv(const Derivation & drv); - -Hash hashDerivationModulo(StoreAPI & store, Derivation drv); +Hash hashDerivationModulo(Store & store, Derivation drv); /* Memoisation of hashDerivationModulo(). */ typedef std::map<Path, Hash> DrvHashes; -extern DrvHashes drvHashes; +extern DrvHashes drvHashes; // FIXME: global, not thread-safe /* Split a string specifying a derivation and a set of outputs (/nix/store/hash-foo!out1,out2,...) into the derivation path and @@ -95,8 +111,6 @@ Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outpu bool wantOutput(const string & output, const std::set<string> & wanted); -PathSet outputPaths(const BasicDerivation & drv); - struct Source; struct Sink; diff --git a/src/libstore/download.cc b/src/libstore/download.cc index 822e9a8db867..01ce1ea2fd4c 100644 --- a/src/libstore/download.cc +++ b/src/libstore/download.cc @@ -188,7 +188,7 @@ DownloadResult downloadFile(string url, const DownloadOptions & options) } -Path downloadFileCached(const string & url, bool unpack) +Path downloadFileCached(ref<Store> store, const string & url, bool unpack) { Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs"; createDirs(cacheDir); diff --git a/src/libstore/download.hh b/src/libstore/download.hh index c1cb25b90c32..7aec8de73e48 100644 --- a/src/libstore/download.hh +++ b/src/libstore/download.hh @@ -1,6 +1,7 @@ #pragma once #include "types.hh" + #include <string> namespace nix { @@ -18,9 +19,11 @@ struct DownloadResult string data, etag; }; +class Store; + DownloadResult downloadFile(string url, const DownloadOptions & options); -Path downloadFileCached(const string & url, bool unpack); +Path downloadFileCached(ref<Store> store, const string & url, bool unpack); MakeError(DownloadError, Error) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 998a7516a13d..d19af1cefaf2 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -1,5 +1,5 @@ +#include "derivations.hh" #include "globals.hh" -#include "misc.hh" #include "local-store.hh" #include <functional> @@ -83,7 +83,7 @@ void LocalStore::addIndirectRoot(const Path & path) } -Path addPermRoot(StoreAPI & store, const Path & _storePath, +Path Store::addPermRoot(const Path & _storePath, const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir) { Path storePath(canonPath(_storePath)); @@ -101,7 +101,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) throw Error(format("cannot create symlink ‘%1%’; already exists") % gcRoot); makeSymlink(gcRoot, storePath); - store.addIndirectRoot(gcRoot); + addIndirectRoot(gcRoot); } else { @@ -127,7 +127,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, check if the root is in a directory in or linked from the gcroots directory. */ if (settings.checkRootReachability) { - Roots roots = store.findRoots(); + Roots roots = findRoots(); if (roots.find(gcRoot) == roots.end()) printMsg(lvlError, format( @@ -139,7 +139,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath, /* Grab the global GC root, causing us to block while a GC is in progress. This prevents the set of permanent roots from increasing while a GC is in progress. */ - store.syncWithGC(); + syncWithGC(); return gcRoot; } @@ -260,19 +260,16 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) } -static void foundRoot(StoreAPI & store, - const Path & path, const Path & target, Roots & roots) +void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) { - Path storePath = toStorePath(target); - if (store.isValidPath(storePath)) - roots[path] = storePath; - else - printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath); -} - + auto foundRoot = [&](const Path & path, const Path & target) { + Path storePath = toStorePath(target); + if (isValidPath(storePath)) + roots[path] = storePath; + else + printMsg(lvlInfo, format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath); + }; -static void findRoots(StoreAPI & store, const Path & path, unsigned char type, Roots & roots) -{ try { if (type == DT_UNKNOWN) @@ -280,13 +277,13 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R if (type == DT_DIR) { for (auto & i : readDirectory(path)) - findRoots(store, path + "/" + i.name, i.type, roots); + findRoots(path + "/" + i.name, i.type, roots); } else if (type == DT_LNK) { Path target = readLink(path); if (isInStore(target)) - foundRoot(store, path, target, roots); + foundRoot(path, target); /* Handle indirect roots. */ else { @@ -300,14 +297,14 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R struct stat st2 = lstat(target); if (!S_ISLNK(st2.st_mode)) return; Path target2 = readLink(target); - if (isInStore(target2)) foundRoot(store, target, target2, roots); + if (isInStore(target2)) foundRoot(target, target2); } } } else if (type == DT_REG) { Path storePath = settings.nixStore + "/" + baseNameOf(path); - if (store.isValidPath(storePath)) + if (isValidPath(storePath)) roots[path] = storePath; } @@ -328,16 +325,16 @@ Roots LocalStore::findRoots() Roots roots; /* Process direct roots in {gcroots,manifests,profiles}. */ - nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); + findRoots(settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); if (pathExists(settings.nixStateDir + "/manifests")) - nix::findRoots(*this, settings.nixStateDir + "/manifests", DT_UNKNOWN, roots); - nix::findRoots(*this, settings.nixStateDir + "/profiles", DT_UNKNOWN, roots); + findRoots(settings.nixStateDir + "/manifests", DT_UNKNOWN, roots); + findRoots(settings.nixStateDir + "/profiles", DT_UNKNOWN, roots); return roots; } -static void addAdditionalRoots(StoreAPI & store, PathSet & roots) +void LocalStore::findRuntimeRoots(PathSet & roots) { Path rootFinder = getEnv("NIX_ROOT_FINDER", settings.nixLibexecDir + "/nix/find-runtime-roots.pl"); @@ -353,7 +350,7 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots) for (auto & i : paths) if (isInStore(i)) { Path path = toStorePath(i); - if (roots.find(path) == roots.end() && store.isValidPath(path)) { + if (roots.find(path) == roots.end() && isValidPath(path)) { debug(format("got additional root ‘%1%’") % path); roots.insert(path); } @@ -628,7 +625,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) to add running programs to the set of roots (to prevent them from being garbage collected). */ if (!options.ignoreLiveness) - addAdditionalRoots(*this, state.roots); + findRuntimeRoots(state.roots); /* Read the temporary roots. This acquires read locks on all per-process temporary root files. So after this point no paths diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index d7cd0b088d98..13179459f1c1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -397,9 +397,15 @@ int LocalStore::getSchema() } +bool LocalStore::haveWriteAccess() +{ + return access(settings.nixDBPath.c_str(), R_OK | W_OK) == 0; +} + + void LocalStore::openDB(bool create) { - if (access(settings.nixDBPath.c_str(), R_OK | W_OK)) + if (!haveWriteAccess()) throw SysError(format("Nix database directory ‘%1%’ is not writable") % settings.nixDBPath); /* Open the Nix database. */ @@ -602,10 +608,10 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe users group); we check for this case below. */ if (st.st_uid != geteuid()) { #if HAVE_LCHOWN - if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1) + if (lchown(path.c_str(), geteuid(), getegid()) == -1) #else if (!S_ISLNK(st.st_mode) && - chown(path.c_str(), geteuid(), (gid_t) -1) == -1) + chown(path.c_str(), geteuid(), getegid()) == -1) #endif throw SysError(format("changing owner of ‘%1%’ to %2%") % path % geteuid()); @@ -649,7 +655,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation & assert(isDerivation(drvName)); drvName = string(drvName, 0, drvName.size() - drvExtension.size()); - if (isFixedOutputDrv(drv)) { + if (drv.isFixedOutput()) { DerivationOutputs::const_iterator out = drv.outputs.find("out"); if (out == drv.outputs.end()) throw Error(format("derivation ‘%1%’ does not have an output named ‘out’") % drvPath); @@ -1329,7 +1335,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos) error if a cycle is detected and roll back the transaction. Cycles can only occur when a derivation has multiple outputs. */ - topoSortPaths(*this, paths); + topoSortPaths(paths); txn.commit(); } end_retry_sqlite; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index ebdf19bf1359..b6d39d345c94 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -80,7 +80,7 @@ struct SQLiteStmt }; -class LocalStore : public StoreAPI +class LocalStore : public Store { private: typedef std::map<Path, RunningSubstituter> RunningSubstituters; @@ -253,6 +253,12 @@ private: int getSchema(); +public: + + static bool haveWriteAccess(); + +private: + void openDB(bool create); void makeStoreWritable(); @@ -297,6 +303,10 @@ private: int openGCLock(LockType lockType); + void findRoots(const Path & path, unsigned char type, Roots & roots); + + void findRuntimeRoots(PathSet & roots); + void removeUnusedLinks(const GCState & state); void startSubstituter(const Path & substituter, diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 61a976c02f5c..12472f017ce4 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,21 +1,21 @@ -#include "misc.hh" -#include "store-api.hh" -#include "local-store.hh" +#include "derivations.hh" #include "globals.hh" +#include "local-store.hh" +#include "store-api.hh" namespace nix { -Derivation derivationFromPath(StoreAPI & store, const Path & drvPath) +Derivation Store::derivationFromPath(const Path & drvPath) { assertStorePath(drvPath); - store.ensurePath(drvPath); + ensurePath(drvPath); return readDerivation(drvPath); } -void computeFSClosure(StoreAPI & store, const Path & path, +void Store::computeFSClosure(const Path & path, PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers) { if (paths.find(path) != paths.end()) return; @@ -24,50 +24,42 @@ void computeFSClosure(StoreAPI & store, const Path & path, PathSet edges; if (flipDirection) { - store.queryReferrers(path, edges); + queryReferrers(path, edges); if (includeOutputs) { - PathSet derivers = store.queryValidDerivers(path); + PathSet derivers = queryValidDerivers(path); for (auto & i : derivers) edges.insert(i); } if (includeDerivers && isDerivation(path)) { - PathSet outputs = store.queryDerivationOutputs(path); + PathSet outputs = queryDerivationOutputs(path); for (auto & i : outputs) - if (store.isValidPath(i) && store.queryDeriver(i) == path) + if (isValidPath(i) && queryDeriver(i) == path) edges.insert(i); } } else { - store.queryReferences(path, edges); + queryReferences(path, edges); if (includeOutputs && isDerivation(path)) { - PathSet outputs = store.queryDerivationOutputs(path); + PathSet outputs = queryDerivationOutputs(path); for (auto & i : outputs) - if (store.isValidPath(i)) edges.insert(i); + if (isValidPath(i)) edges.insert(i); } if (includeDerivers) { - Path deriver = store.queryDeriver(path); - if (store.isValidPath(deriver)) edges.insert(deriver); + Path deriver = queryDeriver(path); + if (isValidPath(deriver)) edges.insert(deriver); } } for (auto & i : edges) - computeFSClosure(store, i, paths, flipDirection, includeOutputs, includeDerivers); -} - - -Path findOutput(const Derivation & drv, string id) -{ - for (auto & i : drv.outputs) - if (i.first == id) return i.second.path; - throw Error(format("derivation has no output ‘%1%’") % id); + computeFSClosure(i, paths, flipDirection, includeOutputs, includeDerivers); } -void queryMissing(StoreAPI & store, const PathSet & targets, +void Store::queryMissing(const PathSet & targets, PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, unsigned long long & downloadSize, unsigned long long & narSize) { @@ -105,27 +97,27 @@ void queryMissing(StoreAPI & store, const PathSet & targets, DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i); if (isDerivation(i2.first)) { - if (!store.isValidPath(i2.first)) { + if (!isValidPath(i2.first)) { // FIXME: we could try to substitute p. unknown.insert(i); continue; } - Derivation drv = derivationFromPath(store, i2.first); + Derivation drv = derivationFromPath(i2.first); PathSet invalid; for (auto & j : drv.outputs) if (wantOutput(j.first, i2.second) - && !store.isValidPath(j.second.path)) + && !isValidPath(j.second.path)) invalid.insert(j.second.path); if (invalid.empty()) continue; todoDrv.insert(i); - if (settings.useSubstitutes && substitutesAllowed(drv)) + if (settings.useSubstitutes && drv.substitutesAllowed()) query.insert(invalid.begin(), invalid.end()); } else { - if (store.isValidPath(i)) continue; + if (isValidPath(i)) continue; query.insert(i); todoNonDrv.insert(i); } @@ -134,20 +126,20 @@ void queryMissing(StoreAPI & store, const PathSet & targets, todo.clear(); SubstitutablePathInfos infos; - store.querySubstitutablePathInfos(query, infos); + querySubstitutablePathInfos(query, infos); for (auto & i : todoDrv) { DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i); // FIXME: cache this - Derivation drv = derivationFromPath(store, i2.first); + Derivation drv = derivationFromPath(i2.first); PathSet outputs; bool mustBuild = false; - if (settings.useSubstitutes && substitutesAllowed(drv)) { + if (settings.useSubstitutes && drv.substitutesAllowed()) { for (auto & j : drv.outputs) { if (!wantOutput(j.first, i2.second)) continue; - if (!store.isValidPath(j.second.path)) { + if (!isValidPath(j.second.path)) { if (infos.find(j.second.path) == infos.end()) mustBuild = true; else @@ -181,38 +173,38 @@ void queryMissing(StoreAPI & store, const PathSet & targets, } -static void dfsVisit(StoreAPI & store, const PathSet & paths, - const Path & path, PathSet & visited, Paths & sorted, - PathSet & parents) +Paths Store::topoSortPaths(const PathSet & paths) { - if (parents.find(path) != parents.end()) - throw BuildError(format("cycle detected in the references of ‘%1%’") % path); + Paths sorted; + PathSet visited, parents; - if (visited.find(path) != visited.end()) return; - visited.insert(path); - parents.insert(path); + std::function<void(const Path & path)> dfsVisit; - PathSet references; - if (store.isValidPath(path)) - store.queryReferences(path, references); + dfsVisit = [&](const Path & path) { + if (parents.find(path) != parents.end()) + throw BuildError(format("cycle detected in the references of ‘%1%’") % path); - for (auto & i : references) - /* Don't traverse into paths that don't exist. That can - happen due to substitutes for non-existent paths. */ - if (i != path && paths.find(i) != paths.end()) - dfsVisit(store, paths, i, visited, sorted, parents); + if (visited.find(path) != visited.end()) return; + visited.insert(path); + parents.insert(path); - sorted.push_front(path); - parents.erase(path); -} + PathSet references; + if (isValidPath(path)) + queryReferences(path, references); + for (auto & i : references) + /* Don't traverse into paths that don't exist. That can + happen due to substitutes for non-existent paths. */ + if (i != path && paths.find(i) != paths.end()) + dfsVisit(i); + + sorted.push_front(path); + parents.erase(path); + }; -Paths topoSortPaths(StoreAPI & store, const PathSet & paths) -{ - Paths sorted; - PathSet visited, parents; for (auto & i : paths) - dfsVisit(store, paths, i, visited, sorted, parents); + dfsVisit(i); + return sorted; } diff --git a/src/libstore/misc.hh b/src/libstore/misc.hh deleted file mode 100644 index 495c528750fb..000000000000 --- a/src/libstore/misc.hh +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "derivations.hh" - - -namespace nix { - - -/* Read a derivation, after ensuring its existence through - ensurePath(). */ -Derivation derivationFromPath(StoreAPI & store, const Path & drvPath); - -/* Place in `paths' the set of all store paths in the file system - closure of `storePath'; that is, all paths than can be directly or - indirectly reached from it. `paths' is not cleared. If - `flipDirection' is true, the set of paths that can reach - `storePath' is returned; that is, the closures under the - `referrers' relation instead of the `references' relation is - returned. */ -void computeFSClosure(StoreAPI & store, const Path & path, - PathSet & paths, bool flipDirection = false, - bool includeOutputs = false, bool includeDerivers = false); - -/* Return the path corresponding to the output identifier `id' in the - given derivation. */ -Path findOutput(const Derivation & drv, string id); - -/* Given a set of paths that are to be built, return the set of - derivations that will be built, and the set of output paths that - will be substituted. */ -void queryMissing(StoreAPI & store, const PathSet & targets, - PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, - unsigned long long & downloadSize, unsigned long long & narSize); - -bool willBuildLocally(const BasicDerivation & drv); - -bool substitutesAllowed(const BasicDerivation & drv); - - -} diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 1c87034f8e1a..eddf5bcbda65 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -161,7 +161,11 @@ bool PathLocks::lockPaths(const PathSet & _paths, PathLocks::~PathLocks() { - unlock(); + try { + unlock(); + } catch (...) { + ignoreException(); + } } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index da3f7da9d19d..cc83a838eddc 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -74,7 +74,7 @@ static void makeName(const Path & profile, unsigned int num, } -Path createGeneration(Path profile, Path outPath) +Path createGeneration(ref<Store> store, Path profile, Path outPath) { /* The new generation number should be higher than old the previous ones. */ @@ -108,7 +108,7 @@ Path createGeneration(Path profile, Path outPath) user environment etc. we've just built. */ Path generation; makeName(profile, num + 1, generation); - addPermRoot(*store, outPath, generation, false, true); + store->addPermRoot(outPath, generation, false, true); return generation; } diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index e99bbf398a86..d758d94b603c 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -31,7 +31,9 @@ typedef list<Generation> Generations; profile, sorted by generation number. */ Generations findGenerations(Path profile, int & curGen); -Path createGeneration(Path profile, Path outPath); +class Store; + +Path createGeneration(ref<Store> store, Path profile, Path outPath); void deleteGeneration(const Path & profile, unsigned int gen); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 262e4650bfb5..679210d4cb16 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -50,14 +50,8 @@ void RemoteStore::openConnection(bool reserveSpace) if (initialised) return; initialised = true; - string remoteMode = getEnv("NIX_REMOTE"); - - if (remoteMode == "daemon") - /* Connect to a daemon that does the privileged work for - us. */ - connectToDaemon(); - else - throw Error(format("invalid setting for NIX_REMOTE, ‘%1%’") % remoteMode); + /* Connect to a daemon that does the privileged work for us. */ + connectToDaemon(); from.fd = fdSocket; to.fd = fdSocket; diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index b0787c072904..f15182285e8c 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -14,7 +14,7 @@ struct FdSink; struct FdSource; -class RemoteStore : public StoreAPI +class RemoteStore : public Store { public: diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index a73ebd824264..039d07e29b3e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -220,7 +220,7 @@ Path computeStorePathForText(const string & name, const string & s, /* Return a string accepted by decodeValidPathInfo() that registers the specified paths as valid. Note: it's the responsibility of the caller to provide a closure. */ -string StoreAPI::makeValidityRegistration(const PathSet & paths, +string Store::makeValidityRegistration(const PathSet & paths, bool showDerivers, bool showHash) { string s = ""; @@ -284,12 +284,12 @@ string showPaths(const PathSet & paths) } -void exportPaths(StoreAPI & store, const Paths & paths, +void Store::exportPaths(const Paths & paths, bool sign, Sink & sink) { for (auto & i : paths) { sink << 1; - store.exportPath(i, sign, sink); + exportPath(i, sign, sink); } sink << 0; } @@ -306,15 +306,24 @@ void exportPaths(StoreAPI & store, const Paths & paths, namespace nix { -std::shared_ptr<StoreAPI> store; +ref<Store> openStore(bool reserveSpace) +{ + enum { mDaemon, mLocal, mAuto } mode; + mode = getEnv("NIX_REMOTE") == "daemon" ? mDaemon : mAuto; -std::shared_ptr<StoreAPI> openStore(bool reserveSpace) -{ - if (getEnv("NIX_REMOTE") == "") - return std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace)); - else - return std::shared_ptr<StoreAPI>(new RemoteStore()); + if (mode == mAuto) { + if (LocalStore::haveWriteAccess()) + mode = mLocal; + else if (pathExists(settings.nixDaemonSocketFile)) + mode = mDaemon; + else + mode = mLocal; + } + + return mode == mDaemon + ? (ref<Store>) make_ref<RemoteStore>() + : (ref<Store>) make_ref<LocalStore>(reserveSpace); } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 9cc5fd45b7c4..888ef3e2a083 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -103,7 +103,7 @@ struct ValidPathInfo typedef list<ValidPathInfo> ValidPathInfos; -enum BuildMode { bmNormal, bmRepair, bmCheck }; +enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash }; struct BuildResult @@ -132,13 +132,14 @@ struct BuildResult struct BasicDerivation; +struct Derivation; -class StoreAPI +class Store { public: - virtual ~StoreAPI() { } + virtual ~Store() { } /* Check whether a path is valid. */ virtual bool isValidPath(const Path & path) = 0; @@ -214,6 +215,10 @@ public: virtual void exportPath(const Path & path, bool sign, Sink & sink) = 0; + /* Export multiple paths in the format expected by ‘nix-store + --import’. */ + void exportPaths(const Paths & paths, bool sign, Sink & sink); + /* Import a sequence of NAR dumps created by exportPaths() into the Nix store. */ virtual Paths importPaths(bool requireSignature, Source & source) = 0; @@ -250,6 +255,10 @@ public: `path' has disappeared. */ virtual void addIndirectRoot(const Path & path) = 0; + /* Register a permanent GC root. */ + Path addPermRoot(const Path & storePath, + const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false); + /* Acquire the global GC lock, then immediately release it. This function must be called after registering a new permanent root, but before exiting. Otherwise, it is possible that a running @@ -298,6 +307,35 @@ public: /* Check the integrity of the Nix store. Returns true if errors remain. */ virtual bool verifyStore(bool checkContents, bool repair) = 0; + + /* Utility functions. */ + + /* Read a derivation, after ensuring its existence through + ensurePath(). */ + Derivation derivationFromPath(const Path & drvPath); + + /* Place in `paths' the set of all store paths in the file system + closure of `storePath'; that is, all paths than can be directly + or indirectly reached from it. `paths' is not cleared. If + `flipDirection' is true, the set of paths that can reach + `storePath' is returned; that is, the closures under the + `referrers' relation instead of the `references' relation is + returned. */ + void computeFSClosure(const Path & path, + PathSet & paths, bool flipDirection = false, + bool includeOutputs = false, bool includeDerivers = false); + + /* Given a set of paths that are to be built, return the set of + derivations that will be built, and the set of output paths + that will be substituted. */ + void queryMissing(const PathSet & targets, + PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, + unsigned long long & downloadSize, unsigned long long & narSize); + + /* Sort a set of paths topologically under the references + relation. If p refers to q, then p preceeds q in this list. */ + Paths topoSortPaths(const PathSet & paths); + }; @@ -372,24 +410,9 @@ Path computeStorePathForText(const string & name, const string & s, void removeTempRoots(); -/* Register a permanent GC root. */ -Path addPermRoot(StoreAPI & store, const Path & storePath, - const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false); - - -/* Sort a set of paths topologically under the references relation. - If p refers to q, then p preceeds q in this list. */ -Paths topoSortPaths(StoreAPI & store, const PathSet & paths); - - -/* For now, there is a single global store API object, but we'll - purify that in the future. */ -extern std::shared_ptr<StoreAPI> store; - - /* Factory method: open the Nix database, either through the local or remote implementation. */ -std::shared_ptr<StoreAPI> openStore(bool reserveSpace = true); +ref<Store> openStore(bool reserveSpace = true); /* Display a set of paths in human-readable form (i.e., between quotes @@ -401,12 +424,6 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven = false); -/* Export multiple paths in the format expected by ‘nix-store - --import’. */ -void exportPaths(StoreAPI & store, const Paths & paths, - bool sign, Sink & sink); - - MakeError(SubstError, Error) MakeError(BuildError, Error) /* denotes a permanent build failure */ diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index 446fcb781564..fb4160669a29 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -2,6 +2,7 @@ #include "types.hh" #include <lzma.h> +#include <cstdio> namespace nix { diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 2d97c5e6b6a7..64739300302b 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -96,19 +96,13 @@ Hash parseHash(HashType ht, const string & s) } -unsigned int hashLength32(const Hash & hash) -{ - return (hash.hashSize * 8 - 1) / 5 + 1; -} - - // omitted: E O U T const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; string printHash32(const Hash & hash) { - unsigned int len = hashLength32(hash); + size_t len = hash.base32Len(); string s; s.reserve(len); @@ -136,7 +130,7 @@ string printHash16or32(const Hash & hash) Hash parseHash32(HashType ht, const string & s) { Hash hash(ht); - unsigned int len = hashLength32(ht); + size_t len = hash.base32Len(); assert(s.size() == len); for (unsigned int n = 0; n < len; ++n) { @@ -163,7 +157,7 @@ Hash parseHash16or32(HashType ht, const string & s) if (s.size() == hash.hashSize * 2) /* hexadecimal representation */ hash = parseHash(ht, s); - else if (s.size() == hashLength32(hash)) + else if (s.size() == hash.base32Len()) /* base-32 representation */ hash = parseHash32(ht, s); else diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index 841b4cb2936c..bac2ebf2dcfa 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -7,7 +7,7 @@ namespace nix { -typedef enum { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 } HashType; +enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 }; const int md5HashSize = 16; @@ -40,6 +40,18 @@ struct Hash /* For sorting. */ bool operator < (const Hash & h) const; + + /* Returns the length of a base-16 representation of this hash. */ + size_t base16Len() const + { + return hashSize * 2; + } + + /* Returns the length of a base-32 representation of this hash. */ + size_t base32Len() const + { + return (hashSize * 8 - 1) / 5 + 1; + } }; @@ -49,9 +61,6 @@ string printHash(const Hash & hash); /* Parse a hexadecimal representation of a hash code. */ Hash parseHash(HashType ht, const string & s); -/* Returns the length of a base-32 hash representation. */ -unsigned int hashLength32(const Hash & hash); - /* Convert a hash to a base-32 representation. */ string printHash32(const Hash & hash); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 160884ee1ad7..0eae46c5fe93 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -5,6 +5,7 @@ #include <string> #include <list> #include <set> +#include <memory> #include <boost/format.hpp> @@ -96,4 +97,63 @@ typedef enum { } Verbosity; +/* A simple non-nullable reference-counted pointer. Actually a wrapper + around std::shared_ptr that prevents non-null constructions. */ +template<typename T> +class ref +{ +private: + + std::shared_ptr<T> p; + +public: + + ref<T>(const ref<T> & r) + : p(r.p) + { } + + explicit ref<T>(const std::shared_ptr<T> & p) + : p(p) + { + if (!p) + throw std::invalid_argument("null pointer cast to ref"); + } + + T* operator ->() const + { + return &*p; + } + + T& operator *() const + { + return *p; + } + + operator std::shared_ptr<T> () + { + return p; + } + + template<typename T2> + operator ref<T2> () + { + return ref<T2>((std::shared_ptr<T2>) p); + } + +private: + + template<typename T2, typename... Args> + friend ref<T2> + make_ref(Args&&... args); + +}; + +template<typename T, typename... Args> +inline ref<T> +make_ref(Args&&... args) +{ + auto p = std::make_shared<T>(std::forward<Args>(args)...); + return ref<T>(p); +} + } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 75032bf90d0b..def0525abc18 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -150,7 +150,7 @@ Path dirOf(const Path & path) string baseNameOf(const Path & path) { if (path.empty()) - return string(""); + return ""; Path::size_type last = path.length() - 1; if (path[last] == '/' && last > 0) @@ -161,6 +161,7 @@ string baseNameOf(const Path & path) pos = 0; else pos += 1; + return string(path, pos, last - pos + 1); } @@ -232,7 +233,13 @@ DirEntries readDirectory(const Path & path) checkInterrupt(); string name = dirent->d_name; if (name == "." || name == "..") continue; - entries.emplace_back(name, dirent->d_ino, dirent->d_type); + entries.emplace_back(name, dirent->d_ino, +#ifdef HAVE_STRUCT_DIRENT_D_TYPE + dirent->d_type +#else + DT_UNKNOWN +#endif + ); } if (errno) throw SysError(format("reading directory ‘%1%’") % path); diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 7ebfd7cee0e9..9eebb67fdf3a 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -8,9 +8,15 @@ #include <unistd.h> #include <signal.h> #include <functional> - +#include <limits> #include <cstdio> +#ifndef HAVE_STRUCT_DIRENT_D_TYPE +#define DT_UNKNOWN 0 +#define DT_REG 1 +#define DT_LNK 2 +#define DT_DIR 3 +#endif namespace nix { @@ -353,6 +359,8 @@ bool statusOk(int status); /* Parse a string into an integer. */ template<class N> bool string2Int(const string & s, N & n) { + if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed) + return false; std::istringstream str(s); str >> n; return str && str.get() == EOF; @@ -413,4 +421,14 @@ string base64Encode(const string & s); string base64Decode(const string & s); +/* Get a value for the specified key from an associate container, or a + default value if the key doesn't exist. */ +template <class T> +string get(const T & map, const string & key, const string & def = "") +{ + auto i = map.find(key); + return i == map.end() ? def : i->second; +} + + } diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 5a72cb712014..b9ccafb751c1 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -82,7 +82,7 @@ int main(int argc, char * * argv) // Run the actual garbage collector. if (!dryRun) { - store = openStore(false); + auto store = openStore(false); options.action = GCOptions::gcDeleteDead; GCResults results; PrintFreed freed(true, results); diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc index c5e11afa1553..aa20aa67daa8 100644 --- a/src/nix-daemon/nix-daemon.cc +++ b/src/nix-daemon/nix-daemon.cc @@ -149,7 +149,7 @@ struct SavingSourceAdapter : Source }; -static void performOp(bool trusted, unsigned int clientVersion, +static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVersion, Source & from, Sink & to, unsigned int op) { switch (op) { @@ -278,8 +278,7 @@ static void performOp(bool trusted, unsigned int clientVersion, startWork(); if (!savedRegular.regular) throw Error("regular file expected"); - Path path = dynamic_cast<LocalStore *>(store.get()) - ->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo); + Path path = store->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo); stopWork(); to << path; @@ -583,56 +582,56 @@ static void processConnection(bool trusted) #endif /* Open the store. */ - store = std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace)); + auto store = make_ref<LocalStore>(reserveSpace); stopWork(); to.flush(); - } catch (Error & e) { - stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); - to.flush(); - return; - } + /* Process client requests. */ + unsigned int opCount = 0; + + while (true) { + WorkerOp op; + try { + op = (WorkerOp) readInt(from); + } catch (Interrupted & e) { + break; + } catch (EndOfFile & e) { + break; + } - /* Process client requests. */ - unsigned int opCount = 0; + opCount++; + + try { + performOp(store, trusted, clientVersion, from, to, op); + } catch (Error & e) { + /* If we're not in a state where we can send replies, then + something went wrong processing the input of the + client. This can happen especially if I/O errors occur + during addTextToStore() / importPath(). If that + happens, just send the error message and exit. */ + bool errorAllowed = canSendStderr; + stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0); + if (!errorAllowed) throw; + } catch (std::bad_alloc & e) { + stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); + throw; + } - while (true) { - WorkerOp op; - try { - op = (WorkerOp) readInt(from); - } catch (Interrupted & e) { - break; - } catch (EndOfFile & e) { - break; - } + to.flush(); - opCount++; + assert(!canSendStderr); + }; - try { - performOp(trusted, clientVersion, from, to, op); - } catch (Error & e) { - /* If we're not in a state where we can send replies, then - something went wrong processing the input of the - client. This can happen especially if I/O errors occur - during addTextToStore() / importPath(). If that - happens, just send the error message and exit. */ - bool errorAllowed = canSendStderr; - stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0); - if (!errorAllowed) throw; - } catch (std::bad_alloc & e) { - stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); - throw; - } + canSendStderr = false; + _isInterrupted = false; + printMsg(lvlDebug, format("%1% operations") % opCount); + } catch (Error & e) { + stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); to.flush(); - - assert(!canSendStderr); - }; - - canSendStderr = false; - _isInterrupted = false; - printMsg(lvlDebug, format("%1% operations") % opCount); + return; + } } @@ -787,10 +786,6 @@ static void daemonLoop(char * * argv) while (1) { try { - /* Important: the server process *cannot* open the SQLite - database, because it doesn't like forks very much. */ - assert(!store); - /* Accept a connection. */ struct sockaddr_un remoteAddr; socklen_t remoteAddrLen = sizeof(remoteAddr); diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index dcfb8f35324a..a9d1ed024dd3 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1,17 +1,17 @@ -#include "profiles.hh" -#include "names.hh" -#include "globals.hh" -#include "misc.hh" -#include "shared.hh" -#include "eval.hh" -#include "get-drvs.hh" #include "attr-path.hh" #include "common-opts.hh" -#include "xml-writer.hh" +#include "derivations.hh" +#include "eval.hh" +#include "get-drvs.hh" +#include "globals.hh" +#include "names.hh" +#include "profiles.hh" +#include "shared.hh" #include "store-api.hh" #include "user-env.hh" #include "util.hh" #include "value-to-json.hh" +#include "xml-writer.hh" #include <cerrno> #include <ctime> @@ -223,8 +223,8 @@ static int comparePriorities(EvalState & state, DrvInfo & drv1, DrvInfo & drv2) static bool isPrebuilt(EvalState & state, DrvInfo & elem) { Path path = elem.queryOutPath(); - if (store->isValidPath(path)) return true; - PathSet ps = store->querySubstitutablePaths(singleton<PathSet>(path)); + if (state.store->isValidPath(path)) return true; + PathSet ps = state.store->querySubstitutablePaths(singleton<PathSet>(path)); return ps.find(path) != ps.end(); } @@ -398,7 +398,7 @@ static void queryInstSources(EvalState & state, if (isDerivation(path)) { elem.setDrvPath(path); - elem.setOutPath(findOutput(derivationFromPath(*store, path), "out")); + elem.setOutPath(state.store->derivationFromPath(path).findOutput("out")); if (name.size() >= drvExtension.size() && string(name, name.size() - drvExtension.size()) == drvExtension) name = string(name, 0, name.size() - drvExtension.size()); @@ -445,7 +445,7 @@ static void printMissing(EvalState & state, DrvInfos & elems) targets.insert(i.queryOutPath()); } - printMissing(*store, targets); + printMissing(state.store, targets); } @@ -711,18 +711,18 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs) if (drv.queryDrvPath() != "") { PathSet paths = singleton<PathSet>(drv.queryDrvPath()); - printMissing(*store, paths); + printMissing(globals.state->store, paths); if (globals.dryRun) return; - store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); + globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal); } else { - printMissing(*store, singleton<PathSet>(drv.queryOutPath())); + printMissing(globals.state->store, singleton<PathSet>(drv.queryOutPath())); if (globals.dryRun) return; - store->ensurePath(drv.queryOutPath()); + globals.state->store->ensurePath(drv.queryOutPath()); } debug(format("switching to new user environment")); - Path generation = createGeneration(globals.profile, drv.queryOutPath()); + Path generation = createGeneration(globals.state->store, globals.profile, drv.queryOutPath()); switchLink(globals.profile, generation); } @@ -973,8 +973,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) printMsg(lvlTalkative, format("skipping derivation named ‘%1%’ which gives an assertion failure") % i.name); i.setFailed(); } - validPaths = store->queryValidPaths(paths); - substitutablePaths = store->querySubstitutablePaths(paths); + validPaths = globals.state->store->queryValidPaths(paths); + substitutablePaths = globals.state->store->querySubstitutablePaths(paths); } @@ -1398,9 +1398,9 @@ int main(int argc, char * * argv) if (!op) throw UsageError("no operation specified"); - store = openStore(); + auto store = openStore(); - globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath)); + globals.state = std::shared_ptr<EvalState>(new EvalState(searchPath, store)); globals.state->repair = repair; if (file != "") diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 9a20b94334f2..4e0e28c1158c 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -38,7 +38,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, drvsToBuild.insert(i.queryDrvPath()); debug(format("building user environment dependencies")); - store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal); + state.store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal); /* Construct the whole top level derivation. */ PathSet references; @@ -76,8 +76,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* This is only necessary when installing store paths, e.g., `nix-env -i /nix/store/abcd...-foo'. */ - store->addTempRoot(j.second); - store->ensurePath(j.second); + state.store->addTempRoot(j.second); + state.store->ensurePath(j.second); references.insert(j.second); } @@ -100,7 +100,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* Also write a copy of the list of user environment elements to the store; we need it for future modifications of the environment. */ - Path manifestFile = store->addTextToStore("env-manifest.nix", + Path manifestFile = state.store->addTextToStore("env-manifest.nix", (format("%1%") % manifest).str(), references); /* Get the environment builder expression. */ @@ -128,7 +128,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* Realise the resulting store expression. */ debug("building user environment"); - store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair ? bmRepair : bmNormal); + state.store->buildPaths(singleton<PathSet>(topLevelDrv), state.repair ? bmRepair : bmNormal); /* Switch the current user environment to the output path. */ PathLocks lock; @@ -141,7 +141,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, } debug(format("switching to new user environment")); - Path generation = createGeneration(profile, topLevelOut); + Path generation = createGeneration(state.store, profile, topLevelOut); switchLink(profile, generation); return true; diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 13a145a3b53e..81c1c8d5637c 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -9,7 +9,6 @@ #include "util.hh" #include "store-api.hh" #include "common-opts.hh" -#include "misc.hh" #include <map> #include <iostream> @@ -80,7 +79,7 @@ void processExpr(EvalState & state, const Strings & attrPaths, else { Path rootName = gcRoot; if (++rootNr > 1) rootName += "-" + std::to_string(rootNr); - drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot); + drvPath = state.store->addPermRoot(drvPath, rootName, indirectRoot); } std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : ""); } @@ -158,9 +157,9 @@ int main(int argc, char * * argv) if (evalOnly && !wantsReadWrite) settings.readOnlyMode = true; - store = openStore(); + auto store = openStore(); - EvalState state(searchPath); + EvalState state(searchPath, store); state.repair = repair; Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); diff --git a/src/nix-prefetch-url/nix-prefetch-url.cc b/src/nix-prefetch-url/nix-prefetch-url.cc index 73a2845e07a5..c0c05a60bd78 100644 --- a/src/nix-prefetch-url/nix-prefetch-url.cc +++ b/src/nix-prefetch-url/nix-prefetch-url.cc @@ -91,8 +91,8 @@ int main(int argc, char * * argv) if (args.size() > 2) throw UsageError("too many arguments"); - store = openStore(); - EvalState state(searchPath); + auto store = openStore(); + EvalState state(searchPath, store); Bindings & autoArgs(*evalAutoArgs(state, autoArgs_)); diff --git a/src/nix-store/dotgraph.cc b/src/nix-store/dotgraph.cc index a333d7351010..8735cf9b667b 100644 --- a/src/nix-store/dotgraph.cc +++ b/src/nix-store/dotgraph.cc @@ -20,8 +20,8 @@ static string nextColour() { static int n = 0; static string colours[] = - { "black", "red", "green", "blue" - , "magenta", "burlywood" }; + { "black", "red", "green", "blue" + , "magenta", "burlywood" }; return colours[n++ % (sizeof(colours) / sizeof(string))]; } @@ -29,7 +29,7 @@ static string nextColour() static string makeEdge(const string & src, const string & dst) { format f = format("%1% -> %2% [color = %3%];\n") - % dotQuote(src) % dotQuote(dst) % dotQuote(nextColour()); + % dotQuote(src) % dotQuote(dst) % dotQuote(nextColour()); return f.str(); } @@ -38,8 +38,8 @@ static string makeNode(const string & id, const string & label, const string & colour) { format f = format("%1% [label = %2%, shape = box, " - "style = filled, fillcolor = %3%];\n") - % dotQuote(id) % dotQuote(label) % dotQuote(colour); + "style = filled, fillcolor = %3%];\n") + % dotQuote(id) % dotQuote(label) % dotQuote(colour); return f.str(); } @@ -65,51 +65,51 @@ void printClosure(const Path & nePath, const StoreExpr & fs) PathSet doneSet; for (PathSet::iterator i = workList.begin(); i != workList.end(); ++i) { - cout << makeEdge(pathLabel(nePath, *i), nePath); + cout << makeEdge(pathLabel(nePath, *i), nePath); } while (!workList.empty()) { - Path path = *(workList.begin()); - workList.erase(path); - - if (doneSet.find(path) == doneSet.end()) { - doneSet.insert(path); - - ClosureElems::const_iterator elem = fs.closure.elems.find(path); - if (elem == fs.closure.elems.end()) - throw Error(format("bad closure, missing path ‘%1%’") % path); - - for (StringSet::const_iterator i = elem->second.refs.begin(); - i != elem->second.refs.end(); ++i) - { - workList.insert(*i); - cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path)); - } - - cout << makeNode(pathLabel(nePath, path), - symbolicName(path), "#ff0000"); - } + Path path = *(workList.begin()); + workList.erase(path); + + if (doneSet.find(path) == doneSet.end()) { + doneSet.insert(path); + + ClosureElems::const_iterator elem = fs.closure.elems.find(path); + if (elem == fs.closure.elems.end()) + throw Error(format("bad closure, missing path ‘%1%’") % path); + + for (StringSet::const_iterator i = elem->second.refs.begin(); + i != elem->second.refs.end(); ++i) + { + workList.insert(*i); + cout << makeEdge(pathLabel(nePath, *i), pathLabel(nePath, path)); + } + + cout << makeNode(pathLabel(nePath, path), + symbolicName(path), "#ff0000"); + } } } #endif -void printDotGraph(const PathSet & roots) +void printDotGraph(ref<Store> store, const PathSet & roots) { PathSet workList(roots); PathSet doneSet; - + cout << "digraph G {\n"; while (!workList.empty()) { - Path path = *(workList.begin()); - workList.erase(path); + Path path = *(workList.begin()); + workList.erase(path); - if (doneSet.find(path) != doneSet.end()) continue; + if (doneSet.find(path) != doneSet.end()) continue; doneSet.insert(path); cout << makeNode(path, symbolicName(path), "#ff0000"); - + PathSet references; store->queryReferences(path, references); @@ -121,42 +121,42 @@ void printDotGraph(const PathSet & roots) cout << makeEdge(*i, path); } } - - -#if 0 - StoreExpr ne = storeExprFromPath(path); - - string label, colour; - - if (ne.type == StoreExpr::neDerivation) { - for (PathSet::iterator i = ne.derivation.inputs.begin(); - i != ne.derivation.inputs.end(); ++i) - { - workList.insert(*i); - cout << makeEdge(*i, path); - } - - label = "derivation"; - colour = "#00ff00"; - for (StringPairs::iterator i = ne.derivation.env.begin(); - i != ne.derivation.env.end(); ++i) - if (i->first == "name") label = i->second; - } - - else if (ne.type == StoreExpr::neClosure) { - label = "<closure>"; - colour = "#00ffff"; - printClosure(path, ne); - } - - else abort(); - - cout << makeNode(path, label, colour); + + +#if 0 + StoreExpr ne = storeExprFromPath(path); + + string label, colour; + + if (ne.type == StoreExpr::neDerivation) { + for (PathSet::iterator i = ne.derivation.inputs.begin(); + i != ne.derivation.inputs.end(); ++i) + { + workList.insert(*i); + cout << makeEdge(*i, path); + } + + label = "derivation"; + colour = "#00ff00"; + for (StringPairs::iterator i = ne.derivation.env.begin(); + i != ne.derivation.env.end(); ++i) + if (i->first == "name") label = i->second; + } + + else if (ne.type == StoreExpr::neClosure) { + label = "<closure>"; + colour = "#00ffff"; + printClosure(path, ne); + } + + else abort(); + + cout << makeNode(path, label, colour); #endif } cout << "}\n"; } - + } diff --git a/src/nix-store/dotgraph.hh b/src/nix-store/dotgraph.hh index 68410d84156d..e2b5fc72fbe1 100644 --- a/src/nix-store/dotgraph.hh +++ b/src/nix-store/dotgraph.hh @@ -4,6 +4,8 @@ namespace nix { -void printDotGraph(const PathSet & roots); +class Store; + +void printDotGraph(ref<Store> store, const PathSet & roots); } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index b49ebd09475c..2dfd9e7c7411 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -1,14 +1,14 @@ -#include "globals.hh" -#include "misc.hh" #include "archive.hh" -#include "shared.hh" +#include "derivations.hh" #include "dotgraph.hh" -#include "xmlgraph.hh" +#include "globals.hh" #include "local-store.hh" -#include "util.hh" +#include "monitor-fd.hh" #include "serve-protocol.hh" +#include "shared.hh" +#include "util.hh" #include "worker-protocol.hh" -#include "monitor-fd.hh" +#include "xmlgraph.hh" #include <iostream> #include <algorithm> @@ -37,13 +37,14 @@ static Path gcRoot; static int rootNr = 0; static bool indirectRoot = false; static bool noOutput = false; +static std::shared_ptr<Store> store; -LocalStore & ensureLocalStore() +ref<LocalStore> ensureLocalStore() { - LocalStore * store2(dynamic_cast<LocalStore *>(store.get())); + auto store2 = std::dynamic_pointer_cast<LocalStore>(store); if (!store2) throw Error("you don't have sufficient rights to use this command"); - return *store2; + return ref<LocalStore>(store2); } @@ -65,7 +66,7 @@ static PathSet realisePath(Path path, bool build = true) if (isDerivation(p.first)) { if (build) store->buildPaths(singleton<PathSet>(path)); - Derivation drv = derivationFromPath(*store, p.first); + Derivation drv = store->derivationFromPath(p.first); rootNr++; if (p.second.empty()) @@ -83,7 +84,7 @@ static PathSet realisePath(Path path, bool build = true) Path rootName = gcRoot; if (rootNr > 1) rootName += "-" + std::to_string(rootNr); if (i->first != "out") rootName += "-" + i->first; - outPath = addPermRoot(*store, outPath, rootName, indirectRoot); + outPath = store->addPermRoot(outPath, rootName, indirectRoot); } outputs.insert(outPath); } @@ -99,7 +100,7 @@ static PathSet realisePath(Path path, bool build = true) Path rootName = gcRoot; rootNr++; if (rootNr > 1) rootName += "-" + std::to_string(rootNr); - path = addPermRoot(*store, path, rootName, indirectRoot); + path = store->addPermRoot(path, rootName, indirectRoot); } return singleton<PathSet>(path); } @@ -117,6 +118,7 @@ static void opRealise(Strings opFlags, Strings opArgs) if (i == "--dry-run") dryRun = true; else if (i == "--repair") buildMode = bmRepair; else if (i == "--check") buildMode = bmCheck; + else if (i == "--hash") buildMode = bmHash; else if (i == "--ignore-unknown") ignoreUnknown = true; else throw UsageError(format("unknown flag ‘%1%’") % i); @@ -128,7 +130,7 @@ static void opRealise(Strings opFlags, Strings opArgs) unsigned long long downloadSize, narSize; PathSet willBuild, willSubstitute, unknown; - queryMissing(*store, PathSet(paths.begin(), paths.end()), + store->queryMissing(PathSet(paths.begin(), paths.end()), willBuild, willSubstitute, unknown, downloadSize, narSize); if (ignoreUnknown) { @@ -140,7 +142,7 @@ static void opRealise(Strings opFlags, Strings opArgs) } if (settings.get("print-missing", true)) - printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize); + printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize); if (dryRun) return; @@ -215,7 +217,7 @@ static PathSet maybeUseOutputs(const Path & storePath, bool useOutput, bool forc { if (forceRealise) realisePath(storePath); if (useOutput && isDerivation(storePath)) { - Derivation drv = derivationFromPath(*store, storePath); + Derivation drv = store->derivationFromPath(storePath); PathSet outputs; for (auto & i : drv.outputs) outputs.insert(i.second.path); @@ -252,7 +254,7 @@ static void printTree(const Path & path, closure(B). That is, if derivation A is an (possibly indirect) input of B, then A is printed first. This has the effect of flattening the tree, preventing deeply nested structures. */ - Paths sorted = topoSortPaths(*store, references); + Paths sorted = store->topoSortPaths(references); reverse(sorted.begin(), sorted.end()); for (auto i = sorted.begin(); i != sorted.end(); ++i) { @@ -317,7 +319,7 @@ static void opQuery(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { i = followLinksToStorePath(i); if (forceRealise) realisePath(i); - Derivation drv = derivationFromPath(*store, i); + Derivation drv = store->derivationFromPath(i); for (auto & j : drv.outputs) cout << format("%1%\n") % j.second.path; } @@ -332,13 +334,13 @@ static void opQuery(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { PathSet ps = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); for (auto & j : ps) { - if (query == qRequisites) computeFSClosure(*store, j, paths, false, includeOutputs); + if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs); else if (query == qReferences) store->queryReferences(j, paths); else if (query == qReferrers) store->queryReferrers(j, paths); - else if (query == qReferrersClosure) computeFSClosure(*store, j, paths, true); + else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true); } } - Paths sorted = topoSortPaths(*store, paths); + Paths sorted = store->topoSortPaths(paths); for (Paths::reverse_iterator i = sorted.rbegin(); i != sorted.rend(); ++i) cout << format("%s\n") % *i; @@ -356,7 +358,7 @@ static void opQuery(Strings opFlags, Strings opArgs) case qBinding: for (auto & i : opArgs) { Path path = useDeriver(followLinksToStorePath(i)); - Derivation drv = derivationFromPath(*store, path); + Derivation drv = store->derivationFromPath(path); StringPairs::iterator j = drv.env.find(bindingName); if (j == drv.env.end()) throw Error(format("derivation ‘%1%’ has no environment binding named ‘%2%’") @@ -393,7 +395,7 @@ static void opQuery(Strings opFlags, Strings opArgs) PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); roots.insert(paths.begin(), paths.end()); } - printDotGraph(roots); + printDotGraph(ref<Store>(store), roots); break; } @@ -403,7 +405,7 @@ static void opQuery(Strings opFlags, Strings opArgs) PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); roots.insert(paths.begin(), paths.end()); } - printXmlGraph(roots); + printXmlGraph(ref<Store>(store), roots); break; } @@ -418,7 +420,7 @@ static void opQuery(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { PathSet paths = maybeUseOutputs(followLinksToStorePath(i), useOutput, forceRealise); for (auto & j : paths) - computeFSClosure(*store, j, referrers, true, + store->computeFSClosure(j, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations); } Roots roots = store->findRoots(); @@ -449,7 +451,7 @@ static void opPrintEnv(Strings opFlags, Strings opArgs) if (opArgs.size() != 1) throw UsageError("‘--print-env’ requires one derivation store path"); Path drvPath = opArgs.front(); - Derivation drv = derivationFromPath(*store, drvPath); + Derivation drv = store->derivationFromPath(drvPath); /* Print each environment variable in the derivation in a format that can be sourced by the shell. */ @@ -572,7 +574,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) } } - ensureLocalStore().registerValidPaths(infos); + ensureLocalStore()->registerValidPaths(infos); } @@ -714,9 +716,9 @@ static void opExport(Strings opFlags, Strings opArgs) else throw UsageError(format("unknown flag ‘%1%’") % i); FdSink sink(STDOUT_FILENO); - Paths sorted = topoSortPaths(*store, PathSet(opArgs.begin(), opArgs.end())); + Paths sorted = store->topoSortPaths(PathSet(opArgs.begin(), opArgs.end())); reverse(sorted.begin(), sorted.end()); - exportPaths(*store, sorted, sign, sink); + store->exportPaths(sorted, sign, sink); } @@ -803,7 +805,7 @@ static void opRepairPath(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { Path path = followLinksToStorePath(i); - ensureLocalStore().repairPath(path); + ensureLocalStore()->repairPath(path); } } @@ -895,7 +897,7 @@ static void opServe(Strings opFlags, Strings opArgs) if (!isDerivation(path)) paths2.insert(path); unsigned long long downloadSize, narSize; PathSet willBuild, willSubstitute, unknown; - queryMissing(*store, PathSet(paths2.begin(), paths2.end()), + store->queryMissing(PathSet(paths2.begin(), paths2.end()), willBuild, willSubstitute, unknown, downloadSize, narSize); /* FIXME: should use ensurePath(), but it only does one path at a time. */ @@ -940,9 +942,9 @@ static void opServe(Strings opFlags, Strings opArgs) case cmdExportPaths: { bool sign = readInt(in); - Paths sorted = topoSortPaths(*store, readStorePaths<PathSet>(in)); + Paths sorted = store->topoSortPaths(readStorePaths<PathSet>(in)); reverse(sorted.begin(), sorted.end()); - exportPaths(*store, sorted, sign, out); + store->exportPaths(sorted, sign, out); break; } @@ -987,7 +989,7 @@ static void opServe(Strings opFlags, Strings opArgs) PathSet paths = readStorePaths<PathSet>(in); PathSet closure; for (auto & i : paths) - computeFSClosure(*store, i, closure, false, includeOutputs); + store->computeFSClosure(i, closure, false, includeOutputs); out << closure; break; } diff --git a/src/nix-store/xmlgraph.cc b/src/nix-store/xmlgraph.cc index 1b3ad3d28ad4..b6e1c1c4b873 100644 --- a/src/nix-store/xmlgraph.cc +++ b/src/nix-store/xmlgraph.cc @@ -33,34 +33,34 @@ static string makeNode(const string & id) } -void printXmlGraph(const PathSet & roots) +void printXmlGraph(ref<Store> store, const PathSet & roots) { PathSet workList(roots); PathSet doneSet; cout << "<?xml version='1.0' encoding='utf-8'?>\n" - << "<nix>\n"; + << "<nix>\n"; while (!workList.empty()) { - Path path = *(workList.begin()); - workList.erase(path); + Path path = *(workList.begin()); + workList.erase(path); - if (doneSet.find(path) != doneSet.end()) continue; - doneSet.insert(path); + if (doneSet.find(path) != doneSet.end()) continue; + doneSet.insert(path); - cout << makeNode(path); + cout << makeNode(path); - PathSet references; - store->queryReferences(path, references); + PathSet references; + store->queryReferences(path, references); - for (PathSet::iterator i = references.begin(); - i != references.end(); ++i) - { - if (*i != path) { - workList.insert(*i); - cout << makeEdge(*i, path); - } - } + for (PathSet::iterator i = references.begin(); + i != references.end(); ++i) + { + if (*i != path) { + workList.insert(*i); + cout << makeEdge(*i, path); + } + } } diff --git a/src/nix-store/xmlgraph.hh b/src/nix-store/xmlgraph.hh index c2216c5a4627..a6e7d4e2805a 100644 --- a/src/nix-store/xmlgraph.hh +++ b/src/nix-store/xmlgraph.hh @@ -4,6 +4,8 @@ namespace nix { -void printXmlGraph(const PathSet & roots); +class Store; + +void printXmlGraph(ref<Store> store, const PathSet & roots); } diff --git a/tests/dump-db.sh b/tests/dump-db.sh index 234b7ac02680..57c8c401600d 100644 --- a/tests/dump-db.sh +++ b/tests/dump-db.sh @@ -9,6 +9,7 @@ deps="$(nix-store -qR $TEST_ROOT/result)" nix-store --dump-db > $TEST_ROOT/dump rm -rf $NIX_DB_DIR +mkdir $NIX_DB_DIR nix-store --load-db < $TEST_ROOT/dump diff --git a/tests/lang/parse-okay-regression-751.nix b/tests/lang/parse-okay-regression-751.nix new file mode 100644 index 000000000000..05c78b3016d3 --- /dev/null +++ b/tests/lang/parse-okay-regression-751.nix @@ -0,0 +1,2 @@ +let const = a: "const"; in +''${ const { x = "q"; }}'' diff --git a/tests/repair.sh b/tests/repair.sh index ae82b649c6ac..92f2f8fe60a5 100644 --- a/tests/repair.sh +++ b/tests/repair.sh @@ -18,6 +18,18 @@ if nix-store --verify --check-contents -v; then exit 1 fi +# The path can be repaired by rebuilding the derivation. +nix-store --verify --check-contents --repair + +nix-store --verify-path $path2 + +# Re-corrupt and delete the deriver. Now --verify --repair should +# not work. +chmod u+w $path2 +touch $path2/bad + +nix-store --delete $(nix-store -qd $path2) + if nix-store --verify --check-contents --repair; then echo "nix-store --verify --repair succeeded unexpectedly" >&2 exit 1 diff --git a/version b/version index b8162070734f..35d51f33b34f 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.11 \ No newline at end of file +1.12 \ No newline at end of file |