Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d6418d46e | ||
|
|
dec2f19502 | ||
|
|
6123144933 | ||
|
|
1dacd427cd | ||
|
|
ea6bf0c21f | ||
|
|
36e67ff16b | ||
|
|
fba17a9043 | ||
|
|
2d9bb56e55 | ||
|
|
5bc41d78ff | ||
|
|
9e4bb20455 | ||
|
|
dc341811d6 | ||
|
|
69befd33a9 | ||
|
|
a5684e09d3 | ||
|
|
411a3461dc | ||
|
|
05d02f798f | ||
|
|
6da92d96ae | ||
|
|
543d8a5942 | ||
|
|
fe95650487 | ||
|
|
3139481822 | ||
|
|
c086183843 | ||
|
|
4f4a14453a | ||
|
|
4ea034a5c5 | ||
|
|
792fd51f41 | ||
|
|
f440558acc | ||
|
|
bb659bad81 | ||
|
|
f6a8e7f4c2 | ||
|
|
b08f4b0da9 | ||
|
|
d7625b5c2d | ||
|
|
b8034e5581 | ||
|
|
9d8a80375d | ||
|
|
d6a7aa8f48 | ||
|
|
b8571d68c4 | ||
|
|
a737f51fd9 | ||
|
|
ff02f5336c | ||
|
|
4bd5282573 | ||
|
|
bce14d0f61 | ||
|
|
7cdefdbe73 | ||
|
|
d05bf04444 | ||
|
|
c1994fecf9 | ||
|
|
672c3acc71 | ||
|
|
7bdb85453d | ||
|
|
9deb822180 | ||
|
|
b1e3b1a4ac | ||
|
|
6b47de580f | ||
|
|
a5e0f64db3 | ||
|
|
221a2daf34 | ||
|
|
176c666f36 | ||
|
|
c945f015de | ||
|
|
28e0742966 | ||
|
|
a5fb4b5b7c | ||
|
|
faaae44f2e | ||
|
|
f53574ebd6 | ||
|
|
d5529f5b85 | ||
|
|
b072fc04a7 |
+55
-42
@@ -12,9 +12,9 @@ such as <function>derivation</function>, are always in scope of every
|
||||
Nix expression; you can just access them right away. But to prevent
|
||||
polluting the namespace too much, most built-ins are not in scope.
|
||||
Instead, you can access them through the <varname>builtins</varname>
|
||||
built-in value, which is an attribute set that contains all built-in
|
||||
functions and values. For instance, <function>derivation</function>
|
||||
is also available as <function>builtins.derivation</function>.</para>
|
||||
built-in value, which is a set that contains all built-in functions
|
||||
and values. For instance, <function>derivation</function> is also
|
||||
available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
|
||||
<variablelist>
|
||||
@@ -39,17 +39,17 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.attrNames</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
<replaceable>set</replaceable></term>
|
||||
|
||||
<listitem><para>Return the names of the attributes in the
|
||||
attribute set <replaceable>attrs</replaceable> in a sorted list.
|
||||
For instance, <literal>builtins.attrNames { y = 1; x = "foo";
|
||||
}</literal> evaluates to <literal>[ "x" "y" ]</literal>. There is
|
||||
no built-in function <function>attrValues</function>, but you can
|
||||
easily define it yourself:
|
||||
<listitem><para>Return the names of the attributes in the set
|
||||
<replaceable>set</replaceable> in a sorted list. For instance,
|
||||
<literal>builtins.attrNames { y = 1; x = "foo"; }</literal>
|
||||
evaluates to <literal>[ "x" "y" ]</literal>. There is no built-in
|
||||
function <function>attrValues</function>, but you can easily
|
||||
define it yourself:
|
||||
|
||||
<programlisting>
|
||||
attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs);</programlisting>
|
||||
attrValues = set: map (name: builtins.getAttr name set) (builtins.attrNames set);</programlisting>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
@@ -68,8 +68,8 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
|
||||
<varlistentry><term><varname>builtins</varname></term>
|
||||
|
||||
<listitem><para>The attribute set <varname>builtins</varname>
|
||||
contains all the built-in functions and values. You can use
|
||||
<listitem><para>The set <varname>builtins</varname> contains all
|
||||
the built-in functions and values. You can use
|
||||
<varname>builtins</varname> to test for the availability of
|
||||
features in the Nix installation, e.g.,
|
||||
|
||||
@@ -258,11 +258,11 @@ stdenv.mkDerivation {
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
<replaceable>s</replaceable> <replaceable>set</replaceable></term>
|
||||
|
||||
<listitem><para><function>getAttr</function> returns the attribute
|
||||
named <replaceable>s</replaceable> from the attribute set
|
||||
<replaceable>attrs</replaceable>. Evaluation aborts if the
|
||||
named <replaceable>s</replaceable> from
|
||||
<replaceable>set</replaceable>. Evaluation aborts if the
|
||||
attribute doesn’t exist. This is a dynamic version of the
|
||||
<literal>.</literal> operator, since <replaceable>s</replaceable>
|
||||
is an expression rather than an identifier.</para></listitem>
|
||||
@@ -289,15 +289,15 @@ stdenv.mkDerivation {
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hasAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
<replaceable>s</replaceable> <replaceable>set</replaceable></term>
|
||||
|
||||
<listitem><para><function>hasAttr</function> returns
|
||||
<literal>true</literal> if the attribute set
|
||||
<replaceable>attrs</replaceable> has an attribute named
|
||||
<replaceable>s</replaceable>, and <literal>false</literal>
|
||||
otherwise. This is a dynamic version of the <literal>?</literal>
|
||||
operator, since <replaceable>s</replaceable> is an expression
|
||||
rather than an identifier.</para></listitem>
|
||||
<literal>true</literal> if <replaceable>set</replaceable> has an
|
||||
attribute named <replaceable>s</replaceable>, and
|
||||
<literal>false</literal> otherwise. This is a dynamic version of
|
||||
the <literal>?</literal> operator, since
|
||||
<replaceable>s</replaceable> is an expression rather than an
|
||||
identifier.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -331,12 +331,12 @@ stdenv.mkDerivation {
|
||||
<listitem><para>Load, parse and return the Nix expression in the
|
||||
file <replaceable>path</replaceable>. If <replaceable>path
|
||||
</replaceable> is a directory, the file <filename>default.nix
|
||||
</filename> in that directory is loaded. Evaluation aborts if
|
||||
the file doesn’t exist or contains an incorrect Nix
|
||||
expression. <function>import</function> implements Nix’s module
|
||||
system: you can put any Nix expression (such as an attribute set
|
||||
or a function) in a separate file, and use it from Nix expressions
|
||||
in other files.</para>
|
||||
</filename> in that directory is loaded. Evaluation aborts if the
|
||||
file doesn’t exist or contains an incorrect Nix expression.
|
||||
<function>import</function> implements Nix’s module system: you
|
||||
can put any Nix expression (such as a set or a function) in a
|
||||
separate file, and use it from Nix expressions in other
|
||||
files.</para>
|
||||
|
||||
<para>A Nix expression loaded by <function>import</function> must
|
||||
not contain any <emphasis>free variables</emphasis> (identifiers
|
||||
@@ -383,9 +383,9 @@ x: x + 456</programlisting>
|
||||
<varlistentry><term><function>builtins.intersectAttrs</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return an attribute set consisting of the
|
||||
attributes in the set <replaceable>e2</replaceable> that also
|
||||
exist in the set <replaceable>e1</replaceable>.</para></listitem>
|
||||
<listitem><para>Return a set consisting of the attributes in the
|
||||
set <replaceable>e2</replaceable> that also exist in the set
|
||||
<replaceable>e1</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -394,7 +394,7 @@ x: x + 456</programlisting>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to an attribute set, and
|
||||
<replaceable>e</replaceable> evaluates to a set, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -490,9 +490,9 @@ x: x + 456</programlisting>
|
||||
<varlistentry><term><function>builtins.listToAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Construct an attribute set from a list specifying
|
||||
the names and values of each attribute. Each element of the list
|
||||
should be an attribute set consisting of a string-valued attribute
|
||||
<listitem><para>Construct a set from a list specifying the names
|
||||
and values of each attribute. Each element of the list should be
|
||||
a set consisting of a string-valued attribute
|
||||
<varname>name</varname> specifying the name of the attribute, and
|
||||
an attribute <varname>value</varname> specifying its value.
|
||||
Example:
|
||||
@@ -547,7 +547,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
a package name and version. The package name is everything up to
|
||||
but not including the first dash followed by a digit, and the
|
||||
version is everything following that dash. The result is returned
|
||||
in an attribute set <literal>{ name, version }</literal>. Thus,
|
||||
in a set <literal>{ name, version }</literal>. Thus,
|
||||
<literal>builtins.parseDrvName "nix-0.12pre12876"</literal>
|
||||
returns <literal>{ name = "nix"; version = "0.12pre12876";
|
||||
}</literal>.</para></listitem>
|
||||
@@ -598,12 +598,12 @@ in config.someSetting</programlisting>
|
||||
|
||||
|
||||
<varlistentry><term><function>removeAttrs</function>
|
||||
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
||||
<replaceable>set</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Remove the attributes listed in
|
||||
<replaceable>list</replaceable> from the attribute set
|
||||
<replaceable>attrs</replaceable>. The attributes don’t have to
|
||||
exist in <replaceable>attrs</replaceable>. For instance,
|
||||
<replaceable>list</replaceable> from
|
||||
<replaceable>set</replaceable>. The attributes don’t have to
|
||||
exist in <replaceable>set</replaceable>. For instance,
|
||||
|
||||
<screen>
|
||||
removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
@@ -792,7 +792,7 @@ in foo</programlisting>
|
||||
servlet container</link>. A servlet container contains a number
|
||||
of servlets (<filename>*.war</filename> files) each exported under
|
||||
a specific URI prefix. So the servlet configuration is a list of
|
||||
attribute sets containing the <varname>path</varname> and
|
||||
sets containing the <varname>path</varname> and
|
||||
<varname>war</varname> of the servlet (<xref
|
||||
linkend='ex-toxml-co-servlets' />). This kind of information is
|
||||
difficult to communicate with the normal method of passing
|
||||
@@ -901,6 +901,19 @@ stdenv.mkDerivation (rec {
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.typeOf</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return a string representing the type of the value
|
||||
<replaceable>e</replaceable>, namely <literal>"int"</literal>,
|
||||
<literal>"bool"</literal>, <literal>"string"</literal>,
|
||||
<literal>"path"</literal>, <literal>"null"</literal>,
|
||||
<literal>"set"</literal>, <literal>"list"</literal> or
|
||||
<literal>"lambda"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -312,6 +312,24 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>NIX_SHOW_STATS</envar></term>
|
||||
|
||||
<listitem><para>If set to <literal>1</literal>, Nix will print some
|
||||
evaluation statistics, such as the number of values
|
||||
allocated.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>NIX_COUNT_CALLS</envar></term>
|
||||
|
||||
<listitem><para>If set to <literal>1</literal>, Nix will print how
|
||||
often functions were called during Nix expression evaluation. This
|
||||
is useful for profiling your Nix expressions.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>GC_INITIAL_HEAP_SIZE</envar></term>
|
||||
|
||||
<listitem><para>If Nix has been configured to use the Boehm garbage
|
||||
|
||||
@@ -48,7 +48,7 @@ Debian, Ubuntu, Mac OS X and various other systems from the <link
|
||||
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
|
||||
You can also get builds of the latest development release from our
|
||||
<link
|
||||
xlink:href="http://hydra.nixos.org/view/nix/trunk/latest">continuous
|
||||
xlink:href="http://hydra.nixos.org/job/nix/trunk/release/latest-finished#tabs-constituents">continuous
|
||||
build system</link>.</para>
|
||||
|
||||
<para>For Fedora, RPM packages are available. These can be installed
|
||||
@@ -188,7 +188,7 @@ a source distribution.</para>
|
||||
downloaded from the <link
|
||||
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
|
||||
You can also grab the <link
|
||||
xlink:href="http://hydra.nixos.org/view/nix/trunk/latest/tarball/download-by-type/file/source-dist">most
|
||||
xlink:href="http://hydra.nixos.org/job/nix/trunk/release/latest-finished#tabs-constituents">most
|
||||
recent development release</link>.</para>
|
||||
|
||||
<para>Alternatively, the most recent sources of Nix can be obtained
|
||||
|
||||
@@ -153,11 +153,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
default.</para>
|
||||
|
||||
<para>The Nix expressions in this directory are combined into a
|
||||
single attribute set, with each file as an attribute that has the
|
||||
name of the file. Thus, if <filename>~/.nix-defexpr</filename>
|
||||
contains two files, <filename>foo</filename> and
|
||||
<filename>bar</filename>, then the default Nix expression will
|
||||
essentially be
|
||||
single set, with each file as an attribute that has the name of
|
||||
the file. Thus, if <filename>~/.nix-defexpr</filename> contains
|
||||
two files, <filename>foo</filename> and <filename>bar</filename>,
|
||||
then the default Nix expression will essentially be
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
@@ -405,7 +404,7 @@ $ nix-env -f ./foo.nix -i -E \
|
||||
I.e., this evaluates to <literal>(f: (f {system =
|
||||
"i686-linux";}).subversionWithJava) (import ./foo.nix)</literal>, thus
|
||||
selecting the <literal>subversionWithJava</literal> attribute from the
|
||||
attribute set returned by calling the function defined in
|
||||
set returned by calling the function defined in
|
||||
<filename>./foo.nix</filename>.</para>
|
||||
|
||||
<para>A dry-run tells you which paths will be downloaded or built from
|
||||
|
||||
@@ -5,10 +5,76 @@
|
||||
<title>Nix Release Notes</title>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.6.1"><title>Release 1.6.1 (October 28, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. Changes of interest
|
||||
are:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Nix 1.6 accidentally changed the semantics of antiquoted
|
||||
paths in strings, such as <literal>"${/foo}/bar"</literal>. This
|
||||
release reverts to the Nix 1.5.3 behaviour.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Previously, Nix optimised expressions such as
|
||||
<literal>"${<replaceable>expr</replaceable>}"</literal> to
|
||||
<replaceable>expr</replaceable>. Thus it neither checked whether
|
||||
<replaceable>expr</replaceable> could be coerced to a string, nor
|
||||
applied such coercions. This meant that
|
||||
<literal>"${123}"</literal> evaluatued to <literal>123</literal>,
|
||||
and <literal>"${./foo}"</literal> evaluated to
|
||||
<literal>./foo</literal> (even though
|
||||
<literal>"${./foo} "</literal> evaluates to
|
||||
<literal>"/nix/store/<replaceable>hash</replaceable>-foo "</literal>).
|
||||
Nix now checks the type of antiquoted expressions and
|
||||
applies coercions.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now shows the exact position of undefined variables. In
|
||||
particular, undefined variable errors in a <literal>with</literal>
|
||||
previously didn't show <emphasis>any</emphasis> position
|
||||
information, so this makes it a lot easier to fix such
|
||||
errors.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Undefined variables are now treated consistently.
|
||||
Previously, the <function>tryEval</function> function would catch
|
||||
undefined variables inside a <literal>with</literal> but not
|
||||
outside. Now <function>tryEval</function> never catches undefined
|
||||
variables.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Bash completion in <command>nix-shell</command> now works
|
||||
correctly.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Stack traces are less verbose: they no longer show calls to
|
||||
builtin functions and only show a single line for each derivation
|
||||
on the call stack.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New built-in function: <function>builtins.typeOf</function>,
|
||||
which returns the type of its argument as a string.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.6.0"><title>Release 1.6.0 (September 10, 2013)</title>
|
||||
<section xml:id="ssec-relnotes-1.6.0"><title>Release 1.6 (September 10, 2013)</title>
|
||||
|
||||
<para>In addition to the usual bug fixes, this release has several new
|
||||
features:</para>
|
||||
|
||||
@@ -15,7 +15,7 @@ on to a more in-depth look at the Nix expression language.</para>
|
||||
For more extensive information on adding packages to the Nix Packages
|
||||
collection (such as functions in the standard environment and coding
|
||||
conventions), please consult <link
|
||||
xlink:href="http://hydra.nixos.org/job/nixpkgs/trunk/tarball/latest/download-by-type/doc/manual">its
|
||||
xlink:href="http://nixos.org/nixpkgs/manual/">its
|
||||
manual</link>.</para></note>
|
||||
|
||||
|
||||
@@ -118,10 +118,10 @@ the single Nix expression in that directory
|
||||
<varname>stdenv.mkDerivation</varname>.
|
||||
<varname>mkDerivation</varname> is a function provided by
|
||||
<varname>stdenv</varname> that builds a package from a set of
|
||||
<emphasis>attributes</emphasis>. An attribute set is just a list
|
||||
of key/value pairs where each value is an arbitrary Nix
|
||||
expression. They take the general form
|
||||
<literal>{ <replaceable>name1</replaceable> =
|
||||
<emphasis>attributes</emphasis>. A set is just a list of
|
||||
key/value pairs where each key is a string and each value is an
|
||||
arbitrary Nix expression. They take the general form <literal>{
|
||||
<replaceable>name1</replaceable> =
|
||||
<replaceable>expr1</replaceable>; <replaceable>...</replaceable>
|
||||
<replaceable>nameN</replaceable> =
|
||||
<replaceable>exprN</replaceable>; }</literal>.</para>
|
||||
@@ -384,9 +384,9 @@ some fragments of
|
||||
|
||||
<para>This is where the actual composition takes place. Here we
|
||||
<emphasis>call</emphasis> the function imported from
|
||||
<filename>../applications/misc/hello/ex-1</filename> with an
|
||||
attribute set containing the things that the function expects,
|
||||
namely <varname>fetchurl</varname>, <varname>stdenv</varname>, and
|
||||
<filename>../applications/misc/hello/ex-1</filename> with a set
|
||||
containing the things that the function expects, namely
|
||||
<varname>fetchurl</varname>, <varname>stdenv</varname>, and
|
||||
<varname>perl</varname>. We use inherit again to use the
|
||||
attributes defined in the surrounding scope (we could also have
|
||||
written <literal>fetchurl = fetchurl;</literal>, etc.).</para>
|
||||
@@ -701,7 +701,7 @@ configureFlags = "
|
||||
number of spaces equal to the minimal indentation of the string as
|
||||
a whole (disregarding the indentation of empty lines). For
|
||||
instance, the first and second line are indented two space, while
|
||||
the third line is indented three spaces. Thus, two spaces are
|
||||
the third line is indented four spaces. Thus, two spaces are
|
||||
stripped from each line, so the resulting string is
|
||||
|
||||
<programlisting>
|
||||
@@ -805,20 +805,21 @@ to be enclosed in parentheses. If they had been omitted, e.g.,
|
||||
[ 123 ./foo.nix "abc" f { x = y; } ]</programlisting>
|
||||
|
||||
the result would be a list of five elements, the fourth one being a
|
||||
function and the fifth being an attribute set.</para>
|
||||
function and the fifth being a set.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Attribute sets</title>
|
||||
<simplesect><title>Sets</title>
|
||||
|
||||
<para>Attribute sets are really the core of the language, since
|
||||
ultimately it's all about creating derivations, which are really just
|
||||
<para>Sets are really the core of the language, since ultimately the
|
||||
Nix language is all about creating derivations, which are really just
|
||||
sets of attributes to be passed to build scripts.</para>
|
||||
|
||||
<para>Attribute sets are just a list of name/value pairs enclosed in
|
||||
curly brackets, where each value is an arbitrary expression terminated
|
||||
by a semicolon. For example:
|
||||
<para>Sets are just a list of name/value pairs (called
|
||||
<emphasis>attributes</emphasis>) enclosed in curly brackets, where
|
||||
each value is an arbitrary expression terminated by a semicolon. For
|
||||
example:
|
||||
|
||||
<programlisting>
|
||||
{ x = 123;
|
||||
@@ -826,12 +827,12 @@ by a semicolon. For example:
|
||||
y = f { bla = 456; };
|
||||
}</programlisting>
|
||||
|
||||
This defines an attribute set with attributes named
|
||||
<varname>x</varname>, <varname>test</varname>, <varname>y</varname>.
|
||||
The order of the attributes is irrelevant. An attribute name may only
|
||||
occur once.</para>
|
||||
This defines a set with attributes named <varname>x</varname>,
|
||||
<varname>text</varname>, <varname>y</varname>. The order of the
|
||||
attributes is irrelevant. An attribute name may only occur
|
||||
once.</para>
|
||||
|
||||
<para>Attributes can be selected from an attribute set using the
|
||||
<para>Attributes can be selected from a set using the
|
||||
<literal>.</literal> operator. For instance,
|
||||
|
||||
<programlisting>
|
||||
@@ -864,10 +865,10 @@ This will evaluate to <literal>123</literal>.</para>
|
||||
<section><title>Language constructs</title>
|
||||
|
||||
|
||||
<simplesect><title>Recursive attribute sets</title>
|
||||
<simplesect><title>Recursive sets</title>
|
||||
|
||||
<para>Recursive attribute sets are just normal attribute sets, but the
|
||||
attributes can refer to each other. For example,
|
||||
<para>Recursive sets are just normal sets, but the attributes can
|
||||
refer to each other. For example,
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
@@ -880,11 +881,11 @@ evaluates to <literal>123</literal>. Note that without
|
||||
<literal>rec</literal> the binding <literal>x = y;</literal> would
|
||||
refer to the variable <varname>y</varname> in the surrounding scope,
|
||||
if one exists, and would be invalid if no such variable exists. That
|
||||
is, in a normal (non-recursive) attribute set, attributes are not
|
||||
added to the lexical scope; in a recursive set, they are.</para>
|
||||
is, in a normal (non-recursive) set, attributes are not added to the
|
||||
lexical scope; in a recursive set, they are.</para>
|
||||
|
||||
<para>Recursive attribute sets of course introduce the danger of
|
||||
infinite recursion. For example,
|
||||
<para>Recursive sets of course introduce the danger of infinite
|
||||
recursion. For example,
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
@@ -901,8 +902,8 @@ encountered</quote>).</para></footnote>.</para>
|
||||
|
||||
<simplesect><title>Let-expressions</title>
|
||||
|
||||
<para>A let-expression allows you define local
|
||||
variables for an expression. For instance,
|
||||
<para>A let-expression allows you define local variables for an
|
||||
expression. For instance,
|
||||
|
||||
<programlisting>
|
||||
let
|
||||
@@ -914,20 +915,14 @@ evaluates to <literal>"foobar"</literal>.
|
||||
|
||||
</para>
|
||||
|
||||
<note><para>There is also an obsolete form of let-expression,
|
||||
<literal>let { <replaceable>attrs</replaceable> }</literal>, which is
|
||||
translated to <literal>rec { <replaceable>attrs</replaceable>
|
||||
}.body</literal>. That is, the body of the let-expression is the
|
||||
<literal>body</literal> attribute of the attribute set.</para></note>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Inheriting attributes</title>
|
||||
|
||||
<para>When defining an attribute set it is often convenient to copy
|
||||
variables from the surrounding lexical scope (e.g., when you want to
|
||||
propagate attributes). This can be shortened using the
|
||||
<para>When defining a set it is often convenient to copy variables
|
||||
from the surrounding lexical scope (e.g., when you want to propagate
|
||||
attributes). This can be shortened using the
|
||||
<literal>inherit</literal> keyword. For instance,
|
||||
|
||||
<programlisting>
|
||||
@@ -936,10 +931,10 @@ let x = 123; in
|
||||
y = 456;
|
||||
}</programlisting>
|
||||
|
||||
evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that this
|
||||
works because <varname>x</varname> is added to the lexical scope by
|
||||
the <literal>let</literal> construct.) It is also possible to inherit
|
||||
attributes from another attribute set. For instance, in this fragment
|
||||
evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that
|
||||
this works because <varname>x</varname> is added to the lexical scope
|
||||
by the <literal>let</literal> construct.) It is also possible to
|
||||
inherit attributes from another set. For instance, in this fragment
|
||||
from <filename>all-packages.nix</filename>,
|
||||
|
||||
<programlisting>
|
||||
@@ -958,13 +953,12 @@ from <filename>all-packages.nix</filename>,
|
||||
libjpg = ...;
|
||||
...</programlisting>
|
||||
|
||||
the attribute set used in the function call to the function defined in
|
||||
the set used in the function call to the function defined in
|
||||
<filename>../tools/graphics/graphviz</filename> inherits a number of
|
||||
variables from the surrounding scope (<varname>fetchurl</varname>
|
||||
... <varname>yacc</varname>), but also inherits
|
||||
<varname>libXaw</varname> (the X Athena Widgets) from the
|
||||
<varname>xlibs</varname> (X11 client-side libraries) attribute
|
||||
set.</para>
|
||||
<varname>xlibs</varname> (X11 client-side libraries) set.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
@@ -1003,11 +997,11 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
|
||||
"fooabc" ]</literal>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>An <emphasis>attribute set pattern</emphasis> of the
|
||||
form <literal>{ name1, name2, …, nameN }</literal>
|
||||
matches an attribute set containing the listed attributes, and binds
|
||||
the values of those attributes to variables in the function body.
|
||||
For example, the function
|
||||
<listitem><para>A <emphasis>set pattern</emphasis> of the form
|
||||
<literal>{ name1, name2, …, nameN }</literal> matches a set
|
||||
containing the listed attributes, and binds the values of those
|
||||
attributes to variables in the function body. For example, the
|
||||
function
|
||||
|
||||
<programlisting>
|
||||
{ x, y, z }: z + y + x</programlisting>
|
||||
@@ -1174,9 +1168,8 @@ used in the Nix expression for Subversion.</para>
|
||||
<programlisting>
|
||||
with <replaceable>e1</replaceable>; <replaceable>e2</replaceable></programlisting>
|
||||
|
||||
introduces the attribute set <replaceable>e1</replaceable> into the
|
||||
lexical scope of the expression <replaceable>e2</replaceable>. For
|
||||
instance,
|
||||
introduces the set <replaceable>e1</replaceable> into the lexical
|
||||
scope of the expression <replaceable>e2</replaceable>. For instance,
|
||||
|
||||
<programlisting>
|
||||
let as = { x = "foo"; y = "bar"; };
|
||||
@@ -1235,7 +1228,7 @@ weakest binding).</para>
|
||||
</entry>
|
||||
<entry>none</entry>
|
||||
<entry>Select attribute denoted by the attribute path
|
||||
<replaceable>attrpath</replaceable> from attribute set
|
||||
<replaceable>attrpath</replaceable> from set
|
||||
<replaceable>e</replaceable>. (An attribute path is a
|
||||
dot-separated list of attribute names.) If the attribute
|
||||
doesn’t exist, return <replaceable>def</replaceable> if
|
||||
@@ -1251,8 +1244,8 @@ weakest binding).</para>
|
||||
<entry><replaceable>e</replaceable> <literal>?</literal>
|
||||
<replaceable>attrpath</replaceable></entry>
|
||||
<entry>none</entry>
|
||||
<entry>Test whether attribute set <replaceable>e</replaceable>
|
||||
contains the attribute denoted by <replaceable>attrpath</replaceable>;
|
||||
<entry>Test whether set <replaceable>e</replaceable> contains
|
||||
the attribute denoted by <replaceable>attrpath</replaceable>;
|
||||
return <literal>true</literal> or
|
||||
<literal>false</literal>.</entry>
|
||||
</row>
|
||||
@@ -1275,10 +1268,11 @@ weakest binding).</para>
|
||||
<entry><replaceable>e1</replaceable> <literal>//</literal>
|
||||
<replaceable>e2</replaceable></entry>
|
||||
<entry>right</entry>
|
||||
<entry>Return an attribute set consisting of the attributes in
|
||||
<entry>Return a set consisting of the attributes in
|
||||
<replaceable>e1</replaceable> and
|
||||
<replaceable>e2</replaceable> (with the latter taking
|
||||
precedence over the former in case of equally named attributes).</entry>
|
||||
precedence over the former in case of equally named
|
||||
attributes).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><replaceable>e1</replaceable> <literal>==</literal>
|
||||
@@ -1322,9 +1316,9 @@ weakest binding).</para>
|
||||
<section xml:id="ssec-derivation"><title>Derivations</title>
|
||||
|
||||
<para>The most important built-in function is
|
||||
<function>derivation</function>, which is used to describe a
|
||||
single derivation (a build action). It takes as input an attribute
|
||||
set, the attributes of which specify the inputs of the build.</para>
|
||||
<function>derivation</function>, which is used to describe a single
|
||||
derivation (a build action). It takes as input a set, the attributes
|
||||
of which specify the inputs of the build.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package Nix::Manifest;
|
||||
|
||||
use strict;
|
||||
use DBI;
|
||||
use DBD::SQLite;
|
||||
use Cwd;
|
||||
use File::stat;
|
||||
use File::Path;
|
||||
|
||||
+37
-5
@@ -6,7 +6,7 @@ let
|
||||
|
||||
pkgs = import <nixpkgs> {};
|
||||
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" "i686-cygwin" "i686-solaris" ];
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" ];
|
||||
|
||||
|
||||
jobs = rec {
|
||||
@@ -173,15 +173,15 @@ let
|
||||
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50;
|
||||
rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) 60;
|
||||
rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) 60;
|
||||
rpm_fedora19i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora19i386) 70;
|
||||
rpm_fedora19x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora19x86_64) 70;
|
||||
|
||||
|
||||
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
|
||||
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
|
||||
deb_debian70i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian70i386) 60;
|
||||
deb_debian70x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian70x86_64) 60;
|
||||
deb_debian7i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian7i386) 60;
|
||||
deb_debian7x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian7x86_64) 60;
|
||||
|
||||
deb_ubuntu1004i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1004i386) 50;
|
||||
deb_ubuntu1004x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1004x86_64) 50;
|
||||
deb_ubuntu1010i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1010i386) 50;
|
||||
deb_ubuntu1010x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1010x86_64) 50;
|
||||
deb_ubuntu1110i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1110i386) 60;
|
||||
@@ -192,6 +192,8 @@ let
|
||||
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70;
|
||||
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) 80;
|
||||
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) 80;
|
||||
deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386) 90;
|
||||
deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64) 90;
|
||||
|
||||
|
||||
# System tests.
|
||||
@@ -203,6 +205,36 @@ let
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
}).test;
|
||||
|
||||
|
||||
# Aggregate job containing the release-critical jobs.
|
||||
release = pkgs.releaseTools.aggregate {
|
||||
name = "nix-${tarball.version}";
|
||||
meta.description = "Release-critical builds";
|
||||
constituents =
|
||||
[ tarball
|
||||
build.i686-freebsd
|
||||
build.i686-linux
|
||||
build.x86_64-darwin
|
||||
build.x86_64-freebsd
|
||||
build.x86_64-linux
|
||||
binaryTarball.i686-freebsd
|
||||
binaryTarball.i686-linux
|
||||
binaryTarball.x86_64-darwin
|
||||
binaryTarball.x86_64-freebsd
|
||||
binaryTarball.x86_64-linux
|
||||
deb_debian7i386
|
||||
deb_debian7x86_64
|
||||
deb_ubuntu1304i386
|
||||
deb_ubuntu1304x86_64
|
||||
deb_ubuntu1310i386
|
||||
deb_ubuntu1310x86_64
|
||||
rpm_fedora19i386
|
||||
rpm_fedora19x86_64
|
||||
tests.remote_builds
|
||||
tests.nix_copy_closure
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ close UPLOADLOCK;
|
||||
my $buildFlags =
|
||||
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
|
||||
. " --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false";
|
||||
. " --option build-keep-log false --option build-use-substitutes false";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
# closed unexpectedly. This is necessary to ensure that no processes
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use DBI;
|
||||
use DBD::SQLite;
|
||||
use File::Basename;
|
||||
use IO::Select;
|
||||
use Nix::Config;
|
||||
|
||||
@@ -202,15 +202,18 @@ foreach my $expr (@exprs) {
|
||||
my $rcfile = "$tmpDir/rc";
|
||||
writeFile(
|
||||
$rcfile,
|
||||
'[ -e ~/.bashrc ] && source ~/.bashrc; ' .
|
||||
'unset BASH_ENV; ' .
|
||||
'[ -n "$PS1" ] && [ -e ~/.bashrc ] && source ~/.bashrc; ' .
|
||||
($pure ? '' : 'p=$PATH; ' ) .
|
||||
'dontAddDisableDepTrack=1; ' .
|
||||
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
||||
($pure ? '' : 'PATH=$PATH:$p; ') .
|
||||
'set +e; ' .
|
||||
'PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
||||
'[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
||||
'unset NIX_ENFORCE_PURITY; ' .
|
||||
'shopt -u nullglob; ' .
|
||||
$envCommand);
|
||||
$ENV{BASH_ENV} = $rcfile;
|
||||
exec($ENV{NIX_BUILD_SHELL} // "bash", "--rcfile", $rcfile);
|
||||
die;
|
||||
}
|
||||
|
||||
@@ -35,15 +35,14 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
v = vNew;
|
||||
state.forceValue(*v);
|
||||
|
||||
/* It should evaluate to either an attribute set or an
|
||||
expression, according to what is specified in the
|
||||
attrPath. */
|
||||
/* It should evaluate to either a set or an expression,
|
||||
according to what is specified in the attrPath. */
|
||||
|
||||
if (apType == apAttr) {
|
||||
|
||||
if (v->type != tAttrs)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
|
||||
format("the expression selected by the selection path `%1%' should be a set but is %2%")
|
||||
% curPath % showType(*v));
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
|
||||
@@ -45,7 +45,7 @@ inline void EvalState::forceAttrs(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tAttrs)
|
||||
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
||||
throwTypeError("value is %1% while a set was expected", showType(v));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+41
-52
@@ -116,7 +116,7 @@ string showType(const Value & v)
|
||||
case tString: return "a string";
|
||||
case tPath: return "a path";
|
||||
case tNull: return "null";
|
||||
case tAttrs: return "an attribute set";
|
||||
case tAttrs: return "a set";
|
||||
case tList: return "a list";
|
||||
case tThunk: return "a thunk";
|
||||
case tApp: return "a function application";
|
||||
@@ -136,6 +136,7 @@ EvalState::EvalState()
|
||||
, sType(symbols.create("type"))
|
||||
, sMeta(symbols.create("meta"))
|
||||
, sName(symbols.create("name"))
|
||||
, sValue(symbols.create("value"))
|
||||
, sSystem(symbols.create("system"))
|
||||
, sOverrides(symbols.create("__overrides"))
|
||||
, sOutputs(symbols.create("outputs"))
|
||||
@@ -248,36 +249,26 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s))
|
||||
throw TypeError(s);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2))
|
||||
{
|
||||
throw TypeError(format(s) % pos % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2))
|
||||
{
|
||||
throw TypeError(format(s) % s1 % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw TypeError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw AssertionError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const char * s, const string & s1, const Pos & pos))
|
||||
{
|
||||
throw UndefinedVarError(format(s) % s1 % pos);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
|
||||
{
|
||||
e.addPrefix(format(s) % s2);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const Pos & pos))
|
||||
{
|
||||
e.addPrefix(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
|
||||
{
|
||||
e.addPrefix(format(s) % s2 % pos);
|
||||
@@ -310,7 +301,7 @@ void mkPath(Value & v, const char * s)
|
||||
}
|
||||
|
||||
|
||||
inline Value * EvalState::lookupVar(Env * env, const VarRef & var, bool noEval)
|
||||
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
{
|
||||
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
||||
|
||||
@@ -319,9 +310,9 @@ inline Value * EvalState::lookupVar(Env * env, const VarRef & var, bool noEval)
|
||||
while (1) {
|
||||
if (!env->haveWithAttrs) {
|
||||
if (noEval) return 0;
|
||||
Expr * attrs = (Expr *) env->values[0];
|
||||
env->values[0] = allocValue();
|
||||
evalAttrs(*env->up, attrs, *env->values[0]);
|
||||
Value * v = allocValue();
|
||||
evalAttrs(*env->up, (Expr *) env->values[0], *v);
|
||||
env->values[0] = v;
|
||||
env->haveWithAttrs = true;
|
||||
}
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
@@ -330,7 +321,7 @@ inline Value * EvalState::lookupVar(Env * env, const VarRef & var, bool noEval)
|
||||
return j->value;
|
||||
}
|
||||
if (!env->prevWith)
|
||||
throwEvalError("undefined variable `%1%'", var.name);
|
||||
throwUndefinedVarError("undefined variable `%1%' at %2%", var.name, var.pos);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
@@ -417,7 +408,7 @@ unsigned long nrAvoided = 0;
|
||||
|
||||
Value * ExprVar::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
Value * v = state.lookupVar(&env, info, true);
|
||||
Value * v = state.lookupVar(&env, *this, true);
|
||||
/* The value might not be initialised in the environment yet.
|
||||
In that case, ignore it. */
|
||||
if (v) { nrAvoided++; return v; }
|
||||
@@ -446,10 +437,14 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||
|
||||
void EvalState::evalFile(const Path & path, Value & v)
|
||||
{
|
||||
Path path2 = resolveExprPath(path);
|
||||
FileEvalCache::iterator i;
|
||||
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
||||
v = i->second;
|
||||
return;
|
||||
}
|
||||
|
||||
FileEvalCache::iterator i = fileEvalCache.find(path2);
|
||||
if (i != fileEvalCache.end()) {
|
||||
Path path2 = resolveExprPath(path);
|
||||
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
|
||||
v = i->second;
|
||||
return;
|
||||
}
|
||||
@@ -462,8 +457,9 @@ void EvalState::evalFile(const Path & path, Value & v)
|
||||
addErrorPrefix(e, "while evaluating the file `%1%':\n", path2);
|
||||
throw;
|
||||
}
|
||||
|
||||
fileEvalCache[path2] = v;
|
||||
//if (path != path2) fileEvalCache[path2] = v;
|
||||
if (path != path2) fileEvalCache[path] = v;
|
||||
}
|
||||
|
||||
|
||||
@@ -493,7 +489,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
||||
{
|
||||
e->eval(*this, env, v);
|
||||
if (v.type != tAttrs)
|
||||
throwTypeError("value is %1% while an attribute set was expected", showType(v));
|
||||
throwTypeError("value is %1% while a set was expected", showType(v));
|
||||
}
|
||||
|
||||
|
||||
@@ -607,7 +603,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value * v2 = state.lookupVar(&env, info, false);
|
||||
Value * v2 = state.lookupVar(&env, *this, false);
|
||||
state.forceValue(*v2);
|
||||
v = *v2;
|
||||
}
|
||||
@@ -646,11 +642,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
if (state.countCalls && pos) state.attrSelects[*pos]++;
|
||||
}
|
||||
|
||||
|
||||
state.forceValue(*vAttrs);
|
||||
|
||||
} catch (Error & e) {
|
||||
if (pos)
|
||||
if (pos && pos->file != state.sDerivationNix)
|
||||
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
|
||||
showAttrPath(attrPath), *pos);
|
||||
throw;
|
||||
@@ -728,12 +723,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
/* And call the primop. */
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[primOp->primOp->name]++;
|
||||
try {
|
||||
primOp->primOp->fun(*this, vArgs, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp->name);
|
||||
throw;
|
||||
}
|
||||
primOp->primOp->fun(*this, vArgs, v);
|
||||
} else {
|
||||
v.type = tPrimOpApp;
|
||||
v.primOpApp.left = allocValue();
|
||||
@@ -909,9 +899,8 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
state.mkAttrs(v, v1.attrs->size() + v2.attrs->size());
|
||||
|
||||
/* Merge the attribute sets, preferring values from the second
|
||||
set. Make sure to keep the resulting vector in sorted
|
||||
order. */
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
Bindings::iterator i = v1.attrs->begin();
|
||||
Bindings::iterator j = v2.attrs->begin();
|
||||
|
||||
@@ -975,8 +964,8 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
std::ostringstream s;
|
||||
NixInt n = 0;
|
||||
|
||||
bool first = true;
|
||||
ValueType firstType;
|
||||
bool first = !forceString;
|
||||
ValueType firstType = tString;
|
||||
|
||||
foreach (vector<Expr *>::iterator, i, *es) {
|
||||
Value vTmp;
|
||||
@@ -991,12 +980,12 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (firstType == tInt && !forceString) {
|
||||
if (firstType == tInt) {
|
||||
if (vTmp.type != tInt)
|
||||
throwEvalError("cannot add %1% to an integer", showType(vTmp));
|
||||
n += vTmp.integer;
|
||||
} else
|
||||
s << state.coerceToString(vTmp, context, false, firstType != tPath);
|
||||
s << state.coerceToString(vTmp, context, false, firstType == tString);
|
||||
}
|
||||
|
||||
if (firstType == tInt)
|
||||
@@ -1094,7 +1083,7 @@ bool EvalState::isDerivation(Value & v)
|
||||
if (i == v.attrs->end()) return false;
|
||||
forceValue(*i->value);
|
||||
if (i->value->type != tString) return false;
|
||||
return forceStringNoCtx(*i->value) == "derivation";
|
||||
return strcmp(i->value->string.s, "derivation") == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1136,8 +1125,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end())
|
||||
throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
|
||||
if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string");
|
||||
return coerceToString(*i->value, context, coerceMore, copyToStore);
|
||||
}
|
||||
|
||||
@@ -1183,9 +1171,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
forceValue(v2);
|
||||
|
||||
/* !!! Hack to support some old broken code that relies on pointer
|
||||
equality tests between attribute sets. (Specifically,
|
||||
builderDefs calls uniqList on a list of attribute sets.) Will
|
||||
remove this eventually. */
|
||||
equality tests between sets. (Specifically, builderDefs calls
|
||||
uniqList on a list of sets.) Will remove this eventually. */
|
||||
if (&v1 == &v2) return true;
|
||||
|
||||
if (v1.type != v2.type) return false;
|
||||
@@ -1223,8 +1210,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
return true;
|
||||
|
||||
case tAttrs: {
|
||||
/* If both attribute sets denote a derivation (type =
|
||||
"derivation"), then compare their outPaths. */
|
||||
/* If both sets denote a derivation (type = "derivation"),
|
||||
then compare their outPaths. */
|
||||
if (isDerivation(v1) && isDerivation(v2)) {
|
||||
Bindings::iterator i = v1.attrs->find(sOutPath);
|
||||
Bindings::iterator j = v2.attrs->find(sOutPath);
|
||||
@@ -1274,10 +1261,11 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
|
||||
printMsg(v, format(" values allocated: %1% (%2% bytes)")
|
||||
% nrValues % (nrValues * sizeof(Value)));
|
||||
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
|
||||
printMsg(v, format(" sets allocated: %1%") % nrAttrsets);
|
||||
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
|
||||
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
|
||||
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size());
|
||||
printMsg(v, format(" size of symbol table: %1%") % symbols.totalSize());
|
||||
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
||||
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
||||
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
||||
@@ -1285,6 +1273,7 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
|
||||
|
||||
if (countCalls) {
|
||||
v = lvlInfo;
|
||||
|
||||
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
||||
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
||||
|
||||
+5
-4
@@ -19,8 +19,8 @@ class EvalState;
|
||||
struct Attr;
|
||||
|
||||
|
||||
/* Attribute sets are represented as a vector of attributes, sorted by
|
||||
symbol (i.e. pointer to the attribute name in the symbol table). */
|
||||
/* Sets are represented as a vector of attributes, sorted by symbol
|
||||
(i.e. pointer to the attribute name in the symbol table). */
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::vector<Attr, gc_allocator<Attr> > BindingsBase;
|
||||
#else
|
||||
@@ -93,8 +93,9 @@ class EvalState
|
||||
public:
|
||||
SymbolTable symbols;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName,
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls;
|
||||
Symbol sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
@@ -209,7 +210,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
inline Value * lookupVar(Env * env, const VarRef & var, bool noEval);
|
||||
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
|
||||
|
||||
friend class ExprVar;
|
||||
friend class ExprAttrs;
|
||||
|
||||
+10
-12
@@ -41,7 +41,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
|
||||
|
||||
/* For each output... */
|
||||
for (unsigned int j = 0; j < i->value->list.length; ++j) {
|
||||
/* Evaluate the corresponding attribute set. */
|
||||
/* Evaluate the corresponding set. */
|
||||
string name = state.forceStringNoCtx(*i->value->list.elems[j]);
|
||||
Bindings::iterator out = attrs->find(state.symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
@@ -119,11 +119,10 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta)
|
||||
typedef set<Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to an attribute set of type
|
||||
`derivation', then put information about it in `drvs' (unless it's
|
||||
already in `doneExprs'). The result boolean indicates whether it
|
||||
makes sense for the caller to recursively search for derivations in
|
||||
`v'. */
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs' (unless it's already in
|
||||
`doneExprs'). The result boolean indicates whether it makes sense
|
||||
for the caller to recursively search for derivations in `v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const string & attrPath, DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
@@ -132,8 +131,8 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
state.forceValue(v);
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., an attribute set like
|
||||
`rec { x = derivation {...}; y = x;}'. */
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (done.find(v.attrs) != done.end()) return false;
|
||||
done.insert(v.attrs);
|
||||
|
||||
@@ -218,10 +217,9 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
if (combineChannels)
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
/* If the value of this attribute is itself an
|
||||
attribute set, should we recurse into it? => Only
|
||||
if it has a `recurseForDerivations = true'
|
||||
attribute. */
|
||||
/* If the value of this attribute is itself a set,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (v2.type == tAttrs) {
|
||||
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
|
||||
if (j != v2.attrs->end() && state.forceBool(*j->value))
|
||||
|
||||
+15
-8
@@ -38,7 +38,7 @@ void ExprPath::show(std::ostream & str)
|
||||
|
||||
void ExprVar::show(std::ostream & str)
|
||||
{
|
||||
str << info.name;
|
||||
str << name;
|
||||
}
|
||||
|
||||
void ExprSelect::show(std::ostream & str)
|
||||
@@ -174,7 +174,7 @@ void ExprPath::bindVars(const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void VarRef::bind(const StaticEnv & env)
|
||||
void ExprVar::bindVars(const StaticEnv & env)
|
||||
{
|
||||
/* Check whether the variable appears in the environment. If so,
|
||||
set its level and displacement. */
|
||||
@@ -198,17 +198,12 @@ void VarRef::bind(const StaticEnv & env)
|
||||
/* Otherwise, the variable must be obtained from the nearest
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1) throw EvalError(format("undefined variable `%1%'") % name);
|
||||
if (withLevel == -1) throw UndefinedVarError(format("undefined variable `%1%' at %2%") % name % pos);
|
||||
|
||||
fromWith = true;
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
void ExprVar::bindVars(const StaticEnv & env)
|
||||
{
|
||||
info.bind(env);
|
||||
}
|
||||
|
||||
void ExprSelect::bindVars(const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
@@ -341,4 +336,16 @@ string ExprLambda::showNamePos()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Symbol table. */
|
||||
|
||||
size_t SymbolTable::totalSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
foreach (Symbols::const_iterator, i, symbols)
|
||||
n += i->size();
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+11
-15
@@ -16,20 +16,23 @@ MakeError(ThrownError, AssertionError)
|
||||
MakeError(Abort, EvalError)
|
||||
MakeError(TypeError, EvalError)
|
||||
MakeError(ImportError, EvalError) // error building an imported derivation
|
||||
MakeError(UndefinedVarError, Error)
|
||||
|
||||
|
||||
/* Position objects. */
|
||||
|
||||
struct Pos
|
||||
{
|
||||
string file;
|
||||
Symbol file;
|
||||
unsigned int line, column;
|
||||
Pos() : line(0), column(0) { };
|
||||
Pos(const string & file, unsigned int line, unsigned int column)
|
||||
Pos(const Symbol & file, unsigned int line, unsigned int column)
|
||||
: file(file), line(line), column(column) { };
|
||||
bool operator < (const Pos & p2) const
|
||||
{
|
||||
int d = file.compare(p2.file);
|
||||
if (!line) return p2.line;
|
||||
if (!p2.line) return false;
|
||||
int d = ((string) file).compare((string) p2.file);
|
||||
if (d < 0) return true;
|
||||
if (d > 0) return false;
|
||||
if (line < p2.line) return true;
|
||||
@@ -107,8 +110,9 @@ struct ExprPath : Expr
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
|
||||
struct VarRef
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
|
||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||
@@ -119,20 +123,12 @@ struct VarRef
|
||||
levels up from the current environment and getting the
|
||||
`displ'th value in that environment. In the latter case, the
|
||||
value is obtained by getting the attribute named `name' from
|
||||
the attribute set stored in the environment that is `level'
|
||||
levels up from the current one.*/
|
||||
the set stored in the environment that is `level' levels up
|
||||
from the current one.*/
|
||||
unsigned int level;
|
||||
unsigned int displ;
|
||||
|
||||
VarRef() { };
|
||||
VarRef(const Symbol & name) : name(name) { };
|
||||
void bind(const StaticEnv & env);
|
||||
};
|
||||
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
VarRef info;
|
||||
ExprVar(const Symbol & name) : info(name) { };
|
||||
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
|
||||
+19
-22
@@ -29,7 +29,7 @@ namespace nix {
|
||||
SymbolTable & symbols;
|
||||
Expr * result;
|
||||
Path basePath;
|
||||
Path path;
|
||||
Symbol path;
|
||||
string error;
|
||||
Symbol sLetBody;
|
||||
ParseData(EvalState & state)
|
||||
@@ -203,7 +203,8 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
/* If this is a single string, then don't do a concatenation. */
|
||||
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -310,13 +311,13 @@ expr_if
|
||||
|
||||
expr_op
|
||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__sub")), new ExprInt(0)), $2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), new ExprInt(0)), $2); }
|
||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||
| expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1)); }
|
||||
| expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3)); }
|
||||
| expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1)); }
|
||||
| expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $3), $1); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__lessThan")), $1), $3)); }
|
||||
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
|
||||
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
|
||||
@@ -328,9 +329,9 @@ expr_op
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(false, l);
|
||||
}
|
||||
| expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__sub")), $1), $3); }
|
||||
| expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__mul")), $1), $3); }
|
||||
| expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__div")), $1), $3); }
|
||||
| expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__sub")), $1), $3); }
|
||||
| expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__mul")), $1), $3); }
|
||||
| expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(noPos, data->symbols.create("__div")), $1), $3); }
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
@@ -349,17 +350,17 @@ expr_select
|
||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||
expr_simple OR_KW
|
||||
{ $$ = new ExprApp($1, new ExprVar(data->symbols.create("or"))); }
|
||||
{ $$ = new ExprApp($1, new ExprVar(CUR_POS, data->symbols.create("or"))); }
|
||||
| expr_simple { $$ = $1; }
|
||||
;
|
||||
|
||||
expr_simple
|
||||
: ID { $$ = new ExprVar(data->symbols.create($1)); }
|
||||
: ID { $$ = new ExprVar(CUR_POS, data->symbols.create($1)); }
|
||||
| INT { $$ = new ExprInt($1); }
|
||||
| '"' string_parts '"' {
|
||||
/* For efficiency, and to simplify parse trees a bit. */
|
||||
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
|
||||
else if ($2->size() == 1) $$ = $2->front();
|
||||
else if ($2->size() == 1 && dynamic_cast<ExprString *>($2->front())) $$ = $2->front();
|
||||
else $$ = new ExprConcatStrings(true, $2);
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
@@ -372,10 +373,10 @@ expr_simple
|
||||
/* The file wasn't found in the search path. However, we can't
|
||||
throw an error here, because the expression might never be
|
||||
evaluated. So return an expression that lazily calls
|
||||
‘abort’. */
|
||||
‘throw’. */
|
||||
$$ = path2 == ""
|
||||
? (Expr * ) new ExprApp(
|
||||
new ExprVar(data->symbols.create("throw")),
|
||||
new ExprVar(noPos, data->symbols.create("throw")),
|
||||
new ExprString(data->symbols.create(
|
||||
(format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str())))
|
||||
: (Expr * ) new ExprPath(path2);
|
||||
@@ -413,7 +414,7 @@ binds
|
||||
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
|
||||
Pos pos = makeCurPos(@3, data);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(*i), pos, true);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, *i), pos, true);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
@@ -486,7 +487,7 @@ Expr * EvalState::parse(const char * text,
|
||||
yyscan_t scanner;
|
||||
ParseData data(*this);
|
||||
data.basePath = basePath;
|
||||
data.path = path;
|
||||
data.path = data.symbols.create(path);
|
||||
|
||||
yylex_init(&scanner);
|
||||
yy_scan_string(text, scanner);
|
||||
@@ -495,11 +496,7 @@ Expr * EvalState::parse(const char * text,
|
||||
|
||||
if (res) throw ParseError(data.error);
|
||||
|
||||
try {
|
||||
data.result->bindVars(staticEnv);
|
||||
} catch (Error & e) {
|
||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
}
|
||||
data.result->bindVars(staticEnv);
|
||||
|
||||
return data.result;
|
||||
}
|
||||
|
||||
+79
-45
@@ -92,6 +92,30 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Return a string representing the type of the expression. */
|
||||
static void prim_typeOf(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
string t;
|
||||
switch (args[0]->type) {
|
||||
case tInt: t = "int"; break;
|
||||
case tBool: t = "bool"; break;
|
||||
case tString: t = "string"; break;
|
||||
case tPath: t = "path"; break;
|
||||
case tNull: t = "null"; break;
|
||||
case tAttrs: t = "set"; break;
|
||||
case tList: t = "list"; break;
|
||||
case tLambda:
|
||||
case tPrimOp:
|
||||
case tPrimOpApp:
|
||||
t = "lambda";
|
||||
break;
|
||||
default: abort();
|
||||
}
|
||||
mkString(v, state.symbols.create(t));
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is the null value. */
|
||||
static void prim_isNull(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
@@ -108,7 +132,7 @@ static void prim_isFunction(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an Int. */
|
||||
/* Determine whether the argument is an integer. */
|
||||
static void prim_isInt(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -116,7 +140,7 @@ static void prim_isInt(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an String. */
|
||||
/* Determine whether the argument is a string. */
|
||||
static void prim_isString(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -124,7 +148,7 @@ static void prim_isString(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an Bool. */
|
||||
/* Determine whether the argument is a Boolean. */
|
||||
static void prim_isBool(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -134,24 +158,31 @@ static void prim_isBool(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
struct CompareValues
|
||||
{
|
||||
bool operator () (const Value & v1, const Value & v2) const
|
||||
bool operator () (const Value * v1, const Value * v2) const
|
||||
{
|
||||
if (v1.type != v2.type)
|
||||
if (v1->type != v2->type)
|
||||
throw EvalError("cannot compare values of different types");
|
||||
switch (v1.type) {
|
||||
switch (v1->type) {
|
||||
case tInt:
|
||||
return v1.integer < v2.integer;
|
||||
return v1->integer < v2->integer;
|
||||
case tString:
|
||||
return strcmp(v1.string.s, v2.string.s) < 0;
|
||||
return strcmp(v1->string.s, v2->string.s) < 0;
|
||||
case tPath:
|
||||
return strcmp(v1.path, v2.path) < 0;
|
||||
return strcmp(v1->path, v2->path) < 0;
|
||||
default:
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(v1) % showType(v2));
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
typedef list<Value *, gc_allocator<Value *> > ValueList;
|
||||
#else
|
||||
typedef list<Value *> ValueList;
|
||||
#endif
|
||||
|
||||
|
||||
static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
startNest(nest, lvlDebug, "finding dependencies");
|
||||
@@ -165,7 +196,7 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError("attribute `startSet' required");
|
||||
state.forceList(*startSet->value);
|
||||
|
||||
list<Value *> workSet;
|
||||
ValueList workSet;
|
||||
for (unsigned int n = 0; n < startSet->value->list.length; ++n)
|
||||
workSet.push_back(startSet->value->list.elems[n]);
|
||||
|
||||
@@ -179,8 +210,10 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
/* Construct the closure by applying the operator to element of
|
||||
`workSet', adding the result to `workSet', continuing until
|
||||
no new elements are found. */
|
||||
list<Value> res;
|
||||
set<Value, CompareValues> doneKeys; // !!! use Value *?
|
||||
ValueList res;
|
||||
// `doneKeys' doesn't need to be a GC root, because its values are
|
||||
// reachable from res.
|
||||
set<Value *, CompareValues> doneKeys;
|
||||
while (!workSet.empty()) {
|
||||
Value * e = *(workSet.begin());
|
||||
workSet.pop_front();
|
||||
@@ -193,9 +226,9 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError("attribute `key' required");
|
||||
state.forceValue(*key->value);
|
||||
|
||||
if (doneKeys.find(*key->value) != doneKeys.end()) continue;
|
||||
doneKeys.insert(*key->value);
|
||||
res.push_back(*e);
|
||||
if (doneKeys.find(key->value) != doneKeys.end()) continue;
|
||||
doneKeys.insert(key->value);
|
||||
res.push_back(e);
|
||||
|
||||
/* Call the `operator' function with `e' as argument. */
|
||||
Value call;
|
||||
@@ -212,8 +245,8 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
/* Create the result list. */
|
||||
state.mkList(v, res.size());
|
||||
unsigned int n = 0;
|
||||
foreach (list<Value>::iterator, i, res)
|
||||
*(v.list.elems[n++] = state.allocValue()) = *i;
|
||||
foreach (ValueList::iterator, i, res)
|
||||
v.list.elems[n++] = *i;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,10 +286,10 @@ static void prim_tryEval(EvalState & state, Value * * args, Value & v)
|
||||
state.mkAttrs(v, 2);
|
||||
try {
|
||||
state.forceValue(*args[0]);
|
||||
v.attrs->push_back(Attr(state.symbols.create("value"), args[0]));
|
||||
v.attrs->push_back(Attr(state.sValue, args[0]));
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("success")), true);
|
||||
} catch (AssertionError & e) {
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("value")), false);
|
||||
mkBool(*state.allocAttr(v, state.sValue), false);
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("success")), false);
|
||||
}
|
||||
v.attrs->sort();
|
||||
@@ -394,10 +427,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n")
|
||||
% key % *i->pos);
|
||||
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
|
||||
% drvName % posDrvName);
|
||||
e.addPrefix(format("while evaluating the attribute `%1%' of the derivation `%2%' at %3%:\n")
|
||||
% key % drvName % posDrvName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -707,12 +738,12 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Attribute sets
|
||||
* Sets
|
||||
*************************************************************/
|
||||
|
||||
|
||||
/* Return the names of the attributes in an attribute set as a sorted
|
||||
list of strings. */
|
||||
/* Return the names of the attributes in a set as a sorted list of
|
||||
strings. */
|
||||
static void prim_attrNames(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0]);
|
||||
@@ -754,7 +785,7 @@ static void prim_hasAttr(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is an attribute set. */
|
||||
/* Determine whether the argument is a set. */
|
||||
static void prim_isAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
@@ -785,10 +816,11 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Builds an attribute set from a list specifying (name, value)
|
||||
pairs. To be precise, a list [{name = "name1"; value = value1;}
|
||||
... {name = "nameN"; value = valueN;}] is transformed to {name1 =
|
||||
value1; ... nameN = valueN;}. */
|
||||
/* Builds a set from a list specifying (name, value) pairs. To be
|
||||
precise, a list [{name = "name1"; value = value1;} ... {name =
|
||||
"nameN"; value = valueN;}] is transformed to {name1 = value1;
|
||||
... nameN = valueN;}. In case of duplicate occurences of the same
|
||||
name, the first takes precedence. */
|
||||
static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
@@ -806,25 +838,24 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
|
||||
throw TypeError("`name' attribute missing in a call to `listToAttrs'");
|
||||
string name = state.forceStringNoCtx(*j->value);
|
||||
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create("value"));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError("`value' attribute missing in a call to `listToAttrs'");
|
||||
|
||||
Symbol sym = state.symbols.create(name);
|
||||
if (seen.find(sym) == seen.end()) {
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError("`value' attribute missing in a call to `listToAttrs'");
|
||||
|
||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||
seen.insert(sym);
|
||||
}
|
||||
/* !!! Throw an error if `name' already exists? */
|
||||
}
|
||||
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
||||
|
||||
/* Return the right-biased intersection of two attribute sets as1 and
|
||||
as2, i.e. a set that contains every attribute from as2 that is also
|
||||
a member of as1. */
|
||||
/* Return the right-biased intersection of two sets as1 and as2,
|
||||
i.e. a set that contains every attribute from as2 that is also a
|
||||
member of as1. */
|
||||
static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0]);
|
||||
@@ -1034,7 +1065,7 @@ static void prim_lessThan(EvalState & state, Value * * args, Value & v)
|
||||
state.forceValue(*args[0]);
|
||||
state.forceValue(*args[1]);
|
||||
CompareValues comp;
|
||||
mkBool(v, comp(*args[0], *args[1]));
|
||||
mkBool(v, comp(args[0], args[1]));
|
||||
}
|
||||
|
||||
|
||||
@@ -1191,6 +1222,7 @@ void EvalState::createBaseEnv()
|
||||
|
||||
// Miscellaneous
|
||||
addPrimOp("import", 1, prim_import);
|
||||
addPrimOp("__typeOf", 1, prim_typeOf);
|
||||
addPrimOp("isNull", 1, prim_isNull);
|
||||
addPrimOp("__isFunction", 1, prim_isFunction);
|
||||
addPrimOp("__isString", 1, prim_isString);
|
||||
@@ -1217,7 +1249,7 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__toFile", 2, prim_toFile);
|
||||
addPrimOp("__filterSource", 2, prim_filterSource);
|
||||
|
||||
// Attribute sets
|
||||
// Sets
|
||||
addPrimOp("__attrNames", 1, prim_attrNames);
|
||||
addPrimOp("__getAttr", 2, prim_getAttr);
|
||||
addPrimOp("__hasAttr", 2, prim_hasAttr);
|
||||
@@ -1262,11 +1294,13 @@ void EvalState::createBaseEnv()
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
evalFile(findFile("nix/derivation.nix"), v);
|
||||
string path = findFile("nix/derivation.nix");
|
||||
sDerivationNix = symbols.create(path);
|
||||
evalFile(path, v);
|
||||
addConstant("derivation", v);
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' attribute
|
||||
set, because attribute lookups expect it to be sorted. */
|
||||
/* Now that we've added all primops, sort the `builtins' set,
|
||||
because attribute lookups expect it to be sorted. */
|
||||
baseEnv.values[0]->attrs->sort();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
namespace nix {
|
||||
|
||||
/* Symbol table used by the parser and evaluator to represent and look
|
||||
up identifiers and attribute sets efficiently.
|
||||
SymbolTable::create() converts a string into a symbol. Symbols
|
||||
have the property that they can be compared efficiently (using a
|
||||
pointer equality test), because the symbol table stores only one
|
||||
copy of each string. */
|
||||
up identifiers and attributes efficiently. SymbolTable::create()
|
||||
converts a string into a symbol. Symbols have the property that
|
||||
they can be compared efficiently (using a pointer equality test),
|
||||
because the symbol table stores only one copy of each string. */
|
||||
|
||||
class Symbol
|
||||
{
|
||||
@@ -89,6 +88,8 @@ public:
|
||||
{
|
||||
return symbols.size();
|
||||
}
|
||||
|
||||
size_t totalSize() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -303,6 +303,9 @@ int main(int argc, char * * argv)
|
||||
if (e.prefix() != "" && !showTrace)
|
||||
printMsg(lvlError, "(use `--show-trace' to show detailed location information)");
|
||||
return e.status;
|
||||
} catch (std::bad_alloc & e) {
|
||||
printMsg(lvlError, "error: out of memory");
|
||||
return 1;
|
||||
} catch (std::exception & e) {
|
||||
printMsg(lvlError, format("error: %1%") % e.what());
|
||||
return 1;
|
||||
|
||||
+1
-1
@@ -329,7 +329,7 @@ static void findRoots(StoreAPI & store, const Path & path, Roots & roots)
|
||||
struct stat st2 = lstat(target);
|
||||
if (!S_ISLNK(st2.st_mode)) return;
|
||||
Path target2 = readLink(target);
|
||||
if (isInStore(target2)) foundRoot(store, path, target2, roots);
|
||||
if (isInStore(target2)) foundRoot(store, target, target2, roots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+213
-183
@@ -48,11 +48,15 @@ static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
{
|
||||
int err = sqlite3_errcode(db);
|
||||
if (err == SQLITE_BUSY) {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printMsg(lvlError, "warning: SQLite database is busy");
|
||||
warned = true;
|
||||
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
||||
if (err == SQLITE_PROTOCOL)
|
||||
printMsg(lvlError, "warning: SQLite database is busy (SQLITE_PROTOCOL)");
|
||||
else {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printMsg(lvlError, "warning: SQLite database is busy");
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
/* Sleep for a while since retrying the transaction right away
|
||||
is likely to fail again. */
|
||||
@@ -71,6 +75,11 @@ static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
}
|
||||
|
||||
|
||||
/* Convenience macros for retrying a SQLite transaction. */
|
||||
#define retry_sqlite while (1) { try {
|
||||
#define end_retry_sqlite break; } catch (SQLiteBusy & e) { } }
|
||||
|
||||
|
||||
SQLite::~SQLite()
|
||||
{
|
||||
try {
|
||||
@@ -412,7 +421,7 @@ void LocalStore::openDB(bool create)
|
||||
stmtInvalidatePath.create(db,
|
||||
"delete from ValidPaths where path = ?;");
|
||||
stmtRegisterFailedPath.create(db,
|
||||
"insert into FailedPaths (path, time) values (?, ?);");
|
||||
"insert or ignore into FailedPaths (path, time) values (?, ?);");
|
||||
stmtHasPathFailed.create(db,
|
||||
"select time from FailedPaths where path = ?;");
|
||||
stmtQueryFailedPaths.create(db,
|
||||
@@ -683,57 +692,64 @@ void LocalStore::addReference(unsigned long long referrer, unsigned long long re
|
||||
|
||||
void LocalStore::registerFailedPath(const Path & path)
|
||||
{
|
||||
if (hasPathFailed(path)) return;
|
||||
SQLiteStmtUse use(stmtRegisterFailedPath);
|
||||
stmtRegisterFailedPath.bind(path);
|
||||
stmtRegisterFailedPath.bind(time(0));
|
||||
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("registering failed path `%1%'") % path);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtRegisterFailedPath);
|
||||
stmtRegisterFailedPath.bind(path);
|
||||
stmtRegisterFailedPath.bind(time(0));
|
||||
if (sqlite3_step(stmtRegisterFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("registering failed path `%1%'") % path);
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::hasPathFailed(const Path & path)
|
||||
{
|
||||
SQLiteStmtUse use(stmtHasPathFailed);
|
||||
stmtHasPathFailed.bind(path);
|
||||
int res = sqlite3_step(stmtHasPathFailed);
|
||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
||||
throwSQLiteError(db, "querying whether path failed");
|
||||
return res == SQLITE_ROW;
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtHasPathFailed);
|
||||
stmtHasPathFailed.bind(path);
|
||||
int res = sqlite3_step(stmtHasPathFailed);
|
||||
if (res != SQLITE_DONE && res != SQLITE_ROW)
|
||||
throwSQLiteError(db, "querying whether path failed");
|
||||
return res == SQLITE_ROW;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryFailedPaths()
|
||||
{
|
||||
SQLiteStmtUse use(stmtQueryFailedPaths);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryFailedPaths);
|
||||
|
||||
PathSet res;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
PathSet res;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryFailedPaths)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryFailedPaths, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error querying failed paths");
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error querying failed paths");
|
||||
|
||||
return res;
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::clearFailedPaths(const PathSet & paths)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
SQLiteStmtUse use(stmtClearFailedPath);
|
||||
stmtClearFailedPath.bind(*i);
|
||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
||||
}
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
SQLiteStmtUse use(stmtClearFailedPath);
|
||||
stmtClearFailedPath.bind(*i);
|
||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("clearing failed path `%1%' in database") % *i);
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
txn.commit();
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -758,44 +774,47 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||
|
||||
assertStorePath(path);
|
||||
|
||||
/* Get the path info. */
|
||||
SQLiteStmtUse use1(stmtQueryPathInfo);
|
||||
retry_sqlite {
|
||||
|
||||
stmtQueryPathInfo.bind(path);
|
||||
/* Get the path info. */
|
||||
SQLiteStmtUse use1(stmtQueryPathInfo);
|
||||
|
||||
int r = sqlite3_step(stmtQueryPathInfo);
|
||||
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
||||
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
|
||||
stmtQueryPathInfo.bind(path);
|
||||
|
||||
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
||||
int r = sqlite3_step(stmtQueryPathInfo);
|
||||
if (r == SQLITE_DONE) throw Error(format("path `%1%' is not valid") % path);
|
||||
if (r != SQLITE_ROW) throwSQLiteError(db, "querying path in database");
|
||||
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
|
||||
assert(s);
|
||||
info.hash = parseHashField(path, s);
|
||||
info.id = sqlite3_column_int(stmtQueryPathInfo, 0);
|
||||
|
||||
info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
|
||||
|
||||
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
||||
if (s) info.deriver = s;
|
||||
|
||||
/* Note that narSize = NULL yields 0. */
|
||||
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
|
||||
|
||||
/* Get the references. */
|
||||
SQLiteStmtUse use2(stmtQueryReferences);
|
||||
|
||||
stmtQueryReferences.bind(info.id);
|
||||
|
||||
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
||||
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 1);
|
||||
assert(s);
|
||||
info.references.insert(s);
|
||||
}
|
||||
info.hash = parseHashField(path, s);
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
||||
info.registrationTime = sqlite3_column_int(stmtQueryPathInfo, 2);
|
||||
|
||||
return info;
|
||||
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
||||
if (s) info.deriver = s;
|
||||
|
||||
/* Note that narSize = NULL yields 0. */
|
||||
info.narSize = sqlite3_column_int64(stmtQueryPathInfo, 4);
|
||||
|
||||
/* Get the references. */
|
||||
SQLiteStmtUse use2(stmtQueryReferences);
|
||||
|
||||
stmtQueryReferences.bind(info.id);
|
||||
|
||||
while ((r = sqlite3_step(stmtQueryReferences)) == SQLITE_ROW) {
|
||||
s = (const char *) sqlite3_column_text(stmtQueryReferences, 0);
|
||||
assert(s);
|
||||
info.references.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting references of `%1%'") % path);
|
||||
|
||||
return info;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -826,7 +845,7 @@ unsigned long long LocalStore::queryValidPathId(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isValidPath(const Path & path)
|
||||
bool LocalStore::isValidPath_(const Path & path)
|
||||
{
|
||||
SQLiteStmtUse use(stmtQueryPathInfo);
|
||||
stmtQueryPathInfo.bind(path);
|
||||
@@ -837,33 +856,44 @@ bool LocalStore::isValidPath(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isValidPath(const Path & path)
|
||||
{
|
||||
retry_sqlite {
|
||||
return isValidPath_(path);
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
||||
{
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (isValidPath(*i)) res.insert(*i);
|
||||
return res;
|
||||
retry_sqlite {
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (isValidPath_(*i)) res.insert(*i);
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryAllValidPaths()
|
||||
{
|
||||
SQLiteStmt stmt;
|
||||
stmt.create(db, "select path from ValidPaths");
|
||||
retry_sqlite {
|
||||
SQLiteStmt stmt;
|
||||
stmt.create(db, "select path from ValidPaths");
|
||||
|
||||
PathSet res;
|
||||
PathSet res;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmt, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmt, 0);
|
||||
assert(s);
|
||||
res.insert(s);
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error getting valid paths");
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, "error getting valid paths");
|
||||
|
||||
return res;
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -875,10 +905,8 @@ void LocalStore::queryReferences(const Path & path,
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||
void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
SQLiteStmtUse use(stmtQueryReferrers);
|
||||
|
||||
stmtQueryReferrers.bind(path);
|
||||
@@ -895,6 +923,15 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||
{
|
||||
assertStorePath(path);
|
||||
retry_sqlite {
|
||||
queryReferrers_(path, referrers);
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
Path LocalStore::queryDeriver(const Path & path)
|
||||
{
|
||||
return queryPathInfo(path).deriver;
|
||||
@@ -905,65 +942,67 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
SQLiteStmtUse use(stmtQueryValidDerivers);
|
||||
stmtQueryValidDerivers.bind(path);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryValidDerivers);
|
||||
stmtQueryValidDerivers.bind(path);
|
||||
|
||||
PathSet derivers;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1);
|
||||
assert(s);
|
||||
derivers.insert(s);
|
||||
}
|
||||
PathSet derivers;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryValidDerivers)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryValidDerivers, 1);
|
||||
assert(s);
|
||||
derivers.insert(s);
|
||||
}
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting valid derivers of `%1%'") % path);
|
||||
|
||||
return derivers;
|
||||
return derivers;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
PathSet outputs;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1);
|
||||
assert(s);
|
||||
outputs.insert(s);
|
||||
}
|
||||
|
||||
PathSet outputs;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 1);
|
||||
assert(s);
|
||||
outputs.insert(s);
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting outputs of `%1%'") % path);
|
||||
|
||||
return outputs;
|
||||
return outputs;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
||||
{
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
|
||||
SQLiteStmtUse use(stmtQueryDerivationOutputs);
|
||||
stmtQueryDerivationOutputs.bind(queryValidPathId(path));
|
||||
StringSet outputNames;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
|
||||
assert(s);
|
||||
outputNames.insert(s);
|
||||
}
|
||||
|
||||
StringSet outputNames;
|
||||
int r;
|
||||
while ((r = sqlite3_step(stmtQueryDerivationOutputs)) == SQLITE_ROW) {
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryDerivationOutputs, 0);
|
||||
assert(s);
|
||||
outputNames.insert(s);
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting output names of `%1%'") % path);
|
||||
|
||||
if (r != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("error getting output names of `%1%'") % path);
|
||||
|
||||
return outputNames;
|
||||
return outputNames;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -971,19 +1010,19 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
|
||||
{
|
||||
if (hashPart.size() != 32) throw Error("invalid hash part");
|
||||
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
Path prefix = settings.nixStore + "/" + hashPart;
|
||||
|
||||
SQLiteStmtUse use(stmtQueryPathFromHashPart);
|
||||
stmtQueryPathFromHashPart.bind(prefix);
|
||||
retry_sqlite {
|
||||
SQLiteStmtUse use(stmtQueryPathFromHashPart);
|
||||
stmtQueryPathFromHashPart.bind(prefix);
|
||||
|
||||
int res = sqlite3_step(stmtQueryPathFromHashPart);
|
||||
if (res == SQLITE_DONE) return "";
|
||||
if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database");
|
||||
int res = sqlite3_step(stmtQueryPathFromHashPart);
|
||||
if (res == SQLITE_DONE) return "";
|
||||
if (res != SQLITE_ROW) throwSQLiteError(db, "finding path in database");
|
||||
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
||||
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
||||
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -1221,39 +1260,33 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
* expense of some speed of the path registering operation. */
|
||||
if (settings.syncBeforeRegistering) sync();
|
||||
|
||||
while (1) {
|
||||
try {
|
||||
SQLiteTxn txn(db);
|
||||
PathSet paths;
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
PathSet paths;
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
if (isValidPath(i->path))
|
||||
updatePathInfo(*i);
|
||||
else
|
||||
addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
unsigned long long referrer = queryValidPathId(i->path);
|
||||
foreach (PathSet::iterator, j, i->references)
|
||||
addReference(referrer, queryValidPathId(*j));
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
error if a cycle is detected and roll back the
|
||||
transaction. Cycles can only occur when a derivation
|
||||
has multiple outputs. */
|
||||
topoSortPaths(*this, paths);
|
||||
|
||||
txn.commit();
|
||||
break;
|
||||
} catch (SQLiteBusy & e) {
|
||||
/* Retry; the `txn' destructor will roll back the current
|
||||
transaction. */
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
if (isValidPath_(i->path))
|
||||
updatePathInfo(*i);
|
||||
else
|
||||
addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
unsigned long long referrer = queryValidPathId(i->path);
|
||||
foreach (PathSet::iterator, j, i->references)
|
||||
addReference(referrer, queryValidPathId(*j));
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
error if a cycle is detected and roll back the
|
||||
transaction. Cycles can only occur when a derivation
|
||||
has multiple outputs. */
|
||||
topoSortPaths(*this, paths);
|
||||
|
||||
txn.commit();
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
@@ -1644,23 +1677,20 @@ void LocalStore::invalidatePathChecked(const Path & path)
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
while (1) {
|
||||
try {
|
||||
SQLiteTxn txn(db);
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
if (isValidPath(path)) {
|
||||
PathSet referrers; queryReferrers(path, referrers);
|
||||
referrers.erase(path); /* ignore self-references */
|
||||
if (!referrers.empty())
|
||||
throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
|
||||
% path % showPaths(referrers));
|
||||
invalidatePath(path);
|
||||
}
|
||||
if (isValidPath_(path)) {
|
||||
PathSet referrers; queryReferrers_(path, referrers);
|
||||
referrers.erase(path); /* ignore self-references */
|
||||
if (!referrers.empty())
|
||||
throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
|
||||
% path % showPaths(referrers));
|
||||
invalidatePath(path);
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
break;
|
||||
} catch (SQLiteBusy & e) { };
|
||||
}
|
||||
txn.commit();
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -304,6 +304,10 @@ private:
|
||||
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
|
||||
|
||||
void optimisePath_(OptimiseStats & stats, const Path & path);
|
||||
|
||||
// Internal versions that are not wrapped in retry_sqlite.
|
||||
bool isValidPath_(const Path & path);
|
||||
void queryReferrers_(const Path & path, PathSet & referrers);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -282,6 +282,7 @@ struct RestoreSink : ParseSink
|
||||
void createRegularFile(const Path & path)
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
fd.close();
|
||||
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
if (fd == -1) throw SysError(format("creating file `%1%'") % p);
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ static void processConnection(bool trusted)
|
||||
try {
|
||||
performOp(trusted, clientVersion, from, to, op);
|
||||
} catch (Error & e) {
|
||||
/* If we're not in a state were we can send replies, then
|
||||
/* 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
|
||||
@@ -731,6 +731,10 @@ static void processConnection(bool trusted)
|
||||
if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
|
||||
stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
|
||||
if (!errorAllowed) break;
|
||||
} catch (std::bad_alloc & e) {
|
||||
if (canSendStderr)
|
||||
stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
|
||||
throw;
|
||||
}
|
||||
|
||||
to.flush();
|
||||
@@ -893,7 +897,7 @@ static void daemonLoop()
|
||||
processConnection(trusted);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
writeToStderr("child error: " + string(e.what()) + "\n");
|
||||
writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -159,11 +159,11 @@ static void loadSourceExpr(EvalState & state, const Path & path, Value & v)
|
||||
}
|
||||
|
||||
/* The path is a directory. Put the Nix expressions in the
|
||||
directory in an attribute set, with the file name of each
|
||||
expression as the attribute name. Recurse into subdirectories
|
||||
(but keep the attribute set flat, not nested, to make it easier
|
||||
for a user to have a ~/.nix-defexpr directory that includes
|
||||
some system-wide directory). */
|
||||
directory in a set, with the file name of each expression as
|
||||
the attribute name. Recurse into subdirectories (but keep the
|
||||
set flat, not nested, to make it easier for a user to have a
|
||||
~/.nix-defexpr directory that includes some system-wide
|
||||
directory). */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
state.mkAttrs(v, 16);
|
||||
state.mkList(*state.allocAttr(v, state.symbols.create("_combineChannels")), 0);
|
||||
|
||||
@@ -17,5 +17,6 @@ in
|
||||
mkDerivation {
|
||||
name = "build-hook";
|
||||
builder = ./dependencies.builder0.sh;
|
||||
inherit input1 input2;
|
||||
input1 = " " + input1 + "/.";
|
||||
input2 = " ${input2}/.";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
[ "${input1: -2}" = /. ]
|
||||
[ "${input2: -2}" = /. ]
|
||||
|
||||
mkdir $out
|
||||
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ let {
|
||||
|
||||
input2 = mkDerivation {
|
||||
name = "dependencies-input-2";
|
||||
builder = ./dependencies.builder2.sh;
|
||||
builder = "${./dependencies.builder2.sh}";
|
||||
};
|
||||
|
||||
body = mkDerivation {
|
||||
name = "dependencies";
|
||||
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
||||
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
|
||||
input1 = input1 + "/.";
|
||||
inherit input2;
|
||||
input2 = "${input2}/.";
|
||||
meta.description = "Random test package";
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# This must fail to evaluate, since ./fnord doesn't exist. If it did
|
||||
# exist, it would produce "/nix/store/<hash>-fnord/xyzzy" (with an
|
||||
# appropriate context).
|
||||
"${./fnord}/xyzzy"
|
||||
@@ -0,0 +1 @@
|
||||
"${x: x}"
|
||||
@@ -0,0 +1 @@
|
||||
"${./fnord}"
|
||||
@@ -0,0 +1 @@
|
||||
''${x: x}''
|
||||
@@ -0,0 +1 @@
|
||||
[ true false true false true false true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
|
||||
@@ -0,0 +1,23 @@
|
||||
with builtins;
|
||||
|
||||
[ (isNull null)
|
||||
(isNull (x: x))
|
||||
(isFunction (x: x))
|
||||
(isFunction "fnord")
|
||||
(isString ("foo" + "bar"))
|
||||
(isString [ "x" ])
|
||||
(isInt (1 + 2))
|
||||
(isInt { x = 123; })
|
||||
(isBool (true && false))
|
||||
(isBool null)
|
||||
(typeOf (3 * 4))
|
||||
(typeOf true)
|
||||
(typeOf "xyzzy")
|
||||
(typeOf null)
|
||||
(typeOf { x = 456; })
|
||||
(typeOf [ 1 2 3 ])
|
||||
(typeOf (x: x))
|
||||
(typeOf ((x: y: x) 1))
|
||||
(typeOf map)
|
||||
(typeOf (map (x: x)))
|
||||
]
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{ system, nix }:
|
||||
|
||||
with import <nixos/lib/testing.nix> { inherit system; };
|
||||
with import <nixpkgs/nixos/lib/testing.nix> { inherit system; };
|
||||
|
||||
makeTest ({ pkgs, ... }: let pkgA = pkgs.aterm; pkgB = pkgs.wget; in {
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{ system, nix }:
|
||||
|
||||
with import <nixos/lib/testing.nix> { inherit system; };
|
||||
with import <nixpkgs/nixos/lib/testing.nix> { inherit system; };
|
||||
|
||||
makeTest ({ pkgs, ... }:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user