Compare commits

...
54 Commits
Author SHA1 Message Date
Eelco Dolstra 8d6418d46e Fix building without a garbage collector
http://hydra.nixos.org/build/6695350
2013-10-28 22:51:12 +01:00
Eelco Dolstra dec2f19502 Fix a segfault in genericClosure
It kept temporary data in STL containers that were not scanned by
Boehm GC, so Nix programs using genericClosure could randomly crash if
the garbage collector kicked in at a bad time.

Also make it a bit more efficient by copying points to values rather
than values.
2013-10-28 18:52:26 +01:00
Eelco Dolstra 6123144933 Drop Cygwin and Solaris builds 2013-10-28 11:56:37 +00:00
Eelco Dolstra 1dacd427cd Update release notes, set version for 1.6.1 release 2013-10-28 11:56:21 +00:00
Eelco Dolstra ea6bf0c21f Slightly optimize listToAttrs 2013-10-28 07:34:44 +01:00
Eelco Dolstra 36e67ff16b Undocument obsolete form of "let" 2013-10-24 22:06:39 +02:00
Eelco Dolstra fba17a9043 Doc fix 2013-10-24 22:05:58 +02:00
Eelco Dolstra 2d9bb56e55 Fix segfault on Darwin
Ever since SQLite in Nixpkgs was updated to 3.8.0.2, Nix has randomly
segfaulted on Darwin:

  http://hydra.nixos.org/build/6175515
  http://hydra.nixos.org/build/6611038

It turns out that this is because the binary cache substituter somehow
ends up loading two versions of SQLite: the one in Nixpkgs and the
other from /usr/lib/libsqlite3.dylib.  It's not exactly clear why the
latter is loaded, but it appears to be because WWW::Curl indirectly loads
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation,
which in turn seems to load /usr/lib/libsqlite3.dylib.  This leads to
a segfault when Perl exits:

  #0  0x00000001010375f4 in sqlite3_finalize ()
  #1  0x000000010125806e in sqlite_st_destroy ()
  #2  0x000000010124bc30 in XS_DBD__SQLite__st_DESTROY ()
  #3  0x00000001001c8155 in XS_DBI_dispatch ()
  ...
  #14 0x0000000100023224 in perl_destruct ()
  #15 0x0000000100000d6a in main ()
  ...

The workaround is to explicitly load DBD::SQLite before WWW::Curl.
2013-10-24 19:15:52 +02:00
Eelco Dolstra 5bc41d78ff Rename "attribute sets" to "sets"
We don't have any other kind of sets so calling them attribute sets is
unnecessarily verbose.
2013-10-24 16:41:04 +02:00
Eelco Dolstra 9e4bb20455 Manual: Fix broken URLs
Fixes #172.
2013-10-24 16:02:08 +02:00
Eelco Dolstra dc341811d6 Add rpm_fedora19i386 to the release-critical builds 2013-10-24 15:54:23 +02:00
Eelco Dolstra 69befd33a9 Remove unnecessary call to forceStringNoCtx 2013-10-24 03:08:34 +02:00
Eelco Dolstra a5684e09d3 Document typeOf 2013-10-24 02:56:00 +02:00
Eelco Dolstra 411a3461dc Add a test of the type primops 2013-10-24 02:51:28 +02:00
Eelco Dolstra 05d02f798f Add a typeOf primop
We already have some primops for determining the type of a value, such
as isString, but they're incomplete: for instance, there is no isPath.
Rather than adding more isBla functions, the generic typeOf function
returns a string representing the type of the argument (e.g. "int").
2013-10-24 02:49:13 +02:00
Eelco Dolstra 6da92d96ae Document NIX_SHOW_STATS and NIX_COUNT_CALLS 2013-10-24 02:22:24 +02:00
Eelco Dolstra 543d8a5942 Don't require NIX_SHOW_STATS for NIX_COUNT_CALLS 2013-10-24 02:20:54 +02:00
Eelco Dolstra fe95650487 Memoize evalFile() lookups under both the original and resolved name
Previously we only used the resolved name, causing repeated resolution
(e.g. /dir to /dir/default.nix).
2013-10-23 11:19:01 +00:00
Eelco Dolstra 3139481822 Add an aggregate job
Also, build for Ubuntu 13.10 and Fedora 19.
2013-10-23 11:52:25 +02:00
Eelco Dolstra c086183843 For auto roots, show the intermediate link
I.e. "nix-store -q --roots" will now show (for example)

  /home/eelco/Dev/nixpkgs/result

rather than

  /nix/var/nix/gcroots/auto/53222qsppi12s2hkap8dm2lg8xhhyk6v
2013-10-22 11:39:10 +02:00
Eelco Dolstra 4f4a14453a Don't set $PS1 in non-interactive shells
Shouldn't really matter, but you never know.
2013-10-18 14:51:25 +02:00
Shea LevyandEelco Dolstra 4ea034a5c5 nix-shell: Play nicely with non-interactive shells
nix-shell with the --command flag might be used non-interactively, but
if bash starts non-interactively (i.e. with stdin or stderr not a
terminal), it won't source the script given in --rcfile. However, in
that case it *will* source the script found in $BASH_ENV, so we can use
that instead.

Also, don't source ~/.bashrc in a non-interactive shell (detectable by
checking the PS1 env var)

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-10-18 14:34:46 +02:00
Eelco Dolstra 792fd51f41 Fold two stack trace messages in derivations
Combined with the previous changes, stack traces involving derivations
are now much less verbose, since something like

  while evaluating the builtin function `getAttr':
  while evaluating the builtin function `derivationStrict':
  while instantiating the derivation named `gtk+-2.24.20' at `/home/eelco/Dev/nixpkgs/pkgs/development/libraries/gtk+/2.x.nix:11:3':
  while evaluating the derivation attribute `propagatedNativeBuildInputs' at `/home/eelco/Dev/nixpkgs/pkgs/stdenv/generic/default.nix:78:17':
  while evaluating the attribute `outPath' at `/nix/store/212ngf4ph63mp6p1np2bapkfikpakfv7-nix-1.6/share/nix/corepkgs/derivation.nix:18:9':
  ...

now reads

  while evaluating the attribute `propagatedNativeBuildInputs' of the derivation `gtk+-2.24.20' at `/home/eelco/Dev/nixpkgs/pkgs/development/libraries/gtk+/2.x.nix:11:3':
  ...
2013-10-17 11:57:25 +02:00
Eelco Dolstra f440558acc Don't show <nix/derivation.nix> in stack traces
Messages like

  while evaluating the attribute `outPath' at `/nix/store/212ngf4ph63mp6p1np2bapkfikpakfv7-nix-1.6/share/nix/corepkgs/derivation.nix:18:9':

are redundant, because Nix already shows that it's evaluating a derivation:

  while instantiating the derivation named `firefox-24.0' at `/home/eelco/Dev/nixpkgs/pkgs/applications/networking/browsers/firefox/default.nix:131:5':
  while evaluating the derivation attribute `nativeBuildInputs' at `/home/eelco/Dev/nixpkgs/pkgs/stdenv/generic/default.nix:76:17':
2013-10-17 11:47:38 +02:00
Eelco Dolstra bb659bad81 Nix 1.6.1 release notes 2013-10-17 11:40:20 +02:00
Eelco Dolstra f6a8e7f4c2 Fix test 2013-10-17 11:18:37 +02:00
Eelco Dolstra b08f4b0da9 Test string semantics a bit more 2013-10-17 01:12:43 +02:00
goblinandEelco Dolstra d7625b5c2d two typos 2013-10-17 00:59:19 +02:00
Eelco Dolstra b8034e5581 Ensure proper type checking/coercion of "${expr}"
Now we only rewrite "${expr}" to expr if expr is a string literal.
2013-10-17 00:57:24 +02:00
Eelco Dolstra 9d8a80375d Add a test for type correctness of antiquotes
Antiquotes should evaluate to strings or paths.  This is usually
checked, except in the case where the antiquote makes up the entire
string, as in "${expr}".  This is optimised to expr, which discards
the runtime type checks / coercions.
2013-10-17 00:51:07 +02:00
Eelco Dolstra d6a7aa8f48 Revert the behaviour of antiquoted paths to pre-Nix 1.6
Commit 159e621d1a accidentally changed
the behaviour of antiquoted paths, e.g.

  "${/foo}/bar"

used to evaluate to "/nix/store/<hash>-foo/bar" (where /foo gets
copied to the store), but in Nix 1.6 it evaluates to "/foo/bar".  This
is inconsistent, since

  " ${/foo}/bar"

evaluates to " /nix/store/<hash>-foo/bar".  So revert to the old
behaviour.
2013-10-17 00:39:59 +02:00
Eelco Dolstra b8571d68c4 Add a regression test for correct path antiquotation behavior
This broke in Nix 1.6.
2013-10-16 23:29:11 +02:00
Eelco Dolstra a737f51fd9 Retry all SQLite operations
To deal with SQLITE_PROTOCOL, we also need to retry read-only
operations.
2013-10-16 15:58:20 +02:00
Eelco Dolstra ff02f5336c Fix a race in registerFailedPath()
Registering the path as failed can fail if another process does the
same thing after the call to hasPathFailed().  This is extremely
unlikely though.
2013-10-16 14:55:53 +02:00
Eelco Dolstra 4bd5282573 Convenience macros for retrying a SQLite transaction 2013-10-16 14:46:35 +02:00
Eelco Dolstra bce14d0f61 Don't wrap read-only queries in a transaction
There is no risk of getting an inconsistent result here: if the ID
returned by queryValidPathId() is deleted from the database
concurrently, subsequent queries involving that ID will simply fail
(since IDs are never reused).
2013-10-16 14:36:53 +02:00
Eelco Dolstra 7cdefdbe73 Print a distinct warning for SQLITE_PROTOCOL 2013-10-16 14:27:36 +02:00
Eelco Dolstra d05bf04444 Treat SQLITE_PROTOCOL as SQLITE_BUSY
In the Hydra build farm we fairly regularly get SQLITE_PROTOCOL errors
(e.g., "querying path in database: locking protocol").  The docs for
this error code say that it "is returned if some other process is
messing with file locks and has violated the file locking protocol
that SQLite uses on its rollback journal files."  However, the SQLite
source code reveals that this error can also occur under high load:

  if( cnt>5 ){
    int nDelay = 1;                      /* Pause time in microseconds */
    if( cnt>100 ){
      VVA_ONLY( pWal->lockError = 1; )
      return SQLITE_PROTOCOL;
    }
    if( cnt>=10 ) nDelay = (cnt-9)*238;  /* Max delay 21ms. Total delay 996ms */
    sqlite3OsSleep(pWal->pVfs, nDelay);
  }

i.e. if certain locks cannot be not acquired, SQLite will retry a
number of times before giving up and returing SQLITE_PROTOCOL.  The
comments say:

  Circumstances that cause a RETRY should only last for the briefest
  instances of time.  No I/O or other system calls are done while the
  locks are held, so the locks should not be held for very long. But
  if we are unlucky, another process that is holding a lock might get
  paged out or take a page-fault that is time-consuming to resolve,
  during the few nanoseconds that it is holding the lock.  In that case,
  it might take longer than normal for the lock to free.
  ...
  The total delay time before giving up is less than 1 second.

On a heavily loaded machine like lucifer (the main Hydra server),
which often has dozens of processes waiting for I/O, it seems to me
that a page fault could easily take more than a second to resolve.
So, let's treat SQLITE_PROTOCOL as SQLITE_BUSY and retry the
transaction.

Issue NixOS/hydra#14.
2013-10-16 14:19:59 +02:00
Eelco Dolstra c1994fecf9 nix-shell: Fix bash completion
Nixpkgs's stdenv setup script sets the "nullglob" option, but doing so
breaks Bash completion on NixOS (when ‘programs.bash.enableCompletion’
is set) and on Ubuntu.  So clear that flag afterwards.  Of course,
this may break stdenv functions in subtle ways...
2013-10-14 15:28:43 +02:00
Eelco Dolstra 672c3acc71 Adjust to the NixOS/Nixpkgs merge 2013-10-11 10:57:23 +02:00
Eelco Dolstra 7bdb85453d printStats(): Print the size of the symbol table in bytes 2013-10-08 15:37:08 +02:00
Eelco Dolstra 9deb822180 Deduplicate filenames in Pos
This saves ~4 MiB of RAM for NixOS system instantiation, and ~18 MiB
for "nix-env -qa".
2013-10-08 15:36:10 +02:00
Eelco Dolstra b1e3b1a4ac Treat undefined variable errors consistently
Previously, a undefined variable inside a "with" caused an EvalError
(which can be caught), while outside, it caused a ParseError (which
cannot be caught).  Now both cause an UndefinedVarError (which cannot
be caught).
2013-10-08 14:45:36 +02:00
Eelco Dolstra 6b47de580f Show the exact position of undefined variables
In particular, undefined variable errors in a "with" previously didn't
show *any* position information, so this should help a lot in those
cases.
2013-10-08 14:40:51 +02:00
Eelco Dolstra a5e0f64db3 Remove some unused functions 2013-10-08 12:30:23 +00:00
Eelco Dolstra 221a2daf34 Merge VarRef into ExprVar 2013-10-08 14:24:53 +02:00
Eelco Dolstra 176c666f36 Don't show calls to primops in stack traces
Since they don't have location information, they just give you crap
like:

  while evaluating the builtin function `getAttr':
  while evaluating the builtin function `derivationStrict':
  ...
2013-10-07 18:02:32 +02:00
Eelco Dolstra c945f015de Fix segfault in nix-repl / hydra-eval-jobs
If a "with" attribute set fails to evaluate, we have to make sure its
Env record remains unchanged.  Otherwise, repeated evaluation gives a
segfault:

  nix-repl> :a with 0; { a = x; b = x; }
  Added 2 variables.

  nix-repl> a
  error: value is an integer while an attribute set was expected

  nix-repl> b
  Segmentation fault
2013-10-02 15:24:45 +02:00
Eelco Dolstra 28e0742966 Report OOM errors better 2013-10-02 14:34:36 +02:00
Eelco Dolstra a5fb4b5b7c Fix typo 2013-10-02 14:22:49 +02:00
Eelco Dolstra faaae44f2e build-remote.pl: Don't use substituters on the remote
It's kinda pointless to check substituters on the remote side, since
we just checked them locally.
2013-09-18 14:04:03 +02:00
Eelco Dolstra f53574ebd6 RestoreSink: Slightly reduce the number of concurrent FDs 2013-09-17 12:06:59 +00:00
Eelco Dolstra d5529f5b85 Version was called 1.6, not 1.6.0 2013-09-10 17:48:11 +02:00
Eelco Dolstra b072fc04a7 Bump version number 2013-09-10 17:41:49 +02:00
40 changed files with 713 additions and 487 deletions
+55 -42
View File
@@ -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 doesnt 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 doesnt exist or contains an incorrect Nix
expression. <function>import</function> implements Nixs 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 doesnt exist or contains an incorrect Nix expression.
<function>import</function> implements Nixs 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 dont have to
exist in <replaceable>attrs</replaceable>. For instance,
<replaceable>list</replaceable> from
<replaceable>set</replaceable>. The attributes dont 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>
+18
View File
@@ -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
+2 -2
View File
@@ -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
+5 -6
View File
@@ -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
+67 -1
View File
@@ -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>
+56 -62
View File
@@ -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
doesnt 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>
+1
View File
@@ -2,6 +2,7 @@ package Nix::Manifest;
use strict;
use DBI;
use DBD::SQLite;
use Cwd;
use File::stat;
use File::Path;
+37 -5
View File
@@ -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
];
};
};
+1 -1
View File
@@ -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
View File
@@ -1,6 +1,7 @@
#! @perl@ -w @perlFlags@
use DBI;
use DBD::SQLite;
use File::Basename;
use IO::Select;
use Nix::Config;
+5 -2
View File
@@ -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;
}
+3 -4
View File
@@ -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));
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+6 -5
View File
@@ -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;
};
}
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+4
View File
@@ -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);
};
+1
View File
@@ -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);
}
+6 -2
View File
@@ -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);
}
+5 -5
View File
@@ -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);
+2 -1
View File
@@ -17,5 +17,6 @@ in
mkDerivation {
name = "build-hook";
builder = ./dependencies.builder0.sh;
inherit input1 input2;
input1 = " " + input1 + "/.";
input2 = " ${input2}/.";
}
+3
View File
@@ -1,3 +1,6 @@
[ "${input1: -2}" = /. ]
[ "${input2: -2}" = /. ]
mkdir $out
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
+3 -3
View File
@@ -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";
};
+4
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
"${x: x}"
+1
View File
@@ -0,0 +1 @@
"${./fnord}"
+1
View File
@@ -0,0 +1 @@
''${x: x}''
+1
View File
@@ -0,0 +1 @@
[ true false true false true false true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
+23
View File
@@ -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)))
]
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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, ... }:
+1 -1
View File
@@ -1 +1 @@
1.6
1.6.1