Compare commits

...
261 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
Eelco Dolstra fecad91b67 Update release notes 2013-09-10 11:21:30 +02:00
Eelco Dolstra 0220da3e10 Remove stray debug line 2013-09-06 17:20:19 +02:00
Eelco Dolstra 936f9d45ba Don't apply the CPU affinity hack to nix-shell (and other Perl programs)
As discovered by Todd Veldhuizen, the shell started by nix-shell has
its affinity set to a single CPU.  This is because nix-shell connects
to the Nix daemon, which causes the affinity hack to be applied.  So
we turn this off for Perl programs.
2013-09-06 16:36:56 +02:00
Domen KožarandEelco Dolstra 4b83830d0c typo 2013-09-06 15:18:07 +02:00
Eelco Dolstra 5904262640 nix-shell: Support a .drv as argument
Fixes #161.
2013-09-06 14:58:05 +02:00
Eelco Dolstra 2c1ecf8e81 nix-env -i: Add a flag ‘--remove-all’ / ‘-r’
This is equivalent to running ‘nix-env -e '*'’ first, except that it
happens in a single transaction.  Thus, ‘nix-env -i pkgs...’ replaces
the profile with the specified set of packages.

The main motivation is to support declarative package management
(similar to environment.systemPackages in NixOS).  That is, if you
have a specification ‘profile.nix’ like this:

  with import <nixpkgs> {};
  [ thunderbird
    geeqie
    ...
  ]

then after any change to ‘profile.nix’, you can run:

  $ nix-env -f profile.nix -ir

to update the profile to match the specification.  (Without the ‘-r’
flag, if you remove a package from ‘profile.nix’, it won't be removed
from the actual profile.)

Suggested by @zefhemel.
2013-09-03 21:21:14 +02:00
Eelco Dolstra 88c07341a6 nix-env: Use wildcard match by default
That is, you don't need to pass '*' anymore, so

  nix-env -qa

is equivalent to

  nix-env -qa '*'
2013-09-03 16:35:49 +02:00
Eelco Dolstra 07a08bddf0 nix-env: Load files in ~/.nix-defexpr on demand
So if you do "nix-env -qa -A nixos", then other channels won't be
parsed/evaluated at all.
2013-09-03 15:45:32 +02:00
Eelco Dolstra c57ed84e28 Check for name collisions in the input Nix expressions 2013-09-03 15:25:51 +02:00
Eelco Dolstra ef4f5ba85e Work on Values instead of Exprs
This prevents some duplicate evaluation in nix-env and
nix-instantiate.

Also, when traversing ~/.nix-defexpr, only read regular files with the
extension .nix.  Previously it was reading files like
.../channels/binary-caches/<name>.  The only reason this didn't cause
problems is pure luck (namely, <name> shadows an actual Nix
expression, the binary-caches files happen to be syntactically valid
Nix expressions, and we iterate over the directory contents in just
the right order).
2013-09-03 13:17:51 +00:00
Eelco Dolstra 06bb2d95b4 Reformat 2013-09-03 11:04:21 +00:00
Eelco Dolstra 6f809194d7 Get rid of the parse tree cache
Since we already cache files in normal form (fileEvalCache), caching
parse trees is redundant.

Note that getting rid of this cache doesn't actually save much memory
at the moment, because parse trees are currently not freed / GC'ed.
2013-09-03 13:01:42 +02:00
Eelco Dolstra 57d18df7d0 Add some support code for nix-repl 2013-09-02 18:34:04 +02:00
Eelco Dolstra 92077b4547 Get rid of a signedness warning 2013-09-02 16:39:17 +02:00
Eelco Dolstra 33972629d7 Fix whitespace 2013-09-02 16:29:15 +02:00
Eelco Dolstra ac1b754138 Lower xz compression level
Fixes #84.
2013-09-02 13:32:51 +02:00
Eelco Dolstra c28dfc0305 Manual: Make '' a bit clearer
Issue #162.
2013-09-02 13:24:29 +02:00
Eelco Dolstra e9b92169a5 Fix manual error
Reported by Matija Šuklje.

Fixes #163.
2013-09-02 13:19:34 +02:00
Eelco Dolstra ecd830b3b9 Update the release notes 2013-09-02 13:11:36 +02:00
Eelco Dolstra 6ec8dab06a Adda test for build-max-log-size 2013-09-02 12:44:30 +02:00
Eelco Dolstra b29d3f4aee Only show trace messages when tracing is enabled 2013-09-02 12:01:04 +02:00
Eelco Dolstra efe4289464 Add an option to limit the log output of builders
This is mostly useful for Hydra to deal with builders that get stuck
in an infinite loop writing data to stdout/stderr.
2013-09-02 11:58:18 +02:00
Shea LevyandEelco Dolstra afc6c1bad6 Simplify inherited attribute handling
This reduces the difference between inherited and non-inherited
attribute handling to the choice of which env to use (in recs and lets)
by setting the AttrDef::e to a new ExprVar in the parser rather than
carrying a separate AttrDef::v VarRef member.

As an added bonus, this allows inherited attributes that inherit from a
with to delay forcing evaluation of the with's attributes.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-08-26 11:31:56 +02:00
Ivan KozikandEelco Dolstra 6cd6ce5608 Fix nix-shell command 2013-08-26 11:15:22 +02:00
Ivan KozikandEelco Dolstra 34bb806f74 Fix typos, especially those that end up in the Nix manual 2013-08-26 11:15:22 +02:00
Gergely RiskoandEelco Dolstra c6c024ca6f Fix personality switching from x86_64 to i686
On Linux, Nix can build i686 packages even on x86_64 systems.  It's not
enough to recognize this situation by settings.thisSystem, we also have
to consult uname().  E.g. we can be running on a i686 Debian with an
amd64 kernel.  In that situation settings.thisSystem is i686-linux, but
we still need to change personality to i686 to make builds consistent.
2013-08-26 11:12:35 +02:00
Eelco Dolstra 03eaef3d7a Manual: Don't use actual hashes of Nix dependencies
These cause an unnecessary runtime dependency :-)
2013-08-23 10:12:20 +02:00
Eelco Dolstra b3110a15e9 Fix corrupt PNG
Libpng used to accept this, but no longer does.
2013-08-21 12:53:51 +02:00
Eelco Dolstra 25a108bb9c Hack to clean up tests/test-tmp 2013-08-21 12:53:46 +02:00
Eelco Dolstra d308aeaf53 Store Nix integers as longs
So on 64-bit systems, integers are now 64-bit.

Fixes #158.
2013-08-19 12:35:03 +02:00
Eelco Dolstra 297b762513 Turn on -Wall 2013-08-19 11:41:15 +02:00
Eelco Dolstra 46222bbc43 Typo 2013-08-14 22:32:49 +02:00
Shea LevyandEelco Dolstra 58204a3c39 corepkgs/nar.nix: Prefer local builds
nar.nix's builder depends on coreutils and nix itself being in $PATH.
Unfortunately, there's no good way to ensure that these packages exist
in the same place on the remote machine: The local machine may have nix
installed in /usr, and the remote machine in /usr/local, but the
generated nar.sh builder will refer to /usr and thus fail on the remote
machine. This ensures that nar.sh is run on the same machine that
instantiates it.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-08-14 22:32:41 +02:00
Eelco Dolstra 3fb7ae0586 Respect MINSIGSTKSZ when allocating an alternative stack
http://hydra.nixos.org/build/5663577
2013-08-07 15:44:19 +00:00
Eelco Dolstra 161a2ccf7a Fix build on non-Linux
http://hydra.nixos.org/build/5662914
2013-08-07 17:26:41 +02:00
Eelco Dolstra a583a2bc59 Run the daemon worker on the same CPU as the client
On a system with multiple CPUs, running Nix operations through the
daemon is significantly slower than "direct" mode:

$ NIX_REMOTE= nix-instantiate '<nixos>' -A system
real    0m0.974s
user    0m0.875s
sys     0m0.088s

$ NIX_REMOTE=daemon nix-instantiate '<nixos>' -A system
real    0m2.118s
user    0m1.463s
sys     0m0.218s

The main reason seems to be that the client and the worker get moved
to a different CPU after every call to the worker.  This patch adds a
hack to lock them to the same CPU.  With this, the overhead of going
through the daemon is very small:

$ NIX_REMOTE=daemon nix-instantiate '<nixos>' -A system
real    0m1.074s
user    0m0.809s
sys     0m0.098s
2013-08-07 14:02:04 +02:00
Eelco Dolstra 263d668222 Set the default GCC optimisation level to -O3 2013-08-06 14:21:46 +02:00
Eelco Dolstra fd7d979c79 Remove obsolete reference to ATerms 2013-08-06 14:15:11 +02:00
Eelco Dolstra 46ffcce0c3 In the profiler output, show function names (if available) 2013-08-02 18:29:23 +00:00
Eelco Dolstra 8e74c0bfd1 Let the ordering operators also work on strings
E.g. ‘"foo" < "bar"’ now works.
2013-08-02 18:53:02 +02:00
Eelco Dolstra 3d77b28eac Add comparison operators ‘<’, ‘<=’, ‘>’ and ‘>=’ 2013-08-02 18:39:40 +02:00
Eelco Dolstra 47701677e8 Add integer ‘-’, ‘*’ and ‘/’ operators 2013-08-02 16:03:02 +00:00
Eelco Dolstra 5d147e125c Add a unary integer negation operator
This allows saying "-1" instead of "builtins.sub 0 1".
2013-08-02 15:43:31 +00:00
Eelco Dolstra 159e621d1a Overload the ‘+’ operator to support integer addition 2013-08-02 15:21:17 +00:00
Eelco Dolstra 511455965e nix-shell: Don't disable Automake dependency tracking
Nixpkgs' stdenv disables dependency tracking by default.  That makes
sense for one-time builds, but in an interactive environment we expect
repeated "make" invocations to do the right thing.
2013-07-31 13:17:50 +02:00
Eelco Dolstra 7df4ef983e Test the delayed with a bit more 2013-07-31 13:12:35 +02:00
Eelco Dolstra 0a470fc345 Make Env smaller
Commit 20866a7031 added a ‘withAttrs’
field to Env, which is annoying because it makes every Env structure
bigger and we allocate millions of them.  E.g. NixOS evaluation took
18 MiB more.  So this commit squeezes ‘withAttrs’ into values[0].
Probably should use a union...
2013-07-31 12:44:21 +02:00
Eelco Dolstra 8ae6d55db1 Don't use NULL 2013-07-31 12:11:14 +02:00
Shea LevyandEelco Dolstra e068f49f7d Avoid thunks when a fromWith var can be looked up without evaluation
Signed-off-by: Shea Levy <shea@shealevy.com>
2013-07-31 11:48:39 +02:00
Shea LevyandEelco Dolstra 20866a7031 Delay evaulation of with attrs until a variable lookup needs them
Evaluation of attribute sets is strict in the attribute names, which
means immediate evaluation of `with` attribute sets rules out some
potentially interesting use cases (e.g. where the attribute names of one
set depend in some way on another but we want to bring those names into
scope for some values in the second set).

The major example of this is overridable self-referential package sets
(e.g. all-packages.nix). With immediate `with` evaluation, the only
options for such sets are to either make them non-recursive and
explicitly use the name of the overridden set in non-overridden one
every time you want to reference another package, or make the set
recursive and use the `__overrides` hack. As shown in the test case that
comes with this commit, though, delayed `with` evaluation allows a nicer
third alternative.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-07-31 11:48:39 +02:00
Eelco Dolstra 70e68e0ec6 Detect stack overflows
Previously, if the Nix evaluator gets a stack overflow due to a deep
or infinite recursion in the Nix expression, the user gets an
unhelpful message ("Segmentation fault") that doesn't indicate that
the problem is in the user's code rather than Nix itself.  Now it
prints:

  error: stack overflow (possible infinite recursion)

This only works on x86_64-linux and i686-linux.

Fixes #35.
2013-07-30 23:37:10 +02:00
Shea LevyandEelco Dolstra e87d1a63bd killUser: Don't let the child kill itself on Apple
The kill(2) in Apple's libc follows POSIX semantics, which means that
kill(-1, SIGKILL) will kill the calling process too. Since nix has no
way to distinguish between the process successfully killing everything
and the process being killed by a rogue builder in that case, it can't
safely conclude that killUser was successful.

Luckily, the actual kill syscall takes a parameter that determines
whether POSIX semantics are followed, so we can call that syscall
directly and avoid the issue on Apple.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-07-30 21:25:16 +02:00
Florian FriesdorfandEelco Dolstra 7cf539c728 buildenv: remove special treatment of python files
buildPythonPackage does not leave easy_install.pth and site.py
anymore. A python package that leaves these files is broken. An
exception to this is setuptoolsSite which packages setuptools'
site.py. To include it into a buildenv, this patch is even needed, not
just cosmetic.
2013-07-23 20:13:43 +02:00
Eelco Dolstra 7b09e9f2c4 Add hacking notes plus a script for running nix-shell 2013-07-19 16:51:31 +02:00
Eelco Dolstra 15e5ac8039 nix-shell: Set $IN_NIX_SHELL
This allows scripts to distinguish between a real build and a Nix
shell.
2013-07-19 14:32:05 +02:00
Eelco Dolstra 48858ad5ca nix-shell: Set some environment variables also set by build.cc
Setting $NIX_STORE causes the purity checks in gcc/ld-wrapper to kick
in, so that's why we unset $NIX_ENFORCE_PURITY.
2013-07-19 14:06:58 +02:00
Eelco Dolstra 2bc5de8635 Rename ‘nix-build --run-env’ to ‘nix-shell’ 2013-07-19 12:02:44 +02:00
Eelco Dolstra dc5f2e7da6 nix-build --run-env: Add a ‘--pure’ flag
This causes the environment to be (almost) cleared, thus giving a
shell that more closely resembled the actual Nix derivation.
2013-07-19 11:23:32 +02:00
Eelco Dolstra a4921b8ceb Revert "build-remote.pl: Enforce timeouts locally"
This reverts commit 69b8f9980f.

The timeout should be enforced remotely.  Otherwise, if the garbage
collector is running either locally or remotely, if will block the
build or closure copying for some time.  If the garbage collector
takes too long, the build may time out, which is not what we want.
Also, on heavily loaded systems, copying large paths to and from the
remote machine can take a long time, also potentially resulting in a
timeout.
2013-07-18 12:52:29 +02:00
Shea LevyandEelco Dolstra 16591eb3cc Allow bind-mounting regular files into the chroot
mount(2) with MS_BIND allows mounting a regular file on top of a regular
file, so there's no reason to only bind directories. This allows finer
control over just which files are and aren't included in the chroot
without having to build symlink trees or the like.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-07-15 16:01:33 +02:00
Eelco Dolstra c3f5413e80 Fix syntax error in unpack-channel 2013-07-12 14:06:05 +02:00
Eelco Dolstra aeb810b01e Garbage collector: Don't follow symlinks arbitrarily
Only indirect roots (symlinks to symlinks to the Nix store) are now
supported.
2013-07-12 14:03:36 +02:00
Gergely RiskoandEelco Dolstra 25a00cae5b Add gzip support for channel unpacking 2013-07-12 11:29:37 +02:00
Domen KožarandEelco Dolstra 620d57f036 doc: typo 2013-07-12 11:27:43 +02:00
Eelco Dolstra 6fcc290619 build-remote.pl: Move "building ..." message to a better place 2013-07-11 17:16:45 +02:00
Eelco Dolstra ed09d0c0a4 nix-build --run-env: Always use Bash
Fixes #113.
Fixes #131.
2013-07-11 14:40:29 +02:00
Eelco Dolstra 656390062a nix-build --run-env: Source $stdenv/setup in the interactive shell
This ensures that not just environment variables are set, but also
shell functions such as unpackPhase, configurePhase and so on.
2013-07-11 14:32:22 +02:00
Ludovic CourtèsandEelco Dolstra 212e96f39c Leave `HAVE_HUP_NOTIFICATION' undefined on GNU/Hurd. 2013-07-07 13:21:06 +02:00
Eelco Dolstra b584a42e3d Manual: auto-optimise-store is disabled by default 2013-07-03 18:35:59 +02:00
Eelco Dolstra 5116214343 Add support for uncompressed NARs in binary caches
Issue NixOS/hydra#102.
2013-07-01 21:03:14 +02:00
Eelco Dolstra 7986711632 copy-from-other-stores.pl: Respect $NIX_BIN_DIR 2013-07-01 13:30:28 +02:00
Eelco Dolstra 1917d750a0 copy-from-other-stores.pl: Report downloaded size as 0 2013-07-01 13:29:59 +02:00
Eelco Dolstra 7ccd946407 Don't set $preferLocalBuild and $requiredSystemFeatures in builders
With C++ std::map, doing a comparison like ‘map["foo"] == ...’ has the
side-effect of adding a mapping from "foo" to the empty string if
"foo" doesn't exist in the map.  So we ended up setting some
environment variables by accident.
2013-06-20 18:07:27 +00:00
Eelco Dolstra 5558652709 Don't substitute derivations that have preferLocalBuild set
In particular this means that "trivial" derivations such as writeText
are not substituted, reducing the number of GET requests to the binary
cache by about 200 on a typical NixOS configuration.
2013-06-20 19:26:31 +02:00
Eelco Dolstra 1906cce6fc Increase SQLite's auto-checkpoint interval
Common operations like instantiating a NixOS system config no longer
fitted in 8192 pages, leading to more fsyncs.  So increase this limit.
2013-06-20 14:01:33 +00:00
Eelco Dolstra 9b11165aec Disable the copy-from-other-stores substituter
This substituter basically cannot work reliably since we switched to
SQLite, since SQLite databases may need write access to open them even
just for reading (and in WAL mode they always do).
2013-06-20 12:01:33 +02:00
Eelco Dolstra 22144afa8d Don't keep "disabled" substituters running
For instance, it's pointless to keep copy-from-other-stores running if
there are no other stores, or download-using-manifests if there are no
manifests.  This also speeds things up because we don't send queries
to those substituters.
2013-06-20 11:55:15 +02:00
Eelco Dolstra 2b29e4b852 Bump version 2013-06-17 15:39:45 +02:00
Eelco Dolstra 6016bcd30e Update release notes for Nix 1.5.3 2013-06-17 11:47:38 +02:00
Eelco Dolstra 1b6ee8f4c7 Allow hard links between the outputs of a derivation 2013-06-13 17:29:56 +02:00
Eelco Dolstra cd49ee0897 Fix a security bug in hash rewriting
Before calling dumpPath(), we have to make sure the files are owned by
the build user.  Otherwise, the build could contain a hard link to
(say) /etc/shadow, which would then be read by the daemon and
rewritten as a world-readable file.

This only affects systems that don't have hard link restrictions
enabled.
2013-06-13 17:12:24 +02:00
Eelco Dolstra 1e2c7c04b1 Fix assertion failure in canonicalisePathMetaData() after hash rewriting
The assertion in canonicalisePathMetaData() failed because the
ownership of the path already changed due to the hash rewriting.  The
solution is not to check the ownership of rewritten paths.

Issue #122.
2013-06-13 17:12:06 +02:00
Eelco Dolstra 6cc2a8f8ed computeFSClosure: Only process the missing/corrupt paths
Issue #122.
2013-06-13 16:43:20 +02:00
Eelco Dolstra bfee9a2581 Typo 2013-06-13 14:51:11 +02:00
Eelco Dolstra f9ff67e948 In repair mode, update the hash of rebuilt paths
Otherwise subsequent invocations of "--repair" will keep rebuilding
the path.  This only happens if the path content differs between
builds (e.g. due to timestamps).
2013-06-13 14:46:07 +02:00
Eelco Dolstra 6b05f688ee nix-daemon: Trust options like binary-caches when the client is root
Fixes #127.
2013-06-12 12:10:26 +02:00
Eelco Dolstra 5c06e5297d download-from-binary-cache.pl: Respect $NIX_CONNECT_TIMEOUT 2013-06-07 16:25:12 +02:00
Eelco Dolstra 24a356bf71 Replace $NIX_DEBUG_SUBST with an option ‘debug-subst’
Thus passing ‘--option debug-subst 1’ allows daemon users to turn on
debug info and see what the substituter is doing.
2013-06-07 15:36:36 +02:00
Eelco Dolstra 24e063efdc download-from-binary-cache.pl: Show if we're waiting for a URL
Previously, if a binary cache is hanging/unreachable/slow,
download-from-binary-cache.pl would also hang without any indication
to the user.  Now, if fetching a URL takes more than 5 seconds, it
will print a message to that effect.
2013-06-07 15:33:44 +02:00
Eelco Dolstra ca70fba0bf Remove obsolete EOF checks 2013-06-07 15:10:23 +02:00
Eelco Dolstra 5959c591a0 Process stderr from substituters while doing have/info queries 2013-06-07 15:02:14 +02:00
Eelco Dolstra c5f9d0d080 Buffer reads from the substituter
This greatly reduces the number of system calls.
2013-06-07 14:00:23 +02:00
Eelco Dolstra 75e12b8e66 download-from-binary-cache.pl: Fix race condition
Fixes the error "DBD::SQLite::db do failed: column url is not unique".
2013-06-05 16:25:07 +02:00
Eelco Dolstra f0576d6775 Update the default binary cache URL to cache.nixos.org 2013-06-05 13:36:43 +02:00
Eelco Dolstra ff08306746 download-from-binary-cache.pl: Treat a 403 error as a 404
Amazon S3 returns HTTP status code 403 if a file doesn't exist and the
user has no permission to list the contents of the bucket.  So treat
it as 404 (meaning it's cached in the NARExistence table).
2013-06-04 15:20:37 +02:00
Eelco Dolstra bc2e43f3c8 build-remote.pl: Remove meaningless signing when importing the output paths
The "$UID != 0" makes no sense: if the local side has write access to
the Nix store (which is always the case) then it doesn't matter if
we're root - we can import unsigned paths either way.
2013-05-29 18:18:59 +02:00
Eelco Dolstra 039d5a023f .bashrc -> .profile 2013-05-29 12:36:56 +02:00
Eelco Dolstra b09b87321c nix-store --export: Export paths in topologically sorted order
Fixes #118.
2013-05-23 14:55:36 -04:00
Eelco Dolstra 107505e13a build-remote.pl: Copy all outputs in one operation 2013-05-23 14:39:58 -04:00
Eelco Dolstra 72d8209548 build-remote.pl: Indicate if remote machine is refusing builds
Fixes #120.
2013-05-23 14:04:46 -04:00
Eelco Dolstra 18a48d80a0 Show function names in error messages
Functions in Nix are anonymous, but if they're assigned to a
variable/attribute, we can use the variable/attribute name in error
messages, e.g.

while evaluating `concatMapStrings' at `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/strings.nix:18:25':
...
2013-05-16 19:08:02 +02:00
Eelco Dolstra 1b3a03f161 Show which function argument was unexpected
Fixes #116.
2013-05-16 17:56:14 +02:00
Eelco Dolstra 229567293c Shut up a compiler warning 2013-05-16 17:48:19 +02:00
Eelco Dolstra a4cb62ac25 download-from-binary-cache.pl: Get rid of an uninitialized value warning
Reported by Pablo Costa.
2013-05-15 15:47:05 +02:00
Eelco Dolstra 31a551a60f Bump version 2013-05-13 23:50:31 +02:00
Eelco Dolstra 6e85d1b5ba Bump release date 2013-05-13 16:52:08 +02:00
Eelco Dolstra 3a0cc43ac8 build-remote.pl: Properly close the SSH connection between attempts 2013-05-10 02:38:50 +02:00
Eelco Dolstra be0b9dda31 build-remote.pl: Pass /dev/null as SSH's stdin
Otherwise it will set the parent's stdin to non-blocking mode, causing
the subsequent read of the set of inputs/outputs to fail randomly.
That's insane.
2013-05-10 02:32:13 +02:00
Eelco Dolstra 78206f06ec build-remote.pl: Allow a machine to refuse a build
Before selecting a machine, build-remote.pl will try to run the
command "nix-builds-inhibited" on the machine.  If this command exists
and returns a 0 exit code, then the machine won't be used.  It's up to
the user to provide this command, but it would typically be a script
that checks whether there is enough disk space and whether the load is
not too high.
2013-05-10 01:09:46 +02:00
Eelco Dolstra 2ee9da9e22 In trace messages, don't print the output path
This doesn't work if there is no output named "out".  Hydra didn't use
it anyway.
2013-05-10 00:24:33 +02:00
Eelco Dolstra 6eba05613a Communicate build timeouts to Hydra 2013-05-09 18:39:04 +02:00
Eelco Dolstra 7a03cbf09d build-remote.pl: Create one process fewer on the remote side 2013-05-09 17:30:07 +02:00
Eelco Dolstra 69b8f9980f build-remote.pl: Enforce timeouts locally
Don't pass --timeout / --max-silent-time to the remote builder.
Instead, let the local Nix process terminate the build if it exceeds a
timeout.  The remote builder will be killed as a side-effect.  This
gives better error reporting (since the timeout message from the
remote side wasn't properly propagated) and handles non-Nix problems
like SSH hangs.
2013-05-09 17:17:17 +02:00
Eelco Dolstra e93acab852 Build Fedora 18 RPMs 2013-05-08 14:41:35 +02:00
Eelco Dolstra 806970349b Update release date 2013-05-07 15:46:25 +02:00
Eelco Dolstra ea019e9a26 Add option ‘extra-binary-caches’
This allows providing additional binary caches, useful in scripts like
Hydra's build reproduction scripts, in particular because untrusted
caches are ignored.
2013-05-07 15:37:28 +02:00
Eelco Dolstra cc837e2458 Build Debian 7.0 debs 2013-05-07 11:21:30 +02:00
Eelco Dolstra 28034bfa49 Build Ubuntu 13.04 debs 2013-05-03 14:14:46 +02:00
Eelco Dolstra 93f4fa8a15 Update release notes 2013-05-03 11:28:32 +02:00
Eelco Dolstra c51b6a893c nix-copy-closure: Show a proper error message if no host name is given 2013-05-03 11:12:11 +02:00
Lluís Batlle i RossellandEelco Dolstra e6c44d166a Fixing the pv position regarding compression
Problem noticed by niksnut.
2013-05-03 11:08:51 +02:00
Lluís Batlle i RossellandEelco Dolstra 7391533ea5 Fixing the pv reference; I didn't mean to change it 2013-05-03 11:08:51 +02:00
Lluís Batlle i RossellandEelco Dolstra 5cc2fc46ec Adding ETA support to the --show-progress in nix-copy-closure
Based on https://github.com/NixOS/nix/pull/6 from shlevy
2013-05-03 11:08:51 +02:00
Eelco Dolstra 3628b61ce0 Nix 1.5.2 release notes 2013-05-01 13:31:33 +02:00
Eelco Dolstra 470553bd05 Don't let stderr writes in substituters cause a deadlock 2013-05-01 13:21:39 +02:00
Eelco Dolstra 4ddd077bfa find-runtime-roots.pl: Don't hardcode /nix/store 2013-04-26 12:15:54 +02:00
Eelco Dolstra 0374d94437 addAdditionalRoots(): Check each path only once 2013-04-26 12:07:25 +02:00
Eelco Dolstra 00f698eb8b find-runtime-roots.pl: Search process environments for roots
For instance, this prevents paths from being deleted that are in use
by a "nix-build --run-env" session.
2013-04-26 12:06:39 +02:00
Eelco Dolstra 938092a213 find-runtime-roots.pl: Use Nix::Utils::readFile 2013-04-26 11:44:19 +02:00
Eelco Dolstra 772b70952f Fix --timeout
I'm not sure if it has ever worked correctly.  The line "lastWait =
after;" seems to mean that the timer was reset every time a build
produced log output.

Note that the timeout is now per build, as documented ("the maximum
number of seconds that a builder can run").
2013-04-23 18:04:59 +02:00
Eelco Dolstra f9974f856e Show that --timeout doesn't work if the build produces log output 2013-04-23 17:16:29 +02:00
Eelco Dolstra 6955d41f2b nix-build: Respect --timeout 2013-04-23 17:16:01 +02:00
Eelco Dolstra 934cf2d1f4 Nix daemon: respect build timeout from the client 2013-04-23 16:59:06 +02:00
Eelco Dolstra 08d96ffad0 Fix --fallback with the binary cache substituter
Reported by Peter Simons.
2013-04-23 12:45:01 +02:00
Eelco Dolstra a9b4e26b5c Test whether --fallback works if NARS have disappeared from the binary cache 2013-04-23 12:44:01 +02:00
Eelco Dolstra c642441beb Test NAR info caching 2013-04-23 12:43:28 +02:00
Eelco Dolstra 05420e7883 Manual: Add a missing step to the build instructions
Reported by Johan Grande.
2013-04-09 17:57:48 +02:00
Eelco Dolstra 258897c265 Complain if /homeless-shelter exists 2013-04-04 11:16:26 +02:00
Eelco Dolstra 239841787b Fix evaluation of the VM tests 2013-03-25 21:59:11 +01:00
Shea LevyandEelco Dolstra cc63db1dd5 makeStoreWritable: Ask forgiveness, not permission
It is surprisingly impossible to check if a mountpoint is a bind mount
on Linux, and in my previous commit I forgot to check if /nix/store was
even a mountpoint at all. statvfs.f_flag is not populated with MS_BIND
(and even if it were, my check was wrong in the previous commit).

Luckily, the semantics of mount with MS_REMOUNT | MS_BIND make both
checks unnecessary: if /nix/store is not a mountpoint, then mount will
fail with EINVAL, and if /nix/store is not a bind-mount, then it will
not be made writable. Thus, if /nix/store is not a mountpoint, we fail
immediately (since we don't know how to make it writable), and if
/nix/store IS a mountpoint but not a bind-mount, we fail at first write
(see below for why we can't check and fail immediately).

Note that, due to what is IMO buggy behavior in Linux, calling mount
with MS_REMOUNT | MS_BIND on a non-bind readonly mount makes the
mountpoint appear writable in two places: In the sixth (but not the
10th!) column of mountinfo, and in the f_flags member of struct statfs.
All other syscalls behave as if the mount point were still readonly (at
least for Linux 3.9-rc1, but I don't think this has changed recently or
is expected to soon). My preferred semantics would be for MS_REMOUNT |
MS_BIND to fail on a non-bind mount, as it doesn't make sense to remount
a non bind-mount as a bind mount.
2013-03-25 19:00:16 +01:00
Shea LevyandEelco Dolstra 2c9cf50746 makeStoreWritable: Use statvfs instead of /proc/self/mountinfo to find out if /nix/store is a read-only bind mount
/nix/store could be a read-only bind mount even if it is / in its own filesystem, so checking the 4th field in mountinfo is insufficient.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-03-25 19:00:16 +01:00
Eelco Dolstra c3fc60d936 Fix evaluation 2013-03-18 21:49:42 +01:00
Eelco Dolstra f72ed36025 Bump version number 2013-03-15 14:21:05 +01:00
Eelco Dolstra 78d777ca15 Remove the "system" jobset input 2013-03-15 13:18:49 +01:00
Eelco Dolstra a68ebf8e37 Require Bison 2.6 2013-03-14 18:33:15 +01:00
Eelco Dolstra 804709706c Fix building against Bison 2.6 2013-03-14 18:31:08 +01:00
Eelco Dolstra c56bc3d81c Make sure that thunks are restored properly if an exception occurs
Fixes Hydra bug #67.
2013-03-14 17:21:13 +01:00
Eelco Dolstra 4b07476848 Prevent config.h from being clobbered 2013-03-08 01:27:04 +01:00
Eelco Dolstra bdd4646338 Revert "Prevent config.h from being clobbered"
This reverts commit 28bba8c44f.
2013-03-08 01:24:59 +01:00
Eelco Dolstra e73d9e9488 Fix annoying Perl 5.16 warnings
I.e.

Subroutine Nix::Store::isValidPath redefined at /nix/store/clfzsf6gi7qh5i9c0vks1ifjam47rijn-perl-5.16.2/lib/perl5/5.16.2/XSLoader.pm line 92.

and so on.
2013-03-08 00:27:32 +01:00
Eelco Dolstra 28bba8c44f Prevent config.h from being clobbered 2013-03-07 23:55:55 +01:00
Eelco Dolstra 8057a192e3 Handle systems without lutimes() or lchown() 2013-02-28 19:55:09 +01:00
Eelco Dolstra 9fa1bee575 Update release notes
Also use a point release version number as suggested by several
people.
2013-02-28 19:36:02 +01:00
Eelco Dolstra f45c731cd7 Handle symlinks properly
Now it's really brown paper bag time...
2013-02-28 14:51:08 +01:00
Eelco Dolstra 88936411bc Bump version number 2013-02-28 13:03:53 +01:00
Eelco Dolstra 0111ba98ea Handle hard links to other files in the output 2013-02-27 17:18:41 +01:00
Eelco Dolstra b008674e46 Refactoring: Split off the non-recursive canonicalisePathMetaData()
Also, change the file mode before changing the owner.  This prevents a
slight time window in which a setuid binary would be setuid root.
2013-02-27 16:42:19 +01:00
Eelco Dolstra 826dc0d07d Remove outdated file 2013-02-26 14:32:48 +01:00
Eelco Dolstra 97c6009c47 Bump version number 2013-02-26 14:32:14 +01:00
Eelco Dolstra ca9c02dff1 Update release notes 2013-02-26 13:23:09 +01:00
Eelco Dolstra 5526a282b5 Security: Don't allow builders to change permissions on files they don't own
It turns out that in multi-user Nix, a builder may be able to do

  ln /etc/shadow $out/foo

Afterwards, canonicalisePathMetaData() will be applied to $out/foo,
causing /etc/shadow's mode to be set to 444 (readable by everybody but
writable by nobody).  That's obviously Very Bad.

Fortunately, this fails in NixOS's default configuration because
/nix/store is a bind mount, so "ln" will fail with "Invalid
cross-device link".  It also fails if hard-link restrictions are
enabled, so a workaround is:

  echo 1 > /proc/sys/fs/protected_hardlinks

The solution is to check that all files in $out are owned by the build
user.  This means that innocuous operations like "ln
${pkgs.foo}/some-file $out/" are now rejected, but that already failed
in chroot builds anyway.
2013-02-26 02:30:19 +01:00
Eelco Dolstra dadf7a5b46 build-remote: Use the --quiet flag
‘--option verbosity 0’ doesn't actually do anything.
2013-02-19 16:56:31 +01:00
Ludovic CourtèsandEelco Dolstra 3e067ac11c Add `Settings::nixDaemonSocketFile'. 2013-02-19 10:19:18 +01:00
Ludovic CourtèsandEelco Dolstra 5ea138dc4b Enable chroot support on old glibc versions. 2013-02-19 10:19:11 +01:00
Eelco Dolstra 79a3ba7fa3 Document ‘hashString’ 2013-02-08 20:04:14 +01:00
Eelco Dolstra 5f18cd2e84 Make "${./path} ..." evaluate to a string, not a path
Wacky string coercion semantics caused expressions like

  exec = "${./my-script} params...";

to evaluate to a path (‘/path/my-script params’), because
anti-quotations are desuged to string concatenation:

  exec = ./my-script + " params...";

By constrast, adding a space at the start would yield a string as
expected:

  exec = " ${./my-script} params...";

Now the first example also evaluates to a string.
2013-02-08 20:04:04 +01:00
Eelco Dolstra 52172607cf Rename "hash" to "hashString" and handle SHA-1 2013-02-08 19:36:23 +01:00
Marc WeberandEelco Dolstra 01a5ea9914 experimental/hash
adding primop function calculating hash of a string

Signed-off-by: Marc Weber <marco-oweber@gmx.de>
2013-02-08 19:26:34 +01:00
Eelco Dolstra 8add116acd Nix::Store::derivationFromPath: Return derivation outputs 2013-02-05 16:02:57 +01:00
Eelco Dolstra d6143801fd Support the colonies 2013-01-30 18:50:58 +01:00
Eelco Dolstra 9842077cb2 Improve -I description
Issue #88.
2013-01-24 13:00:44 +01:00
Eelco Dolstra 5e9c3da412 Only warn about SQLite being busy once
No need to get annoying.
2013-01-23 16:45:10 +01:00
Eelco Dolstra 99ed558a93 Correctly handle missing logs 2013-01-22 22:07:25 +01:00
Eelco Dolstra 1943b60ad8 Fix the VM tests 2013-01-21 22:36:23 +01:00
Eelco Dolstra 96fbbbde55 build-remote.pl: Don't keep build logs on the build slave 2013-01-21 22:26:25 +01:00
Shea LevyandEelco Dolstra d6fd6d8aff corepkgs/fetchurl: Enable making the downloaded file executable 2013-01-21 10:20:52 +01:00
Eelco Dolstra 536c85ea49 Store build logs in /nix/var/log/nix/drvs/<XX>
...where <XX> is the first two characters of the derivation.
Otherwise /nix/var/log/nix/drvs may become so large that we run into
all sorts of weird filesystem limits/inefficiences.  For instance,
ext3/ext4 filesystems will barf with "ext4_dx_add_entry:1551:
Directory index full!" once you hit a few million files.
2013-01-17 15:47:26 +01:00
Eelco Dolstra 66fa9e6a4d Bump version number to 1.4 2013-01-07 14:48:44 +01:00
Eelco Dolstra e42df686f3 Delete a left-over trash directory before doing a GC 2013-01-05 00:13:29 +01:00
Eelco Dolstra 92926be2fe Fix "0 store paths deleted" message 2013-01-04 15:17:19 +01:00
Eelco Dolstra b424d29d1b Open the database after removing immutable bits 2013-01-03 13:29:17 +01:00
Eelco Dolstra 3007f57377 Remove tabs 2013-01-03 13:00:46 +01:00
Eelco Dolstra def5160b61 Clear any immutable bits in the Nix store
Doing this once makes subsequent operations like garbage collecting
more efficient since we don't have to call makeMutable() first.
2013-01-03 12:59:23 +01:00
Eelco Dolstra 0a4e90395c Urgggh
http://hydra.nixos.org/build/3661100
2013-01-02 23:52:15 +01:00
Eelco Dolstra f12492c66d Manual: Fix "nix-store --export" example 2013-01-02 22:36:08 +01:00
Eelco Dolstra aebea2e489 Reinstate the http://nixos.org/binary-cache default for the binary-caches setting 2013-01-02 22:16:37 +01:00
Eelco Dolstra 649bb60617 Use sysconfdir=/etc 2013-01-02 22:12:19 +01:00
Eelco Dolstra 42d6f640c1 Update release notes 2013-01-02 13:00:55 +01:00
Eelco Dolstra 299141ecbd If a substitute closure is incomplete, build dependencies, then retry the substituter
Issue #77.
2013-01-02 12:38:28 +01:00
Eelco Dolstra 1b3a78a459 Automatically fall back if the references of a substitute are not substitutable
Fixes #77.
2013-01-02 12:00:26 +01:00
Eelco Dolstra 82248abd8f Add a test for incomplete closures in the binary cache
Issue #77.
2013-01-02 11:45:23 +01:00
Eelco Dolstra 12f9129f60 nix-build: Support talking to old daemons
Fixes #76.
2012-12-29 23:21:46 +01:00
Eelco Dolstra b7629778ef Allow mounting a path in a different location in the chroot
Fixes #24.
2012-12-29 23:04:02 +01:00
Eelco Dolstra 68dcbb187e download-from-binary-cache: Use the channels of the calling user rather than root
This should make live easier for single-user (non-daemon)
installations.  Note that when the daemon is used, the "calling user"
is root so we're not using any untrusted caches.
2012-12-21 15:00:07 +01:00
Eelco Dolstra 5ee7d8fbab Typo fix 2012-12-21 00:18:59 +01:00
Eelco Dolstra 2754a07ead nix-store -q --roots: Respect the gc-keep-outputs/gc-keep-derivations settings
So if a path is not garbage solely because it's reachable from a root
due to the gc-keep-outputs or gc-keep-derivations settings, ‘nix-store
-q --roots’ now shows that root.
2012-12-20 18:41:44 +01:00
Eelco Dolstra 06f62defe6 Yet another rewrite of the garbage collector
But this time it's *obviously* correct!  No more segfaults due to
infinite recursions for sure, etc.

Also, move directories to /nix/store/trash instead of renaming them to
/nix/store/bla-gc-<pid>.  Then we can just delete /nix/store/trash at
the end.
2012-12-20 17:32:15 +01:00
Eelco Dolstra 9c29a2ed35 Give a better error message if writeFile fails due to permission issues 2012-12-20 12:22:13 +01:00
Eelco Dolstra e775d4d84f If gc-keep-derivations is set, only keep the actual deriver
This prevents zillions of derivations from being kept, and fixes an
infinite recursion in the garbage collector (due to an obscure cycle
that can occur with fixed-output derivations).
2012-12-19 15:33:09 +01:00
Eelco Dolstra fbf0b2fa45 Kill the build hook rather than shutting it down cleanly
Waiting for the hook to shut down cleanly sometimes seems to lead to
hangs.
2012-12-19 11:47:42 +01:00
Eelco Dolstra 228ea7c2f9 Revert brain fart
This reverts commit cc511fd65b.
2012-12-18 20:43:47 +01:00
Eelco Dolstra cc511fd65b Check for potential infinite select() loops when building 2012-12-18 18:50:05 +01:00
Stuart PernsteinerandEelco Dolstra a9045c727f fix use-after-free bug in mkString(Value&, Symbol&) 2012-12-13 11:13:20 +01:00
Eelco Dolstra 9fa12fc201 Allow setting the profile location using $NIX_PROFILE
Fixes #69.
2012-12-12 16:01:46 +01:00
Eelco Dolstra 3ad53e43c8 Debian package: Add dependency on libwww-curl-perl
Fixes issue #70.
2012-12-11 16:50:21 +01:00
Eelco Dolstra 772778c0ec On SQLITE_BUSY, wait a random amount of time
If all contending processes wait a fixed amount of time (100 ms),
there is a good probability that they'll just collide again.
2012-12-11 11:49:42 +01:00
Eelco Dolstra e087bfef5f Bump version number 2012-12-07 13:14:04 +01:00
123 changed files with 3383 additions and 1846 deletions
-2
View File
@@ -27,11 +27,9 @@ init-state:
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots
ln -sfn $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool
-$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(storedir) -$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(storedir)
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests $(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
else else
+8 -2
View File
@@ -68,6 +68,8 @@ if test "$sys_name" = sunos; then
fi fi
CFLAGS=${CFLAGS:--g -O3 -Wall}
CXXFLAGS=${CXXFLAGS:--g -O3 -Wall}
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX AC_PROG_CXX
@@ -106,6 +108,7 @@ AC_LANG_POP(C++)
# Check for chroot support (requires chroot() and bind mounts). # Check for chroot support (requires chroot() and bind mounts).
AC_CHECK_FUNCS([chroot]) AC_CHECK_FUNCS([chroot])
AC_CHECK_FUNCS([unshare]) AC_CHECK_FUNCS([unshare])
AC_CHECK_FUNCS([statvfs])
AC_CHECK_HEADERS([sched.h]) AC_CHECK_HEADERS([sched.h])
AC_CHECK_HEADERS([sys/param.h]) AC_CHECK_HEADERS([sys/param.h])
AC_CHECK_HEADERS([sys/mount.h], [], [], AC_CHECK_HEADERS([sys/mount.h], [], [],
@@ -124,6 +127,10 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
AC_CHECK_FUNCS([lutimes]) AC_CHECK_FUNCS([lutimes])
# Check for sched_setaffinity.
AC_CHECK_FUNCS([sched_setaffinity])
# Check whether the store optimiser can optimise symlinks. # Check whether the store optimiser can optimise symlinks.
AC_MSG_CHECKING([whether it is possible to create a link to a symlink]) AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
ln -s bla tmp_link ln -s bla tmp_link
@@ -182,10 +189,10 @@ NEED_PROG(perl, perl)
NEED_PROG(sed, sed) NEED_PROG(sed, sed)
NEED_PROG(tar, tar) NEED_PROG(tar, tar)
NEED_PROG(bzip2, bzip2) NEED_PROG(bzip2, bzip2)
NEED_PROG(gzip, gzip)
NEED_PROG(xz, xz) NEED_PROG(xz, xz)
AC_PATH_PROG(dot, dot) AC_PATH_PROG(dot, dot)
AC_PATH_PROG(dblatex, dblatex) AC_PATH_PROG(dblatex, dblatex)
AC_PATH_PROG(gzip, gzip)
AC_PATH_PROG(pv, pv, pv) AC_PATH_PROG(pv, pv, pv)
@@ -354,7 +361,6 @@ fi
AC_SUBST(tarFlags) AC_SUBST(tarFlags)
AM_CONFIG_HEADER([config.h]) AM_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
src/Makefile src/Makefile
+2 -5
View File
@@ -36,9 +36,6 @@ sub createLinks {
if ($srcFile =~ /\/propagated-build-inputs$/ || if ($srcFile =~ /\/propagated-build-inputs$/ ||
$srcFile =~ /\/nix-support$/ || $srcFile =~ /\/nix-support$/ ||
$srcFile =~ /\/perllocal.pod$/ || $srcFile =~ /\/perllocal.pod$/ ||
$srcFile =~ /\/easy-install.pth$/ ||
$srcFile =~ /\/site.py$/ ||
$srcFile =~ /\/site.pyc$/ ||
$srcFile =~ /\/info\/dir$/ || $srcFile =~ /\/info\/dir$/ ||
$srcFile =~ /\/log$/) $srcFile =~ /\/log$/)
{ {
@@ -56,7 +53,7 @@ sub createLinks {
elsif (-l _) { elsif (-l _) {
my $target = readlink $dstFile or die; my $target = readlink $dstFile or die;
if (!-d $target) { if (!-d $target) {
die "collission between directory `$srcFile' and non-directory `$target'"; die "collision between directory `$srcFile' and non-directory `$target'";
} }
unlink $dstFile or die "error unlinking `$dstFile': $!"; unlink $dstFile or die "error unlinking `$dstFile': $!";
mkdir $dstFile, 0755 || mkdir $dstFile, 0755 ||
@@ -78,7 +75,7 @@ sub createLinks {
if (-l $dstFile) { if (-l $dstFile) {
my $target = readlink $dstFile; my $target = readlink $dstFile;
my $prevPriority = $priorities{$dstFile}; my $prevPriority = $priorities{$dstFile};
die ( "collission between `$srcFile' and `$target'; " die ( "collision between `$srcFile' and `$target'; "
. "use `nix-env --set-flag " . "use `nix-env --set-flag "
. "priority NUMBER PKGNAME' to change the priority of " . "priority NUMBER PKGNAME' to change the priority of "
. "one of the conflicting packages\n" ) . "one of the conflicting packages\n" )
+1
View File
@@ -7,6 +7,7 @@ in {
shell = "@shell@"; shell = "@shell@";
coreutils = "@coreutils@"; coreutils = "@coreutils@";
bzip2 = "@bzip2@"; bzip2 = "@bzip2@";
gzip = "@gzip@";
xz = "@xz@"; xz = "@xz@";
tar = "@tar@"; tar = "@tar@";
tarFlags = "@tarFlags@"; tarFlags = "@tarFlags@";
+4 -3
View File
@@ -1,6 +1,6 @@
with import <nix/config.nix>; with import <nix/config.nix>;
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? ""}: {system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? "", executable ? false}:
assert (outputHash != "" && outputHashAlgo != "") assert (outputHash != "" && outputHashAlgo != "")
|| md5 != "" || sha1 != "" || sha256 != ""; || md5 != "" || sha1 != "" || sha256 != "";
@@ -8,10 +8,10 @@ assert (outputHash != "" && outputHashAlgo != "")
let let
builder = builtins.toFile "fetchurl.sh" builder = builtins.toFile "fetchurl.sh"
'' (''
echo "downloading $url into $out" echo "downloading $url into $out"
${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out" ${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out"
''; '' + (if executable then "${coreutils}/chmod +x $out" else ""));
in in
@@ -25,6 +25,7 @@ derivation {
if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5"; if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5";
outputHash = if outputHash != "" then outputHash else outputHash = if outputHash != "" then outputHash else
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5; if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
outputHashMode = if executable then "recursive" else "flat";
inherit system url; inherit system url;
+14 -8
View File
@@ -6,25 +6,28 @@ let
'' ''
export PATH=${nixBinDir}:${coreutils} export PATH=${nixBinDir}:${coreutils}
if [ $compressionType = "xz" ]; then if [ $compressionType = xz ]; then
ext=xz ext=.xz
compressor="${xz} -9" compressor="| ${xz} -7"
elif [ $compressionType = bzip2 ]; then
ext=.bz2
compressor="| ${bzip2}"
else else
ext=bz2 ext=
compressor="${bzip2}" compressor=
fi fi
echo "packing $storePath..." echo "packing $storePath..."
mkdir $out mkdir $out
dst=$out/tmp.nar.$ext dst=$out/tmp.nar$ext
set -o pipefail set -o pipefail
nix-store --dump "$storePath" | $compressor > $dst eval "nix-store --dump \"$storePath\" $compressor > $dst"
hash=$(nix-hash --flat --type $hashAlgo --base32 $dst) hash=$(nix-hash --flat --type $hashAlgo --base32 $dst)
echo -n $hash > $out/nar-compressed-hash echo -n $hash > $out/nar-compressed-hash
mv $dst $out/$hash.nar.$ext mv $dst $out/$hash.nar$ext
''; '';
in in
@@ -40,4 +43,7 @@ derivation {
# Don't build in a chroot because Nix's dependencies may not be there. # Don't build in a chroot because Nix's dependencies may not be there.
__noChroot = true; __noChroot = true;
# Remote machines may not have ${nixBinDir} or ${coreutils} in the same prefixes
preferLocalBuild = true;
} }
+5 -2
View File
@@ -6,9 +6,12 @@ let
'' ''
mkdir $out mkdir $out
cd $out cd $out
pat="\.xz\$" xzpat="\.xz\$"
if [[ "$src" =~ $pat ]]; then gzpat="\.gz\$"
if [[ "$src" =~ $xzpat ]]; then
${xz} -d < $src | ${tar} xf - ${tarFlags} ${xz} -d < $src | ${tar} xf - ${tarFlags}
elif [[ "$src" =~ $gzpat ]]; then
${gzip} -d < $src | ${tar} xf - ${tarFlags}
else else
${bzip2} -d < $src | ${tar} xf - ${tarFlags} ${bzip2} -d < $src | ${tar} xf - ${tarFlags}
fi fi
Executable
+17
View File
@@ -0,0 +1,17 @@
#! /bin/sh
if [ -e tests/test-tmp ]; then
chmod -R u+w tests/test-tmp
rm -rf tests/test-tmp
fi
s=$(type -p nix-shell)
exec $s release.nix -A tarball --command "
export NIX_REMOTE=daemon
export NIX_PATH='$NIX_PATH'
export NIX_BUILD_SHELL=$(type -p bash)
export c=\$configureFlags
exec $s release.nix -A build.x86_64-linux --exclude tarball --command '
configureFlags+=\" \$c --prefix=$(pwd)/inst --sysconfdir=$(pwd)/inst/etc\"
return
'" \
"$@"
-33
View File
@@ -1,33 +0,0 @@
To produce a `stable' release from the trunk:
-1. Update the release notes; make sure that the release date is
correct.
0. Make sure that the trunk builds in the release supervisor.
1. Branch the trunk, e.g., `svn cp .../trunk
.../branches/0.5-release'.
2. Switch to the branch, e.g., `svn switch .../branches/0.5-release'.
3. In `configure.ac', change `STABLE=0' into `STABLE=1' and commit.
4. In the release supervisor, add a one-time job to build
`.../branches/0.5-release'.
5. Make sure that the release succeeds.
6. Move the branch to a tag, e.g., `svn mv .../branches/0.5-release
.../tags/0.5'.
Note that the branch should not be used for maintenance; it should
be deleted after the release has been created. A maintenance
branch (e.g., `.../branches/0.5') should be created from the
original revision of the trunk (since maintenance releases should
also be tested first; hence, we cannot have `STABLE=1'). The same
procedure can then be followed to produce maintenance releases;
just substitute `.../branches/VERSION' for the trunk.
7. Switch back to the trunk.
8. Bump the version number in `configure.ac' (in AC_INIT).
+1 -1
View File
@@ -16,7 +16,7 @@ dblatex_opts = \
# Note: we use GIF for now, since the PNGs shipped with Docbook aren't # Note: we use GIF for now, since the PNGs shipped with Docbook aren't
# transparent. # transparent.
man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \ man1_MANS = nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \
nix-collect-garbage.1 nix-push.1 nix-pull.1 \ nix-collect-garbage.1 nix-push.1 nix-pull.1 \
nix-prefetch-url.1 nix-channel.1 \ nix-prefetch-url.1 nix-channel.1 \
nix-install-package.1 nix-hash.1 nix-copy-closure.1 nix-install-package.1 nix-hash.1 nix-copy-closure.1
+68 -43
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 Nix expression; you can just access them right away. But to prevent
polluting the namespace too much, most built-ins are not in scope. polluting the namespace too much, most built-ins are not in scope.
Instead, you can access them through the <varname>builtins</varname> Instead, you can access them through the <varname>builtins</varname>
built-in value, which is an attribute set that contains all built-in built-in value, which is a set that contains all built-in functions
functions and values. For instance, <function>derivation</function> and values. For instance, <function>derivation</function> is also
is also available as <function>builtins.derivation</function>.</para> available as <function>builtins.derivation</function>.</para>
<variablelist> <variablelist>
@@ -39,17 +39,17 @@ is also available as <function>builtins.derivation</function>.</para>
<varlistentry><term><function>builtins.attrNames</function> <varlistentry><term><function>builtins.attrNames</function>
<replaceable>attrs</replaceable></term> <replaceable>set</replaceable></term>
<listitem><para>Return the names of the attributes in the <listitem><para>Return the names of the attributes in the set
attribute set <replaceable>attrs</replaceable> in a sorted list. <replaceable>set</replaceable> in a sorted list. For instance,
For instance, <literal>builtins.attrNames { y = 1; x = "foo"; <literal>builtins.attrNames { y = 1; x = "foo"; }</literal>
}</literal> evaluates to <literal>[ "x" "y" ]</literal>. There is evaluates to <literal>[ "x" "y" ]</literal>. There is no built-in
no built-in function <function>attrValues</function>, but you can function <function>attrValues</function>, but you can easily
easily define it yourself: define it yourself:
<programlisting> <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> </para></listitem>
@@ -68,8 +68,8 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
<varlistentry><term><varname>builtins</varname></term> <varlistentry><term><varname>builtins</varname></term>
<listitem><para>The attribute set <varname>builtins</varname> <listitem><para>The set <varname>builtins</varname> contains all
contains all the built-in functions and values. You can use the built-in functions and values. You can use
<varname>builtins</varname> to test for the availability of <varname>builtins</varname> to test for the availability of
features in the Nix installation, e.g., features in the Nix installation, e.g.,
@@ -258,11 +258,11 @@ stdenv.mkDerivation {
<varlistentry><term><function>builtins.getAttr</function> <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 <listitem><para><function>getAttr</function> returns the attribute
named <replaceable>s</replaceable> from the attribute set named <replaceable>s</replaceable> from
<replaceable>attrs</replaceable>. Evaluation aborts if the <replaceable>set</replaceable>. Evaluation aborts if the
attribute doesnt exist. This is a dynamic version of the attribute doesnt exist. This is a dynamic version of the
<literal>.</literal> operator, since <replaceable>s</replaceable> <literal>.</literal> operator, since <replaceable>s</replaceable>
is an expression rather than an identifier.</para></listitem> is an expression rather than an identifier.</para></listitem>
@@ -289,15 +289,27 @@ stdenv.mkDerivation {
<varlistentry><term><function>builtins.hasAttr</function> <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 <listitem><para><function>hasAttr</function> returns
<literal>true</literal> if the attribute set <literal>true</literal> if <replaceable>set</replaceable> has an
<replaceable>attrs</replaceable> has an attribute named attribute named <replaceable>s</replaceable>, and
<replaceable>s</replaceable>, and <literal>false</literal> <literal>false</literal> otherwise. This is a dynamic version of
otherwise. This is a dynamic version of the <literal>?</literal> the <literal>?</literal> operator, since
operator, since <replaceable>s</replaceable> is an expression <replaceable>s</replaceable> is an expression rather than an
rather than an identifier.</para></listitem> identifier.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.hashString</function>
<replaceable>type</replaceable> <replaceable>s</replaceable></term>
<listitem><para>Return a base-16 representation of the
cryptographic hash of string <replaceable>s</replaceable>. The
hash algorithm specified by <replaceable>type</replaceable> must
be one of <literal>"md5"</literal>, <literal>"sha1"</literal> or
<literal>"sha256"</literal>.</para></listitem>
</varlistentry> </varlistentry>
@@ -319,12 +331,12 @@ stdenv.mkDerivation {
<listitem><para>Load, parse and return the Nix expression in the <listitem><para>Load, parse and return the Nix expression in the
file <replaceable>path</replaceable>. If <replaceable>path file <replaceable>path</replaceable>. If <replaceable>path
</replaceable> is a directory, the file <filename>default.nix </replaceable> is a directory, the file <filename>default.nix
</filename> in that directory is loaded. Evaluation aborts if </filename> in that directory is loaded. Evaluation aborts if the
the file doesnt exist or contains an incorrect Nix file doesnt exist or contains an incorrect Nix expression.
expression. <function>import</function> implements Nixs module <function>import</function> implements Nixs module system: you
system: you can put any Nix expression (such as an attribute set can put any Nix expression (such as a set or a function) in a
or a function) in a separate file, and use it from Nix expressions separate file, and use it from Nix expressions in other
in other files.</para> files.</para>
<para>A Nix expression loaded by <function>import</function> must <para>A Nix expression loaded by <function>import</function> must
not contain any <emphasis>free variables</emphasis> (identifiers not contain any <emphasis>free variables</emphasis> (identifiers
@@ -371,9 +383,9 @@ x: x + 456</programlisting>
<varlistentry><term><function>builtins.intersectAttrs</function> <varlistentry><term><function>builtins.intersectAttrs</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term> <replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return an attribute set consisting of the <listitem><para>Return a set consisting of the attributes in the
attributes in the set <replaceable>e2</replaceable> that also set <replaceable>e2</replaceable> that also exist in the set
exist in the set <replaceable>e1</replaceable>.</para></listitem> <replaceable>e1</replaceable>.</para></listitem>
</varlistentry> </varlistentry>
@@ -382,7 +394,7 @@ x: x + 456</programlisting>
<replaceable>e</replaceable></term> <replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if <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> <literal>false</literal> otherwise.</para></listitem>
</varlistentry> </varlistentry>
@@ -422,7 +434,7 @@ x: x + 456</programlisting>
<replaceable>e</replaceable></term> <replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if <listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a int, and <replaceable>e</replaceable> evaluates to an int, and
<literal>false</literal> otherwise.</para></listitem> <literal>false</literal> otherwise.</para></listitem>
</varlistentry> </varlistentry>
@@ -478,9 +490,9 @@ x: x + 456</programlisting>
<varlistentry><term><function>builtins.listToAttrs</function> <varlistentry><term><function>builtins.listToAttrs</function>
<replaceable>e</replaceable></term> <replaceable>e</replaceable></term>
<listitem><para>Construct an attribute set from a list specifying <listitem><para>Construct a set from a list specifying the names
the names and values of each attribute. Each element of the list and values of each attribute. Each element of the list should be
should be an attribute set consisting of a string-valued attribute a set consisting of a string-valued attribute
<varname>name</varname> specifying the name of the attribute, and <varname>name</varname> specifying the name of the attribute, and
an attribute <varname>value</varname> specifying its value. an attribute <varname>value</varname> specifying its value.
Example: Example:
@@ -535,7 +547,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
a package name and version. The package name is everything up to a package name and version. The package name is everything up to
but not including the first dash followed by a digit, and the but not including the first dash followed by a digit, and the
version is everything following that dash. The result is returned 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> <literal>builtins.parseDrvName "nix-0.12pre12876"</literal>
returns <literal>{ name = "nix"; version = "0.12pre12876"; returns <literal>{ name = "nix"; version = "0.12pre12876";
}</literal>.</para></listitem> }</literal>.</para></listitem>
@@ -586,12 +598,12 @@ in config.someSetting</programlisting>
<varlistentry><term><function>removeAttrs</function> <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 <listitem><para>Remove the attributes listed in
<replaceable>list</replaceable> from the attribute set <replaceable>list</replaceable> from
<replaceable>attrs</replaceable>. The attributes dont have to <replaceable>set</replaceable>. The attributes dont have to
exist in <replaceable>attrs</replaceable>. For instance, exist in <replaceable>set</replaceable>. For instance,
<screen> <screen>
removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen> removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
@@ -780,7 +792,7 @@ in foo</programlisting>
servlet container</link>. A servlet container contains a number servlet container</link>. A servlet container contains a number
of servlets (<filename>*.war</filename> files) each exported under of servlets (<filename>*.war</filename> files) each exported under
a specific URI prefix. So the servlet configuration is a list of 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 <varname>war</varname> of the servlet (<xref
linkend='ex-toxml-co-servlets' />). This kind of information is linkend='ex-toxml-co-servlets' />). This kind of information is
difficult to communicate with the normal method of passing difficult to communicate with the normal method of passing
@@ -889,6 +901,19 @@ stdenv.mkDerivation (rec {
</varlistentry> </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> </variablelist>
+79 -56
View File
@@ -33,18 +33,18 @@ env-keep-derivations = false
<para>You can override settings using the <option>--option</option> <para>You can override settings using the <option>--option</option>
flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para> flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
<para>The following settings are currently available: <para>The following settings are currently available:
<variablelist> <variablelist>
<varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term> <varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term>
<listitem><para>If <literal>true</literal>, the garbage collector <listitem><para>If <literal>true</literal>, the garbage collector
will keep the outputs of non-garbage derivations. If will keep the outputs of non-garbage derivations. If
<literal>false</literal> (default), outputs will be deleted unless <literal>false</literal> (default), outputs will be deleted unless
they are GC roots themselves (or reachable from other roots).</para> they are GC roots themselves (or reachable from other roots).</para>
<para>In general, outputs must be registered as roots separately. <para>In general, outputs must be registered as roots separately.
However, even if the output of a derivation is registered as a However, even if the output of a derivation is registered as a
root, the collector will still delete store paths that are used root, the collector will still delete store paths that are used
@@ -53,7 +53,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
this option to <literal>true</literal>.</para></listitem> this option to <literal>true</literal>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term> <varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term>
@@ -71,7 +71,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry><term><literal>env-keep-derivations</literal></term> <varlistentry><term><literal>env-keep-derivations</literal></term>
<listitem><para>If <literal>false</literal> (default), derivations <listitem><para>If <literal>false</literal> (default), derivations
@@ -95,14 +95,14 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term> <varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term>
<listitem><para>This option defines the maximum number of jobs <listitem><para>This option defines the maximum number of jobs
that Nix will try to build in parallel. The default is that Nix will try to build in parallel. The default is
<literal>1</literal>. You should generally set it to the number <literal>1</literal>. You should generally set it to the number
of CPUs in your system (e.g., <literal>2</literal> on a Athlon 64 of CPUs in your system (e.g., <literal>2</literal> on an Athlon 64
X2). It can be overriden using the <option X2). It can be overridden using the <option
linkend='opt-max-jobs'>--max-jobs</option> (<option>-j</option>) linkend='opt-max-jobs'>--max-jobs</option> (<option>-j</option>)
command line switch.</para></listitem> command line switch.</para></listitem>
@@ -119,7 +119,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
<varname>enableParallelBuilding</varname> is set to <varname>enableParallelBuilding</varname> is set to
<literal>true</literal>, the builder passes the <literal>true</literal>, the builder passes the
<option>-j<replaceable>N</replaceable></option> flag to GNU Make. <option>-j<replaceable>N</replaceable></option> flag to GNU Make.
It can be overriden using the <option It can be overridden using the <option
linkend='opt-cores'>--cores</option> command line switch and linkend='opt-cores'>--cores</option> command line switch and
defaults to <literal>1</literal>. The value <literal>0</literal> defaults to <literal>1</literal>. The value <literal>0</literal>
means that the builder should use all available CPU cores in the means that the builder should use all available CPU cores in the
@@ -134,10 +134,10 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
<para>This option defines the maximum number of seconds that a <para>This option defines the maximum number of seconds that a
builder can go without producing any data on standard output or builder can go without producing any data on standard output or
standard error. This is useful (for instance in a automated standard error. This is useful (for instance in an automated
build system) to catch builds that are stuck in an infinite build system) to catch builds that are stuck in an infinite
loop, or to catch remote builds that are hanging due to network loop, or to catch remote builds that are hanging due to network
problems. It can be overriden using the <option problems. It can be overridden using the <option
linkend="opt-max-silent-time">--max-silent-time</option> command linkend="opt-max-silent-time">--max-silent-time</option> command
line switch.</para> line switch.</para>
@@ -148,15 +148,16 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term> <varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term>
<listitem> <listitem>
<para>This option defines the maximum number of seconds that a <para>This option defines the maximum number of seconds that a
builder can run. This is useful (for instance in a automated builder can run. This is useful (for instance in an automated
build system) to catch builds that are stuck in an infinite loop build system) to catch builds that are stuck in an infinite loop
but keep writing to their standard output or standard error. It but keep writing to their standard output or standard error. It
can be overriden using the <option can be overridden using the <option
linkend="opt-timeout">--timeout</option> command line linkend="opt-timeout">--timeout</option> command line
switch.</para> switch.</para>
@@ -168,6 +169,20 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-build-max-log-size"><term><literal>build-max-log-size</literal></term>
<listitem>
<para>This option defines the maximum number of bytes that a
builder can write to its stdout/stderr. If the builder exceeds
this limit, its killed. A value of <literal>0</literal> (the
default) means that there is no limit.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term> <varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
<listitem><para>This options specifies the Unix group containing <listitem><para>This options specifies the Unix group containing
@@ -234,7 +249,27 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
<listitem><para>When builds are performed in a chroot environment,
Nix will mount some directories from the normal file system
hierarchy inside the chroot. These are the Nix store, the
temporary build directory (usually
<filename>/tmp/nix-build-<replaceable>drvname</replaceable>-<replaceable>number</replaceable></filename>),
the <literal>/proc</literal> filesystem, and the directories
listed here. The default is <literal>/dev /dev/pts</literal>,
since these contain files needed by many builds (such as
<filename>/dev/null</filename>). You can use the syntax
<literal><replaceable>target</replaceable>=<replaceable>source</replaceable></literal>
to mount a path in a different location in the chroot; for
instance, <literal>/bin=/nix-bin</literal> will mount the
directory <literal>/nix-bin</literal> as <literal>/bin</literal>
inside the chroot.</para></listitem>
</varlistentry>
<varlistentry><term><literal>build-use-substitutes</literal></term> <varlistentry><term><literal>build-use-substitutes</literal></term>
<listitem><para>If set to <literal>true</literal> (default), Nix <listitem><para>If set to <literal>true</literal> (default), Nix
@@ -243,7 +278,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry><term><literal>build-fallback</literal></term> <varlistentry><term><literal>build-fallback</literal></term>
<listitem><para>If set to <literal>true</literal>, Nix will fall <listitem><para>If set to <literal>true</literal>, Nix will fall
@@ -253,34 +288,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry> </varlistentry>
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
<listitem><para>When builds are performed in a chroot environment,
Nix will mount (using <command>mount --bind</command> on Linux)
some directories from the normal file system hierarchy inside the
chroot. These are the Nix store, the temporary build directory
(usually
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
and the directories listed here. The default is <literal>dev
/proc</literal>. Files in <filename>/dev</filename> (such as
<filename>/dev/null</filename>) are needed by many builds, and
some files in <filename>/proc</filename> may also be needed
occasionally.</para>
<para>The value used on NixOS is
<programlisting>
build-use-chroot = /dev /proc /bin</programlisting>
to make the <filename>/bin/sh</filename> symlink available (which
is still needed by many builders).</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>build-cache-failures</literal></term> <varlistentry><term><literal>build-cache-failures</literal></term>
<listitem><para>If set to <literal>true</literal>, Nix will <listitem><para>If set to <literal>true</literal>, Nix will
@@ -331,8 +339,8 @@ build-use-chroot = /dev /proc /bin</programlisting>
<varlistentry><term><literal>binary-caches</literal></term> <varlistentry><term><literal>binary-caches</literal></term>
<listitem><para>A list of URLs of binary caches, separated by <listitem><para>A list of URLs of binary caches, separated by
whitespace. The default is empty.<!-- The default is whitespace. The default is
<literal>http://nixos.org/binary-cache</literal>. --></para></listitem> <literal>http://cache.nixos.org</literal>.</para></listitem>
</varlistentry> </varlistentry>
@@ -341,10 +349,12 @@ build-use-chroot = /dev /proc /bin</programlisting>
<listitem><para>A list of names of files that will be read to <listitem><para>A list of names of files that will be read to
obtain additional binary cache URLs. The default is obtain additional binary cache URLs. The default is
<literal>/nix/var/nix/profiles/per-user/root/channels/binary-caches/*</literal>, <literal>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels/binary-caches/*</literal>.
which ensures that Nix will use the binary caches corresponding to Note that when youre using the Nix daemon,
the channels installed by root. Do not set this option to read <replaceable>username</replaceable> is always equal to
files created by untrusted users!</para></listitem> <literal>root</literal>, so Nix will only use the binary caches
provided by the channels installed by root. Do not set this
option to read files created by untrusted users!</para></listitem>
</varlistentry> </varlistentry>
@@ -355,13 +365,25 @@ build-use-chroot = /dev /proc /bin</programlisting>
whitespace. These are not used by default, but can be enabled by whitespace. These are not used by default, but can be enabled by
users of the Nix daemon by specifying <literal>--option users of the Nix daemon by specifying <literal>--option
binary-caches <replaceable>urls</replaceable></literal> on the binary-caches <replaceable>urls</replaceable></literal> on the
command line. Daemon users are only allowed to pass a subset of command line. Unprivileged users are only allowed to pass a
the URLs listed in <literal>binary-caches</literal> and subset of the URLs listed in <literal>binary-caches</literal> and
<literal>trusted-binary-caches</literal>.</para></listitem> <literal>trusted-binary-caches</literal>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry><term><literal>extra-binary-caches</literal></term>
<listitem><para>Additional binary caches appended to those
specified in <option>binary-caches</option> and
<option>binary-caches-files</option>. When used by unprivileged
users, untrusted binary caches (i.e. those not listed in
<option>trusted-binary-caches</option>) are silently
ignored.</para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term> <varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
<listitem><para>The maximum number of parallel HTTP connections <listitem><para>The maximum number of parallel HTTP connections
@@ -415,15 +437,16 @@ build-use-chroot = /dev /proc /bin</programlisting>
</varlistentry> </varlistentry>
<varlistentry><term><literal>auto-optimise-store</literal></term> <varlistentry><term><literal>auto-optimise-store</literal></term>
<listitem><para>If set to <literal>true</literal> (the default), <listitem><para>If set to <literal>true</literal>, Nix
Nix automatically detects files in the store that have identical automatically detects files in the store that have identical
contents, and replaces them with hard links to a single copy. contents, and replaces them with hard links to a single copy.
This saves disk space. If set to <literal>false</literal>, you This saves disk space. If set to <literal>false</literal> (the
can still run <command>nix-store --optimise</command> to get rid default), you can still run <command>nix-store
of duplicate files.</para></listitem> --optimise</command> to get rid of duplicate
files.</para></listitem>
</varlistentry> </varlistentry>
+20 -2
View File
@@ -283,7 +283,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<listitem><para>This variable should be set to <listitem><para>This variable should be set to
<literal>daemon</literal> if you want to use the Nix daemon to <literal>daemon</literal> if you want to use the Nix daemon to
executed Nix operations, which is necessary in <link execute Nix operations. This is necessary in <link
linkend="ssec-multi-user">multi-user Nix installations</link>. linkend="ssec-multi-user">multi-user Nix installations</link>.
Otherwise, it should be left unset.</para></listitem> Otherwise, it should be left unset.</para></listitem>
@@ -293,7 +293,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<varlistentry xml:id="envar-other-stores"><term><envar>NIX_OTHER_STORES</envar></term> <varlistentry xml:id="envar-other-stores"><term><envar>NIX_OTHER_STORES</envar></term>
<listitem><para>This variable contains the paths of remote Nix <listitem><para>This variable contains the paths of remote Nix
installations from whichs paths can be copied, separated by colons. installations from which packages can be copied, separated by colons.
<phrase condition="manual">See <xref linkend="sec-sharing-packages" <phrase condition="manual">See <xref linkend="sec-sharing-packages"
/> for details.</phrase> Each path should be the /> for details.</phrase> Each path should be the
<filename>/nix</filename> directory of a remote Nix installation <filename>/nix</filename> directory of a remote Nix installation
@@ -312,6 +312,24 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry> </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> <varlistentry><term><envar>GC_INITIAL_HEAP_SIZE</envar></term>
<listitem><para>If Nix has been configured to use the Boehm garbage <listitem><para>If Nix has been configured to use the Boehm garbage
Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

+41
View File
@@ -0,0 +1,41 @@
<appendix xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id="chap-hacking">
<title>Hacking</title>
<para>This section provides some notes on how to hack on Nix. To get
the latest version of Nix from GitHub:
<screen>
$ git clone git://github.com/NixOS/nix.git
$ cd hydra
</screen>
</para>
<para>To build it and its dependencies:
<screen>
$ nix-build release.nix -A build.x86_64-linux
</screen>
</para>
<para>To build all dependencies and start a shell in which all
environment variables are set up so that those dependencies can be
found:
<screen>
$ ./dev-shell
</screen>
To build Nix itself in this shell:
<screen>
[nix-shell]$ ./bootstrap
[nix-shell]$ configurePhase
[nix-shell]$ make
</screen>
To test it:
<screen>
[nix-shell]$ make install
[nix-shell]$ make installcheck
</screen>
</para>
</appendix>
+6 -7
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>. xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
You can also get builds of the latest development release from our You can also get builds of the latest development release from our
<link <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> build system</link>.</para>
<para>For Fedora, RPM packages are available. These can be installed <para>For Fedora, RPM packages are available. These can be installed
@@ -166,11 +166,10 @@ a source distribution.</para>
<listitem><para>Recent versions of Bison and Flex to build the <listitem><para>Recent versions of Bison and Flex to build the
parser. (This is because Nix needs GLR support in Bison and parser. (This is because Nix needs GLR support in Bison and
reentrancy support in Flex.) For Bison, you need version 2.3 or reentrancy support in Flex.) For Bison, you need version 2.6, which
higher (1.875 does <emphasis>not</emphasis> work), which can be can be obtained from the <link
obtained from the <link
xlink:href="ftp://alpha.gnu.org/pub/gnu/bison">GNU FTP xlink:href="ftp://alpha.gnu.org/pub/gnu/bison">GNU FTP
server</link>. For Flex, you need version 2.5.33, which is server</link>. For Flex, you need version 2.5.35, which is
available on <link available on <link
xlink:href="http://lex.sourceforge.net/">SourceForge</link>. xlink:href="http://lex.sourceforge.net/">SourceForge</link>.
Slightly older versions may also work, but ancient versions like the Slightly older versions may also work, but ancient versions like the
@@ -189,7 +188,7 @@ a source distribution.</para>
downloaded from the <link downloaded from the <link
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>. xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
You can also grab the <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> recent development release</link>.</para>
<para>Alternatively, the most recent sources of Nix can be obtained <para>Alternatively, the most recent sources of Nix can be obtained
@@ -539,7 +538,7 @@ a symbolic link to the current <emphasis>user environment</emphasis>
installed packages). The simplest way to set the required environment installed packages). The simplest way to set the required environment
variables is to include the file variables is to include the file
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename> <filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
in your <filename>~/.bashrc</filename> (or similar), like this:</para> in your <filename>~/.profile</filename> (or similar), like this:</para>
<screen> <screen>
source <replaceable>prefix</replaceable>/etc/profile.d/nix.sh</screen> source <replaceable>prefix</replaceable>/etc/profile.d/nix.sh</screen>
+10 -8
View File
@@ -19,15 +19,15 @@
</author> </author>
<copyright> <copyright>
<year>2004-2012</year> <year>2004-2013</year>
<holder>Eelco Dolstra</holder> <holder>Eelco Dolstra</holder>
</copyright> </copyright>
<date>May 2012</date> <date>July 2013</date>
</info> </info>
<xi:include href="introduction.xml" /> <xi:include href="introduction.xml" />
<xi:include href="quick-start.xml" /> <xi:include href="quick-start.xml" />
<xi:include href="installation.xml" /> <xi:include href="installation.xml" />
@@ -40,17 +40,18 @@
<title>Command Reference</title> <title>Command Reference</title>
<xi:include href="opt-common.xml" /> <xi:include href="opt-common.xml" />
<xi:include href="env-common.xml" /> <xi:include href="env-common.xml" />
<section> <section>
<title>Main commands</title> <title>Main commands</title>
<xi:include href="nix-env.xml" /> <xi:include href="nix-env.xml" />
<xi:include href="nix-instantiate.xml" /> <xi:include href="nix-instantiate.xml" />
<xi:include href="nix-store.xml" /> <xi:include href="nix-store.xml" />
</section> </section>
<section> <section>
<title>Utilities</title> <title>Utilities</title>
<xi:include href="nix-build.xml" /> <xi:include href="nix-build.xml" />
<xi:include href="nix-shell.xml" />
<xi:include href="nix-channel.xml" /> <xi:include href="nix-channel.xml" />
<xi:include href="nix-collect-garbage.xml" /> <xi:include href="nix-collect-garbage.xml" />
<xi:include href="nix-copy-closure.xml" /> <xi:include href="nix-copy-closure.xml" />
@@ -66,17 +67,18 @@
<title>Files</title> <title>Files</title>
<xi:include href="conf-file.xml" /> <xi:include href="conf-file.xml" />
</section> </section>
</appendix> </appendix>
<xi:include href="troubleshooting.xml" /> <xi:include href="troubleshooting.xml" />
<!-- <xi:include href="bugs.xml" /> --> <!-- <xi:include href="bugs.xml" /> -->
<xi:include href="glossary.xml" /> <xi:include href="glossary.xml" />
<xi:include href="hacking.xml" />
<appendix> <appendix>
<title>Nix Release Notes</title> <title>Nix Release Notes</title>
<xi:include href="release-notes.xml" <xi:include href="release-notes.xml"
xpointer="xmlns(x=http://docbook.org/ns/docbook)xpointer(x:article/x:section)" /> xpointer="xmlns(x=http://docbook.org/ns/docbook)xpointer(x:article/x:section)" />
</appendix> </appendix>
</book> </book>
+2 -48
View File
@@ -38,11 +38,6 @@
</group> </group>
<replaceable>outlink</replaceable> <replaceable>outlink</replaceable>
</arg> </arg>
<arg>
<option>--run-env</option>
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
</arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg> <arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
</refsynopsisdiv> </refsynopsisdiv>
@@ -75,13 +70,6 @@ a root of the Nix garbage collector. This root disappears
automatically when the <filename>result</filename> symlink is deleted automatically when the <filename>result</filename> symlink is deleted
or renamed. So dont rename the symlink.</para></warning> or renamed. So dont rename the symlink.</para></warning>
<para>The subcommand <command>nix-build --run-env</command> will build
the dependencies of the derivation, but not the derivation itself. It
will then start an interactive shell in which all environment
variables defined by the derivation have been set to their
corresponding values. This is useful for reproducing the environment
of a derivation for development.</para>
</refsection> </refsection>
@@ -134,32 +122,12 @@ also <xref linkend="sec-common-options" />.</phrase></para>
</variablelist> </variablelist>
<para>The following common options are supported:</para>
<variablelist condition="manpage"> <variablelist condition="manpage">
<xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" /> <xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
</variablelist> </variablelist>
<para>The following options apply to <command>nix-build --run-env</command>.</para>
<variablelist>
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
<listitem><para>In the environment of the derivation, executeq the
command <replaceable>cmd</replaceable> instead of the default
interactive shell.</para></listitem>
</varlistentry>
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
<listitem><para>Do not build any dependencies whose store path
matches the regular expression <replaceable>regexp</replaceable>.
This option may be specified multiple times.</para></listitem>
</varlistentry>
</variablelist>
</refsection> </refsection>
@@ -176,20 +144,6 @@ lrwxrwxrwx <replaceable>...</replaceable> result -> /nix/store/d18hyl92g30l...
$ ls ./result/bin/ $ ls ./result/bin/
firefox firefox-config</screen> firefox firefox-config</screen>
<para>To build the dependencies of the package Pan, and start an
interactive shell in which to build it:
<screen>
$ nix-build '&lt;nixpkgs>' --run-env -A pan
$ tar xf $src
$ cd pan-*
$ ./configure
$ make
$ ./pan/gui/pan
</screen>
</para>
<para>If a derivation has multiple outputs, <para>If a derivation has multiple outputs,
<command>nix-build</command> will build the default (first) output. <command>nix-build</command> will build the default (first) output.
You can also build all outputs: You can also build all outputs:
+35 -13
View File
@@ -93,11 +93,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<listitem><para>Specifies the profile to be used by those <listitem><para>Specifies the profile to be used by those
operations that operate on a profile (designated below as the operations that operate on a profile (designated below as the
<emphasis>active profile</emphasis>). A profile is sequence of <emphasis>active profile</emphasis>). A profile is a sequence of
user environments called <emphasis>generations</emphasis>, one of user environments called <emphasis>generations</emphasis>, one of
which is the <emphasis>current generation</emphasis>. The default which is the <emphasis>current
profile is the target of the symbolic link generation</emphasis>.</para></listitem>
<filename>~/.nix-profile</filename> (see below).</para></listitem>
</varlistentry> </varlistentry>
@@ -154,11 +153,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
default.</para> default.</para>
<para>The Nix expressions in this directory are combined into a <para>The Nix expressions in this directory are combined into a
single attribute set, with each file as an attribute that has the single set, with each file as an attribute that has the name of
name of the file. Thus, if <filename>~/.nix-defexpr</filename> the file. Thus, if <filename>~/.nix-defexpr</filename> contains
contains two files, <filename>foo</filename> and two files, <filename>foo</filename> and <filename>bar</filename>,
<filename>bar</filename>, then the default Nix expression will then the default Nix expression will essentially be
essentially be
<programlisting> <programlisting>
{ {
@@ -210,6 +208,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<arg choice='plain'><option>--preserve-installed</option></arg> <arg choice='plain'><option>--preserve-installed</option></arg>
<arg choice='plain'><option>-P</option></arg> <arg choice='plain'><option>-P</option></arg>
</group> </group>
<group choice='opt'>
<arg choice='plain'><option>--remove-all</option></arg>
<arg choice='plain'><option>-r</option></arg>
</group>
<arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg> <arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
@@ -273,7 +275,7 @@ number of possible ways:
linkend="ss-functions">functions</link> that are called with the linkend="ss-functions">functions</link> that are called with the
active Nix expression as their single argument. The derivations active Nix expression as their single argument. The derivations
returned by those function calls are installed. This allows returned by those function calls are installed. This allows
derivations to be specified in a unambiguous way, which is necessary derivations to be specified in an unambiguous way, which is necessary
if there are multiple derivations with the same if there are multiple derivations with the same
name.</para></listitem> name.</para></listitem>
@@ -319,6 +321,16 @@ number of possible ways:
</varlistentry> </varlistentry>
<varlistentry><term><option>--remove-all</option></term>
<term><option>-r</option></term>
<listitem><para>Remove all previously installed packages first.
This is equivalent to running <literal>nix-env -e '*'</literal>
first, except that everything happens in a single
transaction.</para></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsection> </refsection>
@@ -392,7 +404,7 @@ $ nix-env -f ./foo.nix -i -E \
I.e., this evaluates to <literal>(f: (f {system = I.e., this evaluates to <literal>(f: (f {system =
"i686-linux";}).subversionWithJava) (import ./foo.nix)</literal>, thus "i686-linux";}).subversionWithJava) (import ./foo.nix)</literal>, thus
selecting the <literal>subversionWithJava</literal> attribute from the 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> <filename>./foo.nix</filename>.</para>
<para>A dry-run tells you which paths will be downloaded or built from <para>A dry-run tells you which paths will be downloaded or built from
@@ -693,7 +705,7 @@ firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
$ nix-env --preserve-installed -i firefox-2.0.0.11 $ nix-env --preserve-installed -i firefox-2.0.0.11
installing `firefox-2.0.0.11' installing `firefox-2.0.0.11'
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment' building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
Collission between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox' collision between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'. and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
<lineannotation>(i.e., cant have two active at the same time)</lineannotation> <lineannotation>(i.e., cant have two active at the same time)</lineannotation>
@@ -1245,7 +1257,7 @@ wrapper around <option>--list-generations</option> and
$ nix-env --rollback $ nix-env --rollback
switching from generation 92 to 91 switching from generation 92 to 91
$ nix-env --rolback $ nix-env --rollback
error: no generation older than the current (91) exists</screen> error: no generation older than the current (91) exists</screen>
</refsection> </refsection>
@@ -1256,6 +1268,16 @@ error: no generation older than the current (91) exists</screen>
<refsection condition="manpage"><title>Environment variables</title> <refsection condition="manpage"><title>Environment variables</title>
<variablelist> <variablelist>
<varlistentry><term><envar>NIX_PROFILE</envar></term>
<listitem><para>Location of the Nix profile. Defaults to the
target of the symlink <filename>~/.nix-profile</filename>, if it
exists, or <filename>/nix/var/nix/profiles/default</filename>
otherwise.</para></listitem>
</varlistentry>
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" /> <xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
</variablelist> </variablelist>
+11 -4
View File
@@ -20,6 +20,7 @@
<command>nix-push</command> <command>nix-push</command>
<arg choice='plain'><option>--dest</option> <replaceable>dest-dir</replaceable></arg> <arg choice='plain'><option>--dest</option> <replaceable>dest-dir</replaceable></arg>
<arg><option>--bzip2</option></arg> <arg><option>--bzip2</option></arg>
<arg><option>--none</option></arg>
<arg><option>--force</option></arg> <arg><option>--force</option></arg>
<arg><option>--link</option></arg> <arg><option>--link</option></arg>
<arg><option>--manifest</option></arg> <arg><option>--manifest</option></arg>
@@ -106,6 +107,12 @@ automatically.</para>
</varlistentry> </varlistentry>
<varlistentry><term><option>--none</option></term>
<listitem><para>Do not compress NARs.</para></listitem>
</varlistentry>
<varlistentry><term><option>--force</option></term> <varlistentry><term><option>--force</option></term>
<listitem><para>Overwrite <filename>.narinfo</filename> files if <listitem><para>Overwrite <filename>.narinfo</filename> files if
@@ -252,7 +259,7 @@ The properties that are currently supported are:
<listitem><para>Each binary cache has a priority (defaulting to <listitem><para>Each binary cache has a priority (defaulting to
50). Binary caches are checked for binaries in order of ascending 50). Binary caches are checked for binaries in order of ascending
priority; thus a higher number denotes a lower priority. The priority; thus a higher number denotes a lower priority. The
binary cache <uri>http://nixos.org/binary-cache</uri> has priority binary cache <uri>http://cache.nixos.org</uri> has priority
40.</para></listitem> 40.</para></listitem>
</varlistentry> </varlistentry>
@@ -270,14 +277,14 @@ URL <replaceable>url</replaceable> has a binary for
<replaceable>p</replaceable>, Nix fetches <replaceable>p</replaceable>, Nix fetches
<replaceable>url/h</replaceable>, where <replaceable>h</replaceable> <replaceable>url/h</replaceable>, where <replaceable>h</replaceable>
is the hash part of <replaceable>p</replaceable>. Thus, if we have a is the hash part of <replaceable>p</replaceable>. Thus, if we have a
cache <uri>http://nixos.org/binary-cache</uri> and we want to obtain cache <uri>http://cache.nixos.org</uri> and we want to obtain
the store path the store path
<screen> <screen>
/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7 /nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
</screen> </screen>
then Nix will attempt to fetch then Nix will attempt to fetch
<screen> <screen>
http://nixos.org/binary-cache/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo http://cache.nixos.org/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
</screen> </screen>
(Commands such as <command>nix-env -qas</command> will issue an HTTP (Commands such as <command>nix-env -qas</command> will issue an HTTP
HEAD request, since it only needs to know if the HEAD request, since it only needs to know if the
@@ -381,7 +388,7 @@ The fields are as follows:
references exist (e.g., references exist (e.g.,
<filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>), <filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>),
Nix will fetch <screen> Nix will fetch <screen>
http://nixos.org/binary-cache/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2 http://cache.nixos.org/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
</screen> and decompress and unpack it to </screen> and decompress and unpack it to
<filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para> <filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para>
+142
View File
@@ -0,0 +1,142 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-shell">
<refmeta>
<refentrytitle>nix-shell</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="source">Nix</refmiscinfo>
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
</refmeta>
<refnamediv>
<refname>nix-shell</refname>
<refpurpose>start an interactive shell based on a Nix expression</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-shell</command>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
<replaceable>attrPath</replaceable>
</arg>
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
<arg><option>--pure</option></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>The command <command>nix-shell</command> will build
the dependencies of the specified derivation, but not the derivation
itself. It will then start an interactive shell in which all
environment variables defined by the derivation have been set to their
corresponding values, and the script <literal>$stdenv/setup</literal>
has been sourced. This is useful for reproducing the environment of a
derivation for development.</para>
</refsection>
<refsection><title>Options</title>
<para>All options not listed here are passed to <command>nix-store
--realise</command>, except for <option>--arg</option> and
<option>--attr</option> / <option>-A</option> which are passed to
<command>nix-instantiate</command>. <phrase condition="manual">See
also <xref linkend="sec-common-options" />.</phrase></para>
<variablelist>
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
<listitem><para>In the environment of the derivation, run the
shell command <replaceable>cmd</replaceable> instead of starting
an interactive shell. However, if you end the shell command with
<literal>return</literal>, you still get an interactive shell.
This can be useful for doing any additional
initialisation.</para></listitem>
</varlistentry>
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
<listitem><para>Do not build any dependencies whose store path
matches the regular expression <replaceable>regexp</replaceable>.
This option may be specified multiple times.</para></listitem>
</varlistentry>
<varlistentry><term><option>--pure</option></term>
<listitem><para>If this flag is specified, the environment is
almost entirely cleared before the interactive shell is started,
so you get an environment that more closely corresponds to the
“real” Nix build. A few variables, in particular
<envar>HOME</envar>, <envar>USER</envar> and
<envar>DISPLAY</envar>, are retained. Note that
<filename>~/.bashrc</filename> and (depending on your Bash
installation) <filename>/etc/bashrc</filename> are still sourced,
so any variables set there will affect the interactive
shell.</para></listitem>
</varlistentry>
</variablelist>
<para>The following common options are supported:</para>
<variablelist condition="manpage">
<xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
</variablelist>
</refsection>
<refsection><title>Examples</title>
<para>To build the dependencies of the package Pan, and start an
interactive shell in which to build it:
<screen>
$ nix-shell '&lt;nixpkgs>' -A pan
$ unpackPhase
$ cd pan-*
$ configurePhase
$ buildPhase
$ ./pan/gui/pan
</screen>
To clear the environment first, and do some additional automatic
initialisation of the interactive shell:
<screen>
$ nix-shell '&lt;nixpkgs>' -A pan --pure \
--command 'export NIX_DEBUG=1; export NIX_CORES=8; return'
</screen>
</para>
</refsection>
<refsection condition="manpage"><title>Environment variables</title>
<variablelist>
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
</variablelist>
</refsection>
</refentry>
+4 -4
View File
@@ -164,7 +164,7 @@ the specified store paths. Realisation is a somewhat overloaded term:
exist in the file system). If the path is already valid, we are exist in the file system). If the path is already valid, we are
done immediately. Otherwise, the path and any missing paths in its done immediately. Otherwise, the path and any missing paths in its
closure may be produced through substitutes. If there are no closure may be produced through substitutes. If there are no
(succesful) subsitutes, realisation fails.</para></listitem> (successful) subsitutes, realisation fails.</para></listitem>
</itemizedlist> </itemizedlist>
@@ -852,7 +852,7 @@ in Nix itself.</para>
<refsection><title>Description</title> <refsection><title>Description</title>
<para>The operation <option>--verify-paths</option> compares the <para>The operation <option>--verify-path</option> compares the
contents of the given store paths to their cryptographic hashes stored contents of the given store paths to their cryptographic hashes stored
in Nixs database. For every changed path, it prints a warning in Nixs database. For every changed path, it prints a warning
message. The exit status is 0 if no path has changed, and 1 message. The exit status is 0 if no path has changed, and 1
@@ -1242,9 +1242,9 @@ variable <envar>_args</envar>.</para>
$ nix-store --print-env $(nix-instantiate '&lt;nixpkgs>' -A firefox) $ nix-store --print-env $(nix-instantiate '&lt;nixpkgs>' -A firefox)
<replaceable></replaceable> <replaceable></replaceable>
export src; src='/nix/store/plpj7qrwcz94z2psh6fchsi7s8yihc7k-firefox-12.0.source.tar.bz2' export src; src='/nix/store/plpj7qrwcz94z2psh6fchsi7s8yihc7k-firefox-12.0.source.tar.bz2'
export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnm-stdenv' export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnn-stdenv'
export system; system='x86_64-linux' export system; system='x86_64-linux'
export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh' export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25c-default-builder.sh'
</screen> </screen>
</refsection> </refsection>
+7 -6
View File
@@ -181,7 +181,7 @@
from, say, a network repository. If the repository is down, the from, say, a network repository. If the repository is down, the
realisation of the derivation will fail. When this option is realisation of the derivation will fail. When this option is
specified, Nix will build the derivation instead. Thus, specified, Nix will build the derivation instead. Thus,
installation from binaries falls back on nstallation from source. installation from binaries falls back on installation from source.
This option is not the default since it is generally not desirable This option is not the default since it is generally not desirable
for a transient failure in obtaining the substitutes to lead to a for a transient failure in obtaining the substitutes to lead to a
full build from source (with the related consumption of full build from source (with the related consumption of
@@ -236,7 +236,7 @@
interpreted by the <command>nix-log2xml</command> tool in the interpreted by the <command>nix-log2xml</command> tool in the
Nix source distribution. The resulting XML file can be fed into Nix source distribution. The resulting XML file can be fed into
the <command>log2html.xsl</command> stylesheet to create an HTML the <command>log2html.xsl</command> stylesheet to create an HTML
file that can be browsed interactively, using Javascript to file that can be browsed interactively, using JavaScript to
expand and collapse parts of the output.</para></listitem> expand and collapse parts of the output.</para></listitem>
</varlistentry> </varlistentry>
@@ -343,10 +343,11 @@
<varlistentry><term><option>-I</option> <replaceable>path</replaceable></term> <varlistentry><term><option>-I</option> <replaceable>path</replaceable></term>
<listitem><para>Add a path to the Nix expression search path. See <listitem><para>Add a path to the Nix expression search path. This
the <envar>NIX_PATH</envar> environment variable for details. Paths option may be given multiple times. See the <envar>NIX_PATH</envar>
added through <option>-I</option> take precedence over environment variable for information on the semantics of the Nix
<envar>NIX_PATH</envar>.</para></listitem> search path. Paths added through <option>-I</option> take
precedence over <envar>NIX_PATH</envar>.</para></listitem>
</varlistentry> </varlistentry>
+5 -5
View File
@@ -129,7 +129,7 @@ downloading binaries from <systemitem
class='fqdomainname'>nixos.org</systemitem>, instead of building class='fqdomainname'>nixos.org</systemitem>, instead of building
them from source. This might still take a while since all them from source. This might still take a while since all
dependencies must be downloaded, but on a reasonably fast connection dependencies must be downloaded, but on a reasonably fast connection
such as an DSL line its on the order of a few minutes.</para> such as a DSL line its on the order of a few minutes.</para>
<para>Naturally, packages can also be uninstalled: <para>Naturally, packages can also be uninstalled:
@@ -225,7 +225,7 @@ uses is to create directory trees of symlinks to
themselves (though automatically generated by themselves (though automatically generated by
<command>nix-env</command>), so they too reside in the Nix store. For <command>nix-env</command>), so they too reside in the Nix store. For
instance, in <xref linkend='fig-user-environments' /> the user instance, in <xref linkend='fig-user-environments' /> the user
environment <filename>/nix/store/5mq2jcn36ldl...-user-env</filename> environment <filename>/nix/store/0c1p5z4kda11...-user-env</filename>
contains a symlink to just Subversion 1.1.2 (arrows in the figure contains a symlink to just Subversion 1.1.2 (arrows in the figure
indicate symlinks). This would be what we would obtain if we had done indicate symlinks). This would be what we would obtain if we had done
@@ -538,7 +538,7 @@ dependencies) to a file, and then unpack that file into another Nix
store. For example, store. For example,
<screen> <screen>
$ nix-store --export $(type -p firefox) > firefox.closure</screen> $ nix-store --export $(nix-store -qR $(type -p firefox)) > firefox.closure</screen>
writes the closure of Firefox to a file. You can then copy this file writes the closure of Firefox to a file. You can then copy this file
to another machine and install the closure: to another machine and install the closure:
@@ -552,7 +552,7 @@ another command, e.g. to copy and install a closure directly to/on
another machine: another machine:
<screen> <screen>
$ nix-store --export $(type -p firefox) | bzip2 | \ $ nix-store --export $(nix-store -qR $(type -p firefox)) | bzip2 | \
ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen> ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen>
But note that <command>nix-copy-closure</command> is generally more But note that <command>nix-copy-closure</command> is generally more
@@ -583,7 +583,7 @@ $ nix-env -i firefox</screen>
and Nix has to build a path that it sees is already present in and Nix has to build a path that it sees is already present in
<filename>/mnt/nix</filename>, then it will just copy from there <filename>/mnt/nix</filename>, then it will just copy from there
instead of building it from source.</para> instead of building it from source.</para>
</section> </section>
+5 -4
View File
@@ -14,9 +14,10 @@ to the following chapters.</para>
<listitem><para>Download a source tarball or RPM or Debian/Ubuntu <listitem><para>Download a source tarball or RPM or Debian/Ubuntu
package from <link xlink:href='http://nixos.org/'/>. Build source package from <link xlink:href='http://nixos.org/'/>. Build source
distributions using the regular sequence: distributions using the regular sequence:
<screen> <screen>
$ tar xvfj nix-<replaceable>version</replaceable>.tar.bz2 $ tar xvfj nix-<replaceable>version</replaceable>.tar.bz2
$ cd nix-<replaceable>version</replaceable>
$ ./configure $ ./configure
$ make $ make
$ make install <lineannotation>(as root)</lineannotation></screen> $ make install <lineannotation>(as root)</lineannotation></screen>
@@ -35,7 +36,7 @@ changed using
<listitem><para>You should add <listitem><para>You should add
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename> <filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
to your <filename>~/.bashrc</filename> (or some other login to your <filename>~/.profile</filename> (or some other login
file).</para></listitem> file).</para></listitem>
<listitem><para>Subscribe to the Nix Packages channel. <listitem><para>Subscribe to the Nix Packages channel.
@@ -69,7 +70,7 @@ libxslt-1.1.0
</para></listitem> </para></listitem>
<listitem><para>Install some packages from the channel: <listitem><para>Install some packages from the channel:
<screen> <screen>
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen> $ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
@@ -87,7 +88,7 @@ $ firefox
<lineannotation>(read Slashdot or something)</lineannotation></screen> <lineannotation>(read Slashdot or something)</lineannotation></screen>
</para></listitem> </para></listitem>
<listitem><para>Uninstall a package: <listitem><para>Uninstall a package:
<screen> <screen>
+330 -7
View File
@@ -5,6 +5,329 @@
<title>Nix Release Notes</title> <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 (September 10, 2013)</title>
<para>In addition to the usual bug fixes, this release has several new
features:</para>
<itemizedlist>
<listitem>
<para>The command <command>nix-build --run-env</command> has been
renamed to <command>nix-shell</command>.</para>
</listitem>
<listitem>
<para><command>nix-shell</command> now sources
<filename>$stdenv/setup</filename> <emphasis>inside</emphasis> the
interactive shell, rather than in a parent shell. This ensures
that shell functions defined by <literal>stdenv</literal> can be
used in the interactive shell.</para>
</listitem>
<listitem>
<para><command>nix-shell</command> has a new flag
<option>--pure</option> to clear the environment, so you get an
environment that more closely corresponds to the “real” Nix build.
</para>
</listitem>
<listitem>
<para><command>nix-shell</command> now sets the shell prompt
(<envar>PS1</envar>) to ensure that Nix shells are distinguishable
from your regular shells.</para>
</listitem>
<listitem>
<para><command>nix-env</command> no longer requires a
<literal>*</literal> argument to match all packages, so
<literal>nix-env -qa</literal> is equivalent to <literal>nix-env
-qa '*'</literal>.</para>
</listitem>
<listitem>
<para><command>nix-env -i</command> has a new flag
<option>--remove-all</option> (<option>-r</option>) to remove all
previous packages from the profile. This makes it easier to do
declarative package management similar to NixOSs
<option>environment.systemPackages</option>. For instance, if you
have a specification <filename>my-packages.nix</filename> like this:
<programlisting>
with import &lt;nixpkgs> {};
[ thunderbird
geeqie
...
]
</programlisting>
then after any change to this file, you can run:
<screen>
$ nix-env -f my-packages.nix -ir
</screen>
to update your profile to match the specification.</para>
</listitem>
<listitem>
<para>The <literal>with</literal> language construct is now more
lazy. It only evaluates its argument if a variable might actually
refer to an attribute in the argument. For instance, this now
works:
<programlisting>
let
pkgs = with pkgs; { foo = "old"; bar = foo; } // overrides;
overrides = { foo = "new"; };
in pkgs.bar
</programlisting>
This evaluates to <literal>"new"</literal>, while previously it
gave an “infinite recursion” error.</para>
</listitem>
<listitem>
<para>Nix now has proper integer arithmetic operators. For
instance, you can write <literal>x + y</literal> instead of
<literal>builtins.add x y</literal>, or <literal>x &lt;
y</literal> instead of <literal>builtins.lessThan x y</literal>.
The comparison operators also work on strings.</para>
</listitem>
<listitem>
<para>On 64-bit systems, Nix integers are now 64 bits rather than
32 bits.</para>
</listitem>
<listitem>
<para>When using the Nix daemon, the <command>nix-daemon</command>
worker process now runs on the same CPU as the client, on systems
that support setting CPU affinity. This gives a significant speedup
on some systems.</para>
</listitem>
<listitem>
<para>If a stack overflow occurs in the Nix evaluator, you now get
a proper error message (rather than “Segmentation fault”) on some
systems.</para>
</listitem>
<listitem>
<para>In addition to directories, you can now bind-mount regular
files in chroots through the (now misnamed) option
<option>build-chroot-dirs</option>.</para>
</listitem>
</itemizedlist>
<para>This release has contributions from Domen Kožar, Eelco Dolstra,
Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
Levy.</para>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.5.3"><title>Release 1.5.3 (June 17, 2013)</title>
<para>This is primarily a bug fix release. The following changes are
noteworthy:</para>
<itemizedlist>
<listitem>
<para>Yet another security bug involving hard links to files
outside the store was fixed. This bug only affected multi-user
installations that do not have hard link restrictions
enabled. (NixOS is thus not vulnerable.)</para>
</listitem>
<listitem>
<para>The default binary cache URL has changed from
<uri>http://nixos.org/binary-cache</uri> to
<uri>http://cache.nixos.org</uri>. The latter is hosted on Amazon
CloudFront (courtesy of <link
xlink:href="http://www.logicblox.com/">LogicBlox</link>) and
should provide better performance for users in both Europe and
North America.</para>
</listitem>
<listitem>
<para>The binary cache substituter now prints a warning message if
fetching information from the cache takes more than five seconds.
Thus network or server problems no longer make Nix appear to just
hang.</para>
</listitem>
<listitem>
<para>Stack traces now show function names, e.g.
<screen>
while evaluating `concatMapStrings' at `<replaceable>...</replaceable>/nixpkgs/pkgs/lib/strings.nix:18:25':
</screen>
Also, if a function is called with an unexpected argument, Nix
now shows the name of the argument.
</para>
</listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.5.2"><title>Release 1.5.2 (May 13, 2013)</title>
<para>This is primarily a bug fix release. It has contributions from
Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.5.1"><title>Release 1.5.1 (February 28, 2013)</title>
<para>The bug fix to the bug fix had a bug itself, of course. But
this time it will work for sure!</para>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.5"><title>Release 1.5 (February 27, 2013)</title>
<para>This is a brown paper bag release to fix a regression introduced
by the hard link security fix in 1.4.</para>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.4"><title>Release 1.4 (February 26, 2013)</title>
<para>This release fixes a security bug in multi-user operation. It
was possible for derivations to cause the mode of files outside of the
Nix store to be changed to 444 (read-only but world-readable) by
creating hard links to those files (<link
xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a79141ac4">details</link>).</para>
<para>There are also the following improvements:</para>
<itemizedlist>
<listitem><para>New built-in function:
<function>builtins.hashString</function>.</para></listitem>
<listitem><para>Build logs are now stored in
<filename>/nix/var/log/nix/drvs/<replaceable>XX</replaceable>/</filename>,
where <replaceable>XX</replaceable> is the first two characters of
the derivation. This is useful on machines that keep a lot of build
logs (such as Hydra servers).</para></listitem>
<listitem><para>The function <function>corepkgs/fetchurl</function>
can now make the downloaded file executable. This will allow
getting rid of all bootstrap binaries in the Nixpkgs source
tree.</para></listitem>
<listitem><para>Language change: The expression <literal>"${./path}
..."</literal> now evaluates to a string instead of a
path.</para></listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.3"><title>Release 1.3 (January 4, 2013)</title>
<para>This is primarily a bug fix release. When this version is first
run on Linux, it removes any immutable bits from the Nix store and
increases the schema version of the Nix store. (The previous release
removed support for setting the immutable bit; this release clears any
remaining immutable bits to make certain operations more
efficient.)</para>
<para>This release has contributions from Eelco Dolstra and Stuart
Pernsteiner.</para>
</section>
<!--==================================================================--> <!--==================================================================-->
@@ -23,10 +346,10 @@
configuration setting <option>binary-caches</option> contains a configuration setting <option>binary-caches</option> contains a
list of URLs of binary caches. For instance, doing list of URLs of binary caches. For instance, doing
<screen> <screen>
$ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache $ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
</screen> </screen>
will install Thunderbird and its dependencies, using the available will install Thunderbird and its dependencies, using the available
pre-built binaries in <uri>http://nixos.org/binary-cache</uri>. pre-built binaries in <uri>http://cache.nixos.org</uri>.
The main advantage over the old “manifest”-based method of getting The main advantage over the old “manifest”-based method of getting
pre-built binaries is that you dont have to worry about your pre-built binaries is that you dont have to worry about your
manifest being in sync with the Nix expressions youre installing manifest being in sync with the Nix expressions youre installing
@@ -39,7 +362,7 @@ $ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
used automatically if you subscribe to that channel. If you use used automatically if you subscribe to that channel. If you use
the Nixpkgs or NixOS channels the Nixpkgs or NixOS channels
(<uri>http://nixos.org/channels</uri>) you automatically get the (<uri>http://nixos.org/channels</uri>) you automatically get the
cache <uri>http://nixos.org/binary-cache</uri>.</para> cache <uri>http://cache.nixos.org</uri>.</para>
<para>Binary caches are created using <command>nix-push</command>. <para>Binary caches are created using <command>nix-push</command>.
For details on the operation and format of binary caches, see the For details on the operation and format of binary caches, see the
@@ -1189,7 +1512,7 @@ irreversible.</para></warning>
</para></listitem> </para></listitem>
<listitem><para>You can now unambigously specify which derivation to <listitem><para>You can now unambiguously specify which derivation to
build or install in <command>nix-env</command>, build or install in <command>nix-env</command>,
<command>nix-instantiate</command> and <command>nix-build</command> <command>nix-instantiate</command> and <command>nix-build</command>
using the <option>--attr</option> / <option>-A</option> flags, which using the <option>--attr</option> / <option>-A</option> flags, which
@@ -1329,7 +1652,7 @@ irreversible.</para></warning>
<itemizedlist> <itemizedlist>
<listitem><para>Make sure that the garbage collector can run <listitem><para>Make sure that the garbage collector can run
succesfully when the disk is full successfully when the disk is full
(<literal>NIX-18</literal>).</para></listitem> (<literal>NIX-18</literal>).</para></listitem>
<listitem><para><command>nix-env</command> now locks the profile <listitem><para><command>nix-env</command> now locks the profile
@@ -1684,7 +2007,7 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
</itemizedlist></para></listitem> </itemizedlist></para></listitem>
<listitem><para>A hash of the contents of a store path is now stored <listitem><para>A hash of the contents of a store path is now stored
in the database after a succesful build. This allows you to check in the database after a successful build. This allows you to check
whether store paths have been tampered with: <command>nix-store whether store paths have been tampered with: <command>nix-store
--verify --check-contents</command>.</para></listitem> --verify --check-contents</command>.</para></listitem>
@@ -1848,7 +2171,7 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
<itemizedlist> <itemizedlist>
<listitem><para>Derivations for other platforms are filtered out <listitem><para>Derivations for other platforms are filtered out
(which can be overriden using (which can be overridden using
<option>--system-filter</option>).</para></listitem> <option>--system-filter</option>).</para></listitem>
<listitem><para><option>--install</option> by default now <listitem><para><option>--install</option> by default now
+2 -2
View File
@@ -17,7 +17,7 @@ bug tracker</link> for a list of currently known issues.</para>
$ nix-env -i docbook-xml $ nix-env -i docbook-xml
... ...
adding /nix/store/s5hyxgm62gk2...-docbook-xml-4.2 adding /nix/store/s5hyxgm62gk2...-docbook-xml-4.2
collission between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd' collision between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd'
and `/nix/store/06h377hr4b33...-docbook-xml-4.3/xml/dtd/docbook/calstblx.dtd' and `/nix/store/06h377hr4b33...-docbook-xml-4.3/xml/dtd/docbook/calstblx.dtd'
at /nix/store/...-builder.pl line 62.</screen> at /nix/store/...-builder.pl line 62.</screen>
@@ -68,7 +68,7 @@ in <filename>/nix/store</filename>, as can be seen using <command>ls
$ ls -l /nix/store $ ls -l /nix/store
drwxrwxrwt 32000 nix nix 4620288 Sep 8 15:08 store</screen> drwxrwxrwt 32000 nix nix 4620288 Sep 8 15:08 store</screen>
The <literal>ext2</literal> file system is limited to a inode link The <literal>ext2</literal> file system is limited to an inode link
count of 32,000 (each subdirectory increasing the count by one). count of 32,000 (each subdirectory increasing the count by one).
Furthermore, the <literal>st_nlink</literal> field of the Furthermore, the <literal>st_nlink</literal> field of the
<function>stat</function> system call is a 16-bit value.</para> <function>stat</function> system call is a 16-bit value.</para>
+65 -71
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 For more extensive information on adding packages to the Nix Packages
collection (such as functions in the standard environment and coding collection (such as functions in the standard environment and coding
conventions), please consult <link 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> manual</link>.</para></note>
@@ -118,10 +118,10 @@ the single Nix expression in that directory
<varname>stdenv.mkDerivation</varname>. <varname>stdenv.mkDerivation</varname>.
<varname>mkDerivation</varname> is a function provided by <varname>mkDerivation</varname> is a function provided by
<varname>stdenv</varname> that builds a package from a set of <varname>stdenv</varname> that builds a package from a set of
<emphasis>attributes</emphasis>. An attribute set is just a list <emphasis>attributes</emphasis>. A set is just a list of
of key/value pairs where each value is an arbitrary Nix key/value pairs where each key is a string and each value is an
expression. They take the general form arbitrary Nix expression. They take the general form <literal>{
<literal>{ <replaceable>name1</replaceable> = <replaceable>name1</replaceable> =
<replaceable>expr1</replaceable>; <replaceable>...</replaceable> <replaceable>expr1</replaceable>; <replaceable>...</replaceable>
<replaceable>nameN</replaceable> = <replaceable>nameN</replaceable> =
<replaceable>exprN</replaceable>; }</literal>.</para> <replaceable>exprN</replaceable>; }</literal>.</para>
@@ -384,9 +384,9 @@ some fragments of
<para>This is where the actual composition takes place. Here we <para>This is where the actual composition takes place. Here we
<emphasis>call</emphasis> the function imported from <emphasis>call</emphasis> the function imported from
<filename>../applications/misc/hello/ex-1</filename> with an <filename>../applications/misc/hello/ex-1</filename> with a set
attribute set containing the things that the function expects, containing the things that the function expects, namely
namely <varname>fetchurl</varname>, <varname>stdenv</varname>, and <varname>fetchurl</varname>, <varname>stdenv</varname>, and
<varname>perl</varname>. We use inherit again to use the <varname>perl</varname>. We use inherit again to use the
attributes defined in the surrounding scope (we could also have attributes defined in the surrounding scope (we could also have
written <literal>fetchurl = fetchurl;</literal>, etc.).</para> written <literal>fetchurl = fetchurl;</literal>, etc.).</para>
@@ -617,7 +617,7 @@ Laziness means that arguments to functions are evaluated only when
they are needed. Functional means that functions are they are needed. Functional means that functions are
<quote>normal</quote> values that can be passed around and manipulated <quote>normal</quote> values that can be passed around and manipulated
in interesting ways. The language is not a full-featured, general in interesting ways. The language is not a full-featured, general
purpose language. It's main job is to describe packages, purpose language. Its main job is to describe packages,
compositions of packages, and the variability within compositions of packages, and the variability within
packages.</para> packages.</para>
@@ -701,7 +701,7 @@ configureFlags = "
number of spaces equal to the minimal indentation of the string as number of spaces equal to the minimal indentation of the string as
a whole (disregarding the indentation of empty lines). For a whole (disregarding the indentation of empty lines). For
instance, the first and second line are indented two space, while 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 stripped from each line, so the resulting string is
<programlisting> <programlisting>
@@ -714,18 +714,18 @@ configureFlags = "
text on the initial line.</para> text on the initial line.</para>
<para>Antiquotation <para>Antiquotation
(<literal>${<replaceable>expr</replaceable>}}</literal>) is (<literal>${<replaceable>expr</replaceable>}</literal>) is
supported in indented strings.</para> supported in indented strings.</para>
<para>Since <literal>${</literal> and <literal>''</literal> have <para>Since <literal>${</literal> and <literal>''</literal> have
special meaning in indented strings, you need a way to quote them. special meaning in indented strings, you need a way to quote them.
<literal>${</literal> can be escaped by prefixing it with <literal>${</literal> can be escaped by prefixing it with
<literal>''</literal>, i.e., <literal>''${</literal>. <literal>''</literal> (that is, two single quotes), i.e.,
<literal>''</literal> can be escaped by prefixing it with <literal>''${</literal>. <literal>''</literal> can be escaped by
<literal>'</literal>, i.e., <literal>'''</literal>. Finally, prefixing it with <literal>'</literal>, i.e.,
linefeed, carriage-return and tab characters can be writted as <literal>'''</literal>. Finally, linefeed, carriage-return and
<literal>''\n</literal>, <literal>''\r</literal>, tab characters can be written as <literal>''\n</literal>,
<literal>''\t</literal>.</para> <literal>''\r</literal>, <literal>''\t</literal>.</para>
<para>Indented strings are primarily useful in that they allow <para>Indented strings are primarily useful in that they allow
multi-line string literals to follow the indentation of the multi-line string literals to follow the indentation of the
@@ -775,7 +775,7 @@ stdenv.mkDerivation {
absolute at parse time relative to the directory of the Nix absolute at parse time relative to the directory of the Nix
expression that contained it. For instance, if a Nix expression in expression that contained it. For instance, if a Nix expression in
<filename>/foo/bar/bla.nix</filename> refers to <filename>/foo/bar/bla.nix</filename> refers to
<filename>../xyzzy/fnord.nix</filename>, the absolutised path is <filename>../xyzzy/fnord.nix</filename>, the absolute path is
<filename>/foo/xyzzy/fnord.nix</filename>.</para></listitem> <filename>/foo/xyzzy/fnord.nix</filename>.</para></listitem>
<listitem><para><emphasis>Booleans</emphasis> with values <listitem><para><emphasis>Booleans</emphasis> with values
@@ -805,20 +805,21 @@ to be enclosed in parentheses. If they had been omitted, e.g.,
[ 123 ./foo.nix "abc" f { x = y; } ]</programlisting> [ 123 ./foo.nix "abc" f { x = y; } ]</programlisting>
the result would be a list of five elements, the fourth one being a 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>
<simplesect><title>Attribute sets</title> <simplesect><title>Sets</title>
<para>Attribute sets are really the core of the language, since <para>Sets are really the core of the language, since ultimately the
ultimately it's all about creating derivations, which are really just Nix language is all about creating derivations, which are really just
sets of attributes to be passed to build scripts.</para> sets of attributes to be passed to build scripts.</para>
<para>Attribute sets are just a list of name/value pairs enclosed in <para>Sets are just a list of name/value pairs (called
curly brackets, where each value is an arbitrary expression terminated <emphasis>attributes</emphasis>) enclosed in curly brackets, where
by a semicolon. For example: each value is an arbitrary expression terminated by a semicolon. For
example:
<programlisting> <programlisting>
{ x = 123; { x = 123;
@@ -826,12 +827,12 @@ by a semicolon. For example:
y = f { bla = 456; }; y = f { bla = 456; };
}</programlisting> }</programlisting>
This defines an attribute set with attributes named This defines a set with attributes named <varname>x</varname>,
<varname>x</varname>, <varname>test</varname>, <varname>y</varname>. <varname>text</varname>, <varname>y</varname>. The order of the
The order of the attributes is irrelevant. An attribute name may only attributes is irrelevant. An attribute name may only occur
occur once.</para> 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, <literal>.</literal> operator. For instance,
<programlisting> <programlisting>
@@ -864,10 +865,10 @@ This will evaluate to <literal>123</literal>.</para>
<section><title>Language constructs</title> <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 <para>Recursive sets are just normal sets, but the attributes can
attributes can refer to each other. For example, refer to each other. For example,
<programlisting> <programlisting>
rec { rec {
@@ -880,11 +881,11 @@ evaluates to <literal>123</literal>. Note that without
<literal>rec</literal> the binding <literal>x = y;</literal> would <literal>rec</literal> the binding <literal>x = y;</literal> would
refer to the variable <varname>y</varname> in the surrounding scope, refer to the variable <varname>y</varname> in the surrounding scope,
if one exists, and would be invalid if no such variable exists. That if one exists, and would be invalid if no such variable exists. That
is, in a normal (non-recursive) attribute set, attributes are not is, in a normal (non-recursive) set, attributes are not added to the
added to the lexical scope; in a recursive set, they are.</para> lexical scope; in a recursive set, they are.</para>
<para>Recursive attribute sets of course introduce the danger of <para>Recursive sets of course introduce the danger of infinite
infinite recursion. For example, recursion. For example,
<programlisting> <programlisting>
rec { rec {
@@ -901,8 +902,8 @@ encountered</quote>).</para></footnote>.</para>
<simplesect><title>Let-expressions</title> <simplesect><title>Let-expressions</title>
<para>A let-expression allows you define local <para>A let-expression allows you define local variables for an
variables for an expression. For instance, expression. For instance,
<programlisting> <programlisting>
let let
@@ -914,20 +915,14 @@ evaluates to <literal>"foobar"</literal>.
</para> </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>
<simplesect><title>Inheriting attributes</title> <simplesect><title>Inheriting attributes</title>
<para>When defining an attribute set it is often convenient to copy <para>When defining a set it is often convenient to copy variables
variables from the surrounding lexical scope (e.g., when you want to from the surrounding lexical scope (e.g., when you want to propagate
propagate attributes). This can be shortened using the attributes). This can be shortened using the
<literal>inherit</literal> keyword. For instance, <literal>inherit</literal> keyword. For instance,
<programlisting> <programlisting>
@@ -936,10 +931,10 @@ let x = 123; in
y = 456; y = 456;
}</programlisting> }</programlisting>
evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that this evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that
works because <varname>x</varname> is added to the lexical scope by this works because <varname>x</varname> is added to the lexical scope
the <literal>let</literal> construct.) It is also possible to inherit by the <literal>let</literal> construct.) It is also possible to
attributes from another attribute set. For instance, in this fragment inherit attributes from another set. For instance, in this fragment
from <filename>all-packages.nix</filename>, from <filename>all-packages.nix</filename>,
<programlisting> <programlisting>
@@ -958,13 +953,12 @@ from <filename>all-packages.nix</filename>,
libjpg = ...; libjpg = ...;
...</programlisting> ...</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 <filename>../tools/graphics/graphviz</filename> inherits a number of
variables from the surrounding scope (<varname>fetchurl</varname> variables from the surrounding scope (<varname>fetchurl</varname>
... <varname>yacc</varname>), but also inherits ... <varname>yacc</varname>), but also inherits
<varname>libXaw</varname> (the X Athena Widgets) from the <varname>libXaw</varname> (the X Athena Widgets) from the
<varname>xlibs</varname> (X11 client-side libraries) attribute <varname>xlibs</varname> (X11 client-side libraries) set.</para>
set.</para>
</simplesect> </simplesect>
@@ -1003,11 +997,11 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
"fooabc" ]</literal>.</para></listitem> "fooabc" ]</literal>.</para></listitem>
<listitem><para>An <emphasis>attribute set pattern</emphasis> of the <listitem><para>A <emphasis>set pattern</emphasis> of the form
form <literal>{ name1, name2, …, nameN }</literal> <literal>{ name1, name2, …, nameN }</literal> matches a set
matches an attribute set containing the listed attributes, and binds containing the listed attributes, and binds the values of those
the values of those attributes to variables in the function body. attributes to variables in the function body. For example, the
For example, the function function
<programlisting> <programlisting>
{ x, y, z }: z + y + x</programlisting> { x, y, z }: z + y + x</programlisting>
@@ -1174,9 +1168,8 @@ used in the Nix expression for Subversion.</para>
<programlisting> <programlisting>
with <replaceable>e1</replaceable>; <replaceable>e2</replaceable></programlisting> with <replaceable>e1</replaceable>; <replaceable>e2</replaceable></programlisting>
introduces the attribute set <replaceable>e1</replaceable> into the introduces the set <replaceable>e1</replaceable> into the lexical
lexical scope of the expression <replaceable>e2</replaceable>. For scope of the expression <replaceable>e2</replaceable>. For instance,
instance,
<programlisting> <programlisting>
let as = { x = "foo"; y = "bar"; }; let as = { x = "foo"; y = "bar"; };
@@ -1235,7 +1228,7 @@ weakest binding).</para>
</entry> </entry>
<entry>none</entry> <entry>none</entry>
<entry>Select attribute denoted by the attribute path <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 <replaceable>e</replaceable>. (An attribute path is a
dot-separated list of attribute names.) If the attribute dot-separated list of attribute names.) If the attribute
doesnt exist, return <replaceable>def</replaceable> if doesnt exist, return <replaceable>def</replaceable> if
@@ -1251,8 +1244,8 @@ weakest binding).</para>
<entry><replaceable>e</replaceable> <literal>?</literal> <entry><replaceable>e</replaceable> <literal>?</literal>
<replaceable>attrpath</replaceable></entry> <replaceable>attrpath</replaceable></entry>
<entry>none</entry> <entry>none</entry>
<entry>Test whether attribute set <replaceable>e</replaceable> <entry>Test whether set <replaceable>e</replaceable> contains
contains the attribute denoted by <replaceable>attrpath</replaceable>; the attribute denoted by <replaceable>attrpath</replaceable>;
return <literal>true</literal> or return <literal>true</literal> or
<literal>false</literal>.</entry> <literal>false</literal>.</entry>
</row> </row>
@@ -1275,10 +1268,11 @@ weakest binding).</para>
<entry><replaceable>e1</replaceable> <literal>//</literal> <entry><replaceable>e1</replaceable> <literal>//</literal>
<replaceable>e2</replaceable></entry> <replaceable>e2</replaceable></entry>
<entry>right</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>e1</replaceable> and
<replaceable>e2</replaceable> (with the latter taking <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>
<row> <row>
<entry><replaceable>e1</replaceable> <literal>==</literal> <entry><replaceable>e1</replaceable> <literal>==</literal>
@@ -1322,9 +1316,9 @@ weakest binding).</para>
<section xml:id="ssec-derivation"><title>Derivations</title> <section xml:id="ssec-derivation"><title>Derivations</title>
<para>The most important built-in function is <para>The most important built-in function is
<function>derivation</function>, which is used to describe a <function>derivation</function>, which is used to describe a single
single derivation (a build action). It takes as input an attribute derivation (a build action). It takes as input a set, the attributes
set, the attributes of which specify the inputs of the build.</para> of which specify the inputs of the build.</para>
<itemizedlist> <itemizedlist>
+1
View File
@@ -22,6 +22,7 @@ Requires: /usr/bin/perl
Requires: curl Requires: curl
Requires: perl-DBD-SQLite Requires: perl-DBD-SQLite
Requires: bzip2 Requires: bzip2
Requires: gzip
Requires: xz Requires: xz
BuildRequires: bzip2-devel BuildRequires: bzip2-devel
BuildRequires: sqlite-devel BuildRequires: sqlite-devel
+9 -5
View File
@@ -3,16 +3,13 @@ package Nix::CopyClosure;
use strict; use strict;
use Nix::Config; use Nix::Config;
use Nix::Store; use Nix::Store;
use List::Util qw(sum);
sub copyTo { sub copyTo {
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor, my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor,
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_; $includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_;
$compressor = "$compressor |" if $compressor ne "";
$decompressor = "$decompressor |" if $decompressor ne "";
$progressViewer = "$progressViewer |" if $progressViewer ne "";
# Get the closure of this path. # Get the closure of this path.
my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs, my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
map { followLinksToStorePath $_ } @{$storePaths}))); map { followLinksToStorePath $_ } @{$storePaths})));
@@ -28,21 +25,28 @@ sub copyTo {
# we'll want to use --from-stdin, but we can't rely on the # we'll want to use --from-stdin, but we can't rely on the
# target having this option yet. # target having this option yet.
my @missing = (); my @missing = ();
my $missingSize = 0;
while (scalar(@closure) > 0) { while (scalar(@closure) > 0) {
my @ps = splice(@closure, 0, 1500); my @ps = splice(@closure, 0, 1500);
open(READ, "set -f; ssh $sshHost @{$sshOpts} nix-store --check-validity --print-invalid @ps|"); open(READ, "set -f; ssh $sshHost @{$sshOpts} nix-store --check-validity --print-invalid @ps|");
while (<READ>) { while (<READ>) {
chomp; chomp;
push @missing, $_; push @missing, $_;
my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($_, 1);
$missingSize += $narSize;
} }
close READ or die; close READ or die;
} }
$compressor = "$compressor |" if $compressor ne "";
$decompressor = "$decompressor |" if $decompressor ne "";
$progressViewer = "$progressViewer -s $missingSize |" if $progressViewer ne "";
# Export the store paths and import them on the remote machine. # Export the store paths and import them on the remote machine.
if (scalar @missing > 0) { if (scalar @missing > 0) {
print STDERR "copying ", scalar @missing, " missing paths to $sshHost...\n"; print STDERR "copying ", scalar @missing, " missing paths to $sshHost...\n";
unless ($dryRun) { unless ($dryRun) {
open SSH, "| $compressor $progressViewer ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die; open SSH, "| $progressViewer $compressor ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
exportPaths(fileno(SSH), $sign, @missing); exportPaths(fileno(SSH), $sign, @missing);
close SSH or die "copying store paths to remote machine `$sshHost' failed: $?"; close SSH or die "copying store paths to remote machine `$sshHost' failed: $?";
} }
+5 -1
View File
@@ -2,6 +2,7 @@ package Nix::Manifest;
use strict; use strict;
use DBI; use DBI;
use DBD::SQLite;
use Cwd; use Cwd;
use File::stat; use File::stat;
use File::Path; use File::Path;
@@ -227,6 +228,9 @@ sub writeManifest {
sub updateManifestDB { sub updateManifestDB {
my $manifestDir = $Nix::Config::manifestDir; my $manifestDir = $Nix::Config::manifestDir;
my @manifests = glob "$manifestDir/*.nixmanifest";
return undef if scalar @manifests == 0;
mkpath($manifestDir); mkpath($manifestDir);
unlink "$manifestDir/cache.sqlite"; # remove obsolete cache unlink "$manifestDir/cache.sqlite"; # remove obsolete cache
@@ -311,7 +315,7 @@ EOF
# unless we've already done so on a previous run. # unless we've already done so on a previous run.
my %seen; my %seen;
for my $manifestLink (glob "$manifestDir/*.nixmanifest") { for my $manifestLink (@manifests) {
my $manifest = Cwd::abs_path($manifestLink); my $manifest = Cwd::abs_path($manifestLink);
next unless -f $manifest; next unless -f $manifest;
my $timestamp = lstat($manifest)->mtime; my $timestamp = lstat($manifest)->mtime;
+1
View File
@@ -44,6 +44,7 @@ sub closeSSHConnection {
if ($sshStarted) { if ($sshStarted) {
system("ssh $sshHost @sshOpts -O exit 2> /dev/null") == 0 system("ssh $sshHost @sshOpts -O exit 2> /dev/null") == 0
or warn "unable to stop SSH master: $?"; or warn "unable to stop SSH master: $?";
$sshStarted = 0;
} }
} }
+17 -17
View File
@@ -42,50 +42,50 @@ if ($Nix::Config::useBindings) {
use File::Temp; use File::Temp;
use Fcntl qw/F_SETFD/; use Fcntl qw/F_SETFD/;
sub hashFile { *hashFile = sub {
my ($algo, $base32, $path) = @_; my ($algo, $base32, $path) = @_;
my $res = backtick("$Nix::Config::binDir/nix-hash", "--flat", $path, "--type", $algo, $base32 ? "--base32" : ()); my $res = backtick("$Nix::Config::binDir/nix-hash", "--flat", $path, "--type", $algo, $base32 ? "--base32" : ());
chomp $res; chomp $res;
return $res; return $res;
} };
sub hashPath { *hashPath = sub {
my ($algo, $base32, $path) = @_; my ($algo, $base32, $path) = @_;
my $res = backtick("$Nix::Config::binDir/nix-hash", $path, "--type", $algo, $base32 ? "--base32" : ()); my $res = backtick("$Nix::Config::binDir/nix-hash", $path, "--type", $algo, $base32 ? "--base32" : ());
chomp $res; chomp $res;
return $res; return $res;
} };
sub hashString { *hashString = sub {
my ($algo, $base32, $s) = @_; my ($algo, $base32, $s) = @_;
my $fh = File::Temp->new(); my $fh = File::Temp->new();
print $fh $s; print $fh $s;
my $res = backtick("$Nix::Config::binDir/nix-hash", $fh->filename, "--type", $algo, $base32 ? "--base32" : ()); my $res = backtick("$Nix::Config::binDir/nix-hash", $fh->filename, "--type", $algo, $base32 ? "--base32" : ());
chomp $res; chomp $res;
return $res; return $res;
} };
sub addToStore { *addToStore = sub {
my ($srcPath, $recursive, $algo) = @_; my ($srcPath, $recursive, $algo) = @_;
die "not implemented" if $recursive || $algo ne "sha256"; die "not implemented" if $recursive || $algo ne "sha256";
my $res = backtick("$Nix::Config::binDir/nix-store", "--add", $srcPath); my $res = backtick("$Nix::Config::binDir/nix-store", "--add", $srcPath);
chomp $res; chomp $res;
return $res; return $res;
} };
sub isValidPath { *isValidPath = sub {
my ($path) = @_; my ($path) = @_;
my $res = backtick("$Nix::Config::binDir/nix-store", "--check-validity", "--print-invalid", $path); my $res = backtick("$Nix::Config::binDir/nix-store", "--check-validity", "--print-invalid", $path);
chomp $res; chomp $res;
return $res ne $path; return $res ne $path;
} };
sub queryPathHash { *queryPathHash = sub {
my ($path) = @_; my ($path) = @_;
my $res = backtick("$Nix::Config::binDir/nix-store", "--query", "--hash", $path); my $res = backtick("$Nix::Config::binDir/nix-store", "--query", "--hash", $path);
chomp $res; chomp $res;
return $res; return $res;
} };
} }
1; 1;
+13 -9
View File
@@ -15,11 +15,12 @@
using namespace nix; using namespace nix;
void doInit() void doInit()
{ {
if (!store) { if (!store) {
try { try {
settings.processEnvironment(); settings.processEnvironment();
settings.lockCPU = false;
store = openStore(); store = openStore();
} catch (Error & e) { } catch (Error & e) {
croak(e.what()); croak(e.what());
@@ -237,32 +238,35 @@ SV * derivationFromPath(char * drvPath)
doInit(); doInit();
Derivation drv = derivationFromPath(*store, drvPath); Derivation drv = derivationFromPath(*store, drvPath);
hash = newHV(); hash = newHV();
/* TODO: handle drv.outputs */ HV * outputs = newHV();
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
hv_store(outputs, i->first.c_str(), i->first.size(), newSVpv(i->second.path.c_str(), 0), 0);
hv_stores(hash, "outputs", newRV((SV *) outputs));
AV * inputDrvs = newAV(); AV * inputDrvs = newAV();
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i) for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
av_push(inputDrvs, newSVpv(i->first.c_str(), 0)); // !!! ignores i->second av_push(inputDrvs, newSVpv(i->first.c_str(), 0)); // !!! ignores i->second
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs)); hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
AV * inputSrcs = newAV(); AV * inputSrcs = newAV();
for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i) for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i)
av_push(inputSrcs, newSVpv(i->c_str(), 0)); av_push(inputSrcs, newSVpv(i->c_str(), 0));
hv_stores(hash, "inputSrcs", newRV((SV *) inputSrcs)); hv_stores(hash, "inputSrcs", newRV((SV *) inputSrcs));
hv_stores(hash, "platform", newSVpv(drv.platform.c_str(), 0)); hv_stores(hash, "platform", newSVpv(drv.platform.c_str(), 0));
hv_stores(hash, "builder", newSVpv(drv.builder.c_str(), 0)); hv_stores(hash, "builder", newSVpv(drv.builder.c_str(), 0));
AV * args = newAV(); AV * args = newAV();
for (Strings::iterator i = drv.args.begin(); i != drv.args.end(); ++i) for (Strings::iterator i = drv.args.begin(); i != drv.args.end(); ++i)
av_push(args, newSVpv(i->c_str(), 0)); av_push(args, newSVpv(i->c_str(), 0));
hv_stores(hash, "args", newRV((SV *) args)); hv_stores(hash, "args", newRV((SV *) args));
HV * env = newHV(); HV * env = newHV();
for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i) for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i)
hv_store(env, i->first.c_str(), i->first.size(), newSVpv(i->second.c_str(), 0), 0); hv_store(env, i->first.c_str(), i->first.size(), newSVpv(i->second.c_str(), 0), 0);
hv_stores(hash, "env", newRV((SV *) env)); hv_stores(hash, "env", newRV((SV *) env));
RETVAL = newRV_noinc((SV *)hash); RETVAL = newRV_noinc((SV *)hash);
} catch (Error & e) { } catch (Error & e) {
croak(e.what()); croak(e.what());
+2 -2
View File
@@ -23,7 +23,7 @@ sub uniq {
sub writeFile { sub writeFile {
my ($fn, $s) = @_; my ($fn, $s) = @_;
open TMP, ">$fn" or die; open TMP, ">$fn" or die "cannot create file `$fn': $!";
print TMP "$s" or die; print TMP "$s" or die;
close TMP or die; close TMP or die;
} }
@@ -31,7 +31,7 @@ sub writeFile {
sub readFile { sub readFile {
local $/ = undef; local $/ = undef;
my ($fn) = @_; my ($fn) = @_;
open TMP, "<$fn" or die; open TMP, "<$fn" or die "cannot open file `$fn': $!";
my $s = <TMP>; my $s = <TMP>;
close TMP or die; close TMP or die;
return $s; return $s;
+67 -23
View File
@@ -1,15 +1,19 @@
{ nixpkgs ? <nixpkgs> { nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
, nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
, officialRelease ? false , officialRelease ? false
}: }:
let let
pkgs = import <nixpkgs> {};
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" ];
jobs = rec { jobs = rec {
tarball = tarball =
with import nixpkgs {}; with pkgs;
releaseTools.sourceTarball { releaseTools.sourceTarball {
name = "nix-tarball"; name = "nix-tarball";
@@ -19,7 +23,7 @@ let
inherit officialRelease; inherit officialRelease;
buildInputs = buildInputs =
[ curl bison25 flex2535 perl libxml2 libxslt w3m bzip2 [ curl bison flex2535 perl libxml2 libxslt w3m bzip2
tetex dblatex nukeReferences pkgconfig sqlite git tetex dblatex nukeReferences pkgconfig sqlite git
]; ];
@@ -72,10 +76,9 @@ let
}; };
build = build = pkgs.lib.genAttrs systems (system:
{ system ? "x86_64-linux" }:
with import nixpkgs { inherit system; }; with import <nixpkgs> { inherit system; };
releaseTools.nixBuild { releaseTools.nixBuild {
name = "nix"; name = "nix";
@@ -89,20 +92,25 @@ let
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix} --with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix} --with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
--enable-gc --enable-gc
--sysconfdir=/etc
''; '';
enableParallelBuilding = true; enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d";
installFlags = "sysconfdir=$(out)/etc";
doInstallCheck = true; doInstallCheck = true;
}; });
binaryTarball =
{ system ? "x86_64-linux" }:
with import nixpkgs { inherit system; }; binaryTarball = pkgs.lib.genAttrs systems (system:
with import <nixpkgs> { inherit system; };
let let
toplevel = build { inherit system; }; toplevel = builtins.getAttr system jobs.build;
version = toplevel.src.version; version = toplevel.src.version;
in in
@@ -125,11 +133,11 @@ let
--transform "s,$TMPDIR/install,/usr/bin/nix-finish-install," \ --transform "s,$TMPDIR/install,/usr/bin/nix-finish-install," \
--transform "s,$TMPDIR/reginfo,/nix/store/reginfo," \ --transform "s,$TMPDIR/reginfo,/nix/store/reginfo," \
$TMPDIR/install $TMPDIR/reginfo $storePaths $TMPDIR/install $TMPDIR/reginfo $storePaths
''; '');
coverage = coverage =
with import nixpkgs { system = "x86_64-linux"; }; with import <nixpkgs> { system = "x86_64-linux"; };
releaseTools.coverageAnalysis { releaseTools.coverageAnalysis {
name = "nix-build"; name = "nix-build";
@@ -161,17 +169,19 @@ let
}; };
rpm_fedora13i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora13i386) 50;
rpm_fedora13x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora13x86_64) 50;
rpm_fedora16i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora16i386) 50; rpm_fedora16i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora16i386) 50;
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50; 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_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50; deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
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_ubuntu1010i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1010i386) 50;
deb_ubuntu1010x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1010x86_64) 50; deb_ubuntu1010x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1010x86_64) 50;
deb_ubuntu1110i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1110i386) 60; deb_ubuntu1110i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1110i386) 60;
@@ -180,17 +190,51 @@ let
deb_ubuntu1204x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1204x86_64) 60; deb_ubuntu1204x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1204x86_64) 60;
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) 70; deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) 70;
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70; 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. # System tests.
tests.remote_builds = (import ./tests/remote-builds.nix rec { tests.remote_builds = (import ./tests/remote-builds.nix rec {
nix = build { inherit system; }; system = "x86_64-linux"; nix = build.x86_64-linux; system = "x86_64-linux";
}).test; }).test;
tests.nix_copy_closure = (import ./tests/nix-copy-closure.nix rec { tests.nix_copy_closure = (import ./tests/nix-copy-closure.nix rec {
nix = build { inherit system; }; system = "x86_64-linux"; nix = build.x86_64-linux; system = "x86_64-linux";
}).test; }).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
];
};
}; };
@@ -200,7 +244,7 @@ let
makeRPM = makeRPM =
system: diskImageFun: prio: system: diskImageFun: prio:
with import nixpkgs { inherit system; }; with import <nixpkgs> { inherit system; };
releaseTools.rpmBuild rec { releaseTools.rpmBuild rec {
name = "nix-rpm"; name = "nix-rpm";
@@ -219,7 +263,7 @@ let
makeDeb = makeDeb =
system: diskImageFun: prio: system: diskImageFun: prio:
with import nixpkgs { inherit system; }; with import <nixpkgs> { inherit system; };
releaseTools.debBuild { releaseTools.debBuild {
name = "nix-deb"; name = "nix-deb";
@@ -229,7 +273,7 @@ let
memSize = 1024; memSize = 1024;
meta.schedulingPriority = prio; meta.schedulingPriority = prio;
configureFlags = "--sysconfdir=/etc"; configureFlags = "--sysconfdir=/etc";
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" ]; debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ];
doInstallCheck = true; doInstallCheck = true;
}; };
+5 -2
View File
@@ -7,15 +7,18 @@ noinst_SCRIPTS = nix-profile.sh \
find-runtime-roots.pl build-remote.pl nix-reduce-build \ find-runtime-roots.pl build-remote.pl nix-reduce-build \
copy-from-other-stores.pl nix-http-export.cgi copy-from-other-stores.pl nix-http-export.cgi
profiledir = $(sysconfdir)/profile.d
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl find-runtime-roots.pl install-exec-local: download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl find-runtime-roots.pl
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d $(INSTALL) -d $(DESTDIR)$(profiledir)
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh $(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(profiledir)/nix.sh
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix $(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix $(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix $(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters $(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL_PROGRAM) download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl $(DESTDIR)$(libexecdir)/nix/substituters $(INSTALL_PROGRAM) download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix $(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
ln -sf nix-build $(DESTDIR)$(bindir)/nix-shell
include ../substitute.mk include ../substitute.mk
+17 -14
View File
@@ -195,9 +195,13 @@ REQ: while (1) {
# Connect to the selected machine. # Connect to the selected machine.
@sshOpts = ("-i", $machine->{sshKeys}, "-x"); @sshOpts = ("-i", $machine->{sshKeys}, "-x");
$hostName = $machine->{hostName}; $hostName = $machine->{hostName};
last REQ if openSSHConnection $hostName; if (openSSHConnection($hostName)) {
last REQ if system("ssh $hostName @sshOpts nix-builds-inhibited < /dev/null > /dev/null 2>&1") != 0;
warn "unable to open SSH connection to $hostName, trying other available machines...\n"; warn "machine `$hostName' is refusing builds, trying other available machines...\n";
closeSSHConnection;
} else {
warn "unable to open SSH connection to `$hostName', trying other available machines...\n";
}
$machine->{enabled} = 0; $machine->{enabled} = 0;
} }
} }
@@ -209,7 +213,6 @@ my @inputs = split /\s/, readline(STDIN);
my @outputs = split /\s/, readline(STDIN); my @outputs = split /\s/, readline(STDIN);
print STDERR "building `$drvPath' on `$hostName'\n";
print STDERR "@ build-remote $drvPath $hostName\n" if $printBuildTrace; print STDERR "@ build-remote $drvPath $hostName\n" if $printBuildTrace;
@@ -258,8 +261,9 @@ close UPLOADLOCK;
# Perform the build. # Perform the build.
my $buildFlags = my $buildFlags =
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout " "--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
. "--fallback --add-root $rootsDir/\$PPID.out --option verbosity 0"; . " --fallback --add-root $rootsDir/\$PPID.out --quiet"
. " --option build-keep-log false --option build-use-substitutes false";
# We let the remote side kill its process group when the connection is # We let the remote side kill its process group when the connection is
# closed unexpectedly. This is necessary to ensure that no processes # closed unexpectedly. This is necessary to ensure that no processes
@@ -268,8 +272,9 @@ my $buildFlags =
# is interrupted unless the `-tt' flag is used to force a pseudo-tty, # is interrupted unless the `-tt' flag is used to force a pseudo-tty,
# in which case every child receives SIGHUP; however, `-tt' doesn't # in which case every child receives SIGHUP; however, `-tt' doesn't
# work on some platforms when connection sharing is used.) # work on some platforms when connection sharing is used.)
print STDERR "building `$drvPath' on `$hostName'\n";
pipe STDIN, DUMMY; # make sure we have a readable STDIN pipe STDIN, DUMMY; # make sure we have a readable STDIN
if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) { if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & exec nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
# Note that if we get exit code 100 from `nix-store -r', it # Note that if we get exit code 100 from `nix-store -r', it
# denotes a permanent build failure (as opposed to an SSH problem # denotes a permanent build failure (as opposed to an SSH problem
# or a temporary Nix problem). We propagate this to the caller to # or a temporary Nix problem). We propagate this to the caller to
@@ -285,13 +290,11 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store
# Copy the output from the build machine. # Copy the output from the build machine.
foreach my $output (@outputs) { my @outputs2 = grep { !isValidPath($_) } @outputs;
my $maybeSignRemote = ""; if (scalar @outputs2 > 0) {
$maybeSignRemote = "--sign" if $UID != 0; system("exec ssh $hostName @sshOpts 'nix-store --export @outputs2'" .
next if isValidPath($output); "| NIX_HELD_LOCKS='@outputs2' @bindir@/nix-store --import > /dev/null") == 0
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" . or die("cannot copy paths " . join(", ", @outputs) . " from `$hostName': $?");
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
or die "cannot copy $output from $hostName: $?";
} }
+8 -5
View File
@@ -16,6 +16,9 @@ foreach my $dir (@remoteStoresAll) {
push @remoteStores, glob($dir); push @remoteStores, glob($dir);
} }
exit if scalar @remoteStores == 0;
print "\n";
$ENV{"NIX_REMOTE"} = ""; $ENV{"NIX_REMOTE"} = "";
@@ -27,7 +30,7 @@ sub findStorePath {
next unless -e $sourcePath || -l $sourcePath; next unless -e $sourcePath || -l $sourcePath;
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db"; $ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
return ($store, $sourcePath) if return ($store, $sourcePath) if
system("@bindir@/nix-store --check-validity $storePath") == 0; system("$binDir/nix-store --check-validity $storePath") == 0;
} }
return undef; return undef;
} }
@@ -53,16 +56,16 @@ if ($ARGV[0] eq "--query") {
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db"; $ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
my $deriver = `@bindir@/nix-store --query --deriver $storePath`; my $deriver = `$binDir/nix-store --query --deriver $storePath`;
die "cannot query deriver of `$storePath'" if $? != 0; die "cannot query deriver of `$storePath'" if $? != 0;
chomp $deriver; chomp $deriver;
$deriver = "" if $deriver eq "unknown-deriver"; $deriver = "" if $deriver eq "unknown-deriver";
my @references = split "\n", my @references = split "\n",
`@bindir@/nix-store --query --references $storePath`; `$binDir/nix-store --query --references $storePath`;
die "cannot query references of `$storePath'" if $? != 0; die "cannot query references of `$storePath'" if $? != 0;
my $narSize = `@bindir@/nix-store --query --size $storePath`; my $narSize = `$binDir/nix-store --query --size $storePath`;
die "cannot query size of `$storePath'" if $? != 0; die "cannot query size of `$storePath'" if $? != 0;
chomp $narSize; chomp $narSize;
@@ -70,7 +73,7 @@ if ($ARGV[0] eq "--query") {
print "$deriver\n"; print "$deriver\n";
print scalar @references, "\n"; print scalar @references, "\n";
print "$_\n" foreach @references; print "$_\n" foreach @references;
print "$narSize\n"; print "0\n";
print "$narSize\n"; print "$narSize\n";
} }
+63 -24
View File
@@ -1,6 +1,7 @@
#! @perl@ -w @perlFlags@ #! @perl@ -w @perlFlags@
use DBI; use DBI;
use DBD::SQLite;
use File::Basename; use File::Basename;
use IO::Select; use IO::Select;
use Nix::Config; use Nix::Config;
@@ -24,8 +25,11 @@ my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
my $didExpiration = 0; my $didExpiration = 0;
my $debug = ($ENV{"NIX_DEBUG_SUBST"} // "") eq 1; my $showAfter = 5; # show that we're waiting for a request after this many seconds
open(STDERR, ">>/dev/tty") if $debug;
my $debug = ($Nix::Config::config{"debug-subst"} // "") eq 1 || ($Nix::Config::config{"untrusted-debug-subst"} // "") eq 1;
my $cacheFileURLs = ($ENV{"_NIX_CACHE_FILE_URLS"} // "") eq 1; # for testing
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence); my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence);
@@ -36,13 +40,16 @@ my %requests;
my %scheduled; my %scheduled;
my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"}; my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
my $userName = getpwuid($<) or die "cannot figure out user name";
sub addRequest { sub addRequest {
my ($storePath, $url, $head) = @_; my ($storePath, $url, $head) = @_;
my $curl = WWW::Curl::Easy->new; my $curl = WWW::Curl::Easy->new;
my $curlId = $curlIdCount++; my $curlId = $curlIdCount++;
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET" }; $requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET"
, shown => 0, started => time() };
$curl->setopt(CURLOPT_PRIVATE, $curlId); $curl->setopt(CURLOPT_PRIVATE, $curlId);
$curl->setopt(CURLOPT_URL, $url); $curl->setopt(CURLOPT_URL, $url);
@@ -53,6 +60,7 @@ sub addRequest {
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version"); $curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
$curl->setopt(CURLOPT_NOBODY, 1) if $head; $curl->setopt(CURLOPT_NOBODY, 1) if $head;
$curl->setopt(CURLOPT_FAILONERROR, 1); $curl->setopt(CURLOPT_FAILONERROR, 1);
$curl->setopt(CURLOPT_TIMEOUT, int($ENV{"NIX_CONNECT_TIMEOUT"} // 0));
if ($activeRequests >= $maxParallelRequests) { if ($activeRequests >= $maxParallelRequests) {
$scheduled{$curlId} = 1; $scheduled{$curlId} = 1;
@@ -72,7 +80,7 @@ sub processRequests {
# Sleep until we can read or write some data. # Sleep until we can read or write some data.
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) { if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 0.1); IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 1.0);
} }
if ($curlm->perform() != $activeRequests) { if ($curlm->perform() != $activeRequests) {
@@ -97,6 +105,16 @@ sub processRequests {
} }
} }
} }
my $time = time();
while (my ($key, $request) = each %requests) {
next unless defined $request->{handle};
next if $request->{shown};
if ($time > $request->{started} + $showAfter) {
print STDERR "still waiting for $request->{url} after $showAfter seconds...\n";
$request->{shown} = 1;
}
}
} }
} }
@@ -182,20 +200,16 @@ sub getAvailableCaches {
return if $gotCaches; return if $gotCaches;
$gotCaches = 1; $gotCaches = 1;
return if
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
sub strToList { sub strToList {
my ($s) = @_; my ($s) = @_;
return map { s/\/+$//; $_ } split(/ /, $s); return map { s/\/+$//; $_ } split(/ /, $s);
} }
my @urls = strToList ($Nix::Config::config{"binary-caches"} // ""); my @urls = strToList($Nix::Config::config{"binary-caches"} //
# // ($Nix::Config::storeDir eq "/nix/store" ? "http://nixos.org/binary-cache" : "")); ($Nix::Config::storeDir eq "/nix/store" ? "http://cache.nixos.org" : ""));
my $urlsFiles = $Nix::Config::config{"binary-cache-files"} my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
// "/nix/var/nix/profiles/per-user/root/channels/binary-caches/*"; // "$Nix::Config::stateDir/profiles/per-user/$userName/channels/binary-caches/*";
foreach my $urlFile (glob $urlsFiles) { foreach my $urlFile (glob $urlsFiles) {
next unless -f $urlFile; next unless -f $urlFile;
open FILE, "<$urlFile" or die "cannot open $urlFile\n"; open FILE, "<$urlFile" or die "cannot open $urlFile\n";
@@ -204,12 +218,15 @@ sub getAvailableCaches {
push @urls, strToList($url); push @urls, strToList($url);
} }
push @urls, strToList($Nix::Config::config{"extra-binary-caches"} // "");
# Allow Nix daemon users to override the binary caches to a subset # Allow Nix daemon users to override the binary caches to a subset
# of those listed in the config file. Note that untrusted-* # of those listed in the config file. Note that untrusted-*
# denotes options passed by the client. # denotes options passed by the client.
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
if (defined $Nix::Config::config{"untrusted-binary-caches"}) { if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"}; my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
@urls = (); @urls = ();
foreach my $url (@untrustedUrls) { foreach my $url (@untrustedUrls) {
die "binary cache $url is not trusted (please add it to trusted-binary-caches [@trustedUrls] in $Nix::Config::confDir/nix.conf)\n" die "binary cache $url is not trusted (please add it to trusted-binary-caches [@trustedUrls] in $Nix::Config::confDir/nix.conf)\n"
@@ -218,6 +235,12 @@ sub getAvailableCaches {
} }
} }
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-extra-binary-caches"} // "";
foreach my $url (@untrustedUrls) {
next unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
push @urls, $url;
}
foreach my $url (uniq @urls) { foreach my $url (uniq @urls) {
# FIXME: not atomic. # FIXME: not atomic.
@@ -252,11 +275,12 @@ sub getAvailableCaches {
elsif ($1 eq "Priority") { $priority = int($2); } elsif ($1 eq "Priority") { $priority = int($2); }
} }
$dbh->do("insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)", $dbh->do("insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
{}, $url, time(), $storeDir, $wantMassQuery, $priority); {}, $url, time(), $storeDir, $wantMassQuery, $priority);
my $id = $dbh->last_insert_id("", "", "", ""); $queryCache->execute($url);
$res = $queryCache->fetchrow_hashref() or die;
next if $storeDir ne $Nix::Config::storeDir; next if $storeDir ne $Nix::Config::storeDir;
push @caches, { id => $id, url => $url, wantMassQuery => $wantMassQuery, priority => $priority }; push @caches, { id => $res->{id}, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
} }
@caches = sort { $a->{priority} <=> $b->{priority} } @caches; @caches = sort { $a->{priority} <=> $b->{priority} } @caches;
@@ -265,16 +289,22 @@ sub getAvailableCaches {
} }
sub shouldCache {
my ($url) = @_;
return $cacheFileURLs || $url !~ /^file:/;
}
sub processNARInfo { sub processNARInfo {
my ($storePath, $cache, $request) = @_; my ($storePath, $cache, $request) = @_;
if ($request->{result} != 0) { if ($request->{result} != 0) {
if ($request->{result} != 37 && $request->{httpStatus} != 404) { if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
print STDERR "could not download $request->{url} (" . print STDERR "could not download $request->{url} (" .
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n"; ($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
} else { } else {
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time()) $insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
unless $request->{url} =~ /^file:/; if shouldCache $request->{url};
} }
return undef; return undef;
} }
@@ -287,7 +317,7 @@ sub processNARInfo {
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression}, $cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize}, $narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time()) join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time())
unless $request->{url} =~ /^file:/; if shouldCache $request->{url};
return $narInfo; return $narInfo;
} }
@@ -448,17 +478,17 @@ sub printSubstitutablePaths {
foreach my $request (values %requests) { foreach my $request (values %requests) {
if ($request->{result} != 0) { if ($request->{result} != 0) {
if ($request->{result} != 37 && $request->{httpStatus} != 404) { if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
print STDERR "could not check $request->{url} (" . print STDERR "could not check $request->{url} (" .
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n"; ($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
} else { } else {
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time()) $insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
unless $request->{url} =~ /^file:/; if shouldCache $request->{url};
} }
push @left2, $request->{storePath}; push @left2, $request->{storePath};
} else { } else {
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time()) $insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
unless $request->{url} =~ /^file:/; if shouldCache $request->{url};
print "$request->{storePath}\n"; print "$request->{storePath}\n";
} }
} }
@@ -484,8 +514,9 @@ sub downloadBinary {
next unless defined $info; next unless defined $info;
my $decompressor; my $decompressor;
if ($info->{compression} eq "bzip2") { $decompressor = "$Nix::Config::bzip2 -d"; } if ($info->{compression} eq "bzip2") { $decompressor = "| $Nix::Config::bzip2 -d"; }
elsif ($info->{compression} eq "xz") { $decompressor = "$Nix::Config::xz -d"; } elsif ($info->{compression} eq "xz") { $decompressor = "| $Nix::Config::xz -d"; }
elsif ($info->{compression} eq "none") { $decompressor = ""; }
else { else {
print STDERR "unknown compression method $info->{compression}\n"; print STDERR "unknown compression method $info->{compression}\n";
next; next;
@@ -493,7 +524,7 @@ sub downloadBinary {
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
print STDERR "\n*** Downloading $url to $storePath...\n"; print STDERR "\n*** Downloading $url to $storePath...\n";
checkURL $url; checkURL $url;
if (system("$Nix::Config::curl --fail --location --insecure '$url' | $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) { if (system("$Nix::Config::curl --fail --location --insecure '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n"; warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n";
next; next;
} }
@@ -506,9 +537,17 @@ sub downloadBinary {
} }
print STDERR "could not download $storePath from any binary cache\n"; print STDERR "could not download $storePath from any binary cache\n";
exit 1;
} }
# Bail out right away if binary caches are disabled.
exit 0 if
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
print "\n";
flush STDOUT;
initCache(); initCache();
+7 -4
View File
@@ -22,6 +22,8 @@ my $curl = "$Nix::Config::curl --fail --location --insecure";
# Open the manifest cache and update it if necessary. # Open the manifest cache and update it if necessary.
my $dbh = updateManifestDB(); my $dbh = updateManifestDB();
exit 0 unless defined $dbh; # exit if there are no manifests
print "\n";
# $hashCache->{$algo}->{$path} yields the $algo-hash of $path. # $hashCache->{$algo}->{$path} yields the $algo-hash of $path.
@@ -342,17 +344,18 @@ while (scalar @path > 0) {
checkURL $narFile->{url}; checkURL $narFile->{url};
my $decompressor = my $decompressor =
$narFile->{compressionType} eq "bzip2" ? "$Nix::Config::bzip2 -d" : $narFile->{compressionType} eq "bzip2" ? "| $Nix::Config::bzip2 -d" :
$narFile->{compressionType} eq "xz" ? "$Nix::Config::xz -d" : $narFile->{compressionType} eq "xz" ? "| $Nix::Config::xz -d" :
$narFile->{compressionType} eq "none" ? "" :
die "unknown compression type `$narFile->{compressionType}'"; die "unknown compression type `$narFile->{compressionType}'";
if ($curStep < $maxStep) { if ($curStep < $maxStep) {
# The archive will be used a base to a patch. # The archive will be used a base to a patch.
system("$curl '$narFile->{url}' | $decompressor > $tmpNar") == 0 system("$curl '$narFile->{url}' $decompressor > $tmpNar") == 0
or die "cannot download and unpack `$narFile->{url}' to `$v'\n"; or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
} else { } else {
# Unpack the archive to the target path. # Unpack the archive to the target path.
system("$curl '$narFile->{url}' | $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0 system("$curl '$narFile->{url}' $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0
or die "cannot download and unpack `$narFile->{url}' to `$v'\n"; or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
} }
+23 -19
View File
@@ -1,22 +1,22 @@
#! @perl@ -w #! @perl@ -w @perlFlags@
use strict; use strict;
use Nix::Utils;
my $procDir = "/proc"; use Nix::Config;
sub readProc { sub readProc {
return unless -d $procDir; return unless -d "/proc";
opendir DIR, "/proc" or return;
opendir DIR, $procDir or return;
foreach my $name (readdir DIR) { foreach my $name (readdir DIR) {
next unless $name =~ /^\d+$/; next unless $name =~ /^\d+$/;
my $process = "$procDir/$name"; my $process = "/proc/$name";
#print STDERR "=== $process\n"; #print STDERR "=== $process\n";
my $target; my $target;
print "$target\n" if $target = readlink "$process/exe"; print "$target\n" if $target = readlink "$process/exe";
print "$target\n" if $target = readlink "$process/cwd"; print "$target\n" if $target = readlink "$process/cwd";
@@ -36,6 +36,13 @@ sub readProc {
} }
close MAP; close MAP;
} }
# Get all store paths that appear in the environment of this process.
eval {
my $env = Nix::Utils::readFile "$process/environ";
my @matches = $env =~ /\Q$Nix::Config::storeDir\E\/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*/g;
print "$_\n" foreach @matches;
}
} }
closedir DIR; closedir DIR;
@@ -58,18 +65,15 @@ readProc;
lsof; lsof;
sub readFile { sub printFile {
my $path = shift; my ($fn) = @_;
if (-e $path) { if (-e $fn) {
if (open FILE, "$path") { print Nix::Utils::readFile($fn), "\n";
while (<FILE>) {
print;
}
close FILE;
}
} }
} }
# This is rather NixOS-specific, so it probably shouldn't be here. # This is rather NixOS-specific, so it probably shouldn't be here.
readFile "/proc/sys/kernel/modprobe"; printFile "/proc/sys/kernel/modprobe";
readFile "/proc/sys/kernel/fbsplash"; printFile "/proc/sys/kernel/fbsplash";
printFile "/proc/sys/kernel/poweroff_cmd";
+60 -23
View File
@@ -3,23 +3,27 @@
use strict; use strict;
use Nix::Config; use Nix::Config;
use Nix::Store; use Nix::Store;
use Nix::Utils;
use File::Temp qw(tempdir); use File::Temp qw(tempdir);
my $dryRun = 0; my $dryRun = 0;
my $verbose = 0; my $verbose = 0;
my $runEnv = 0; my $runEnv = $0 =~ /nix-shell$/;
my $pure = 0;
my @instArgs = (); my @instArgs = ();
my @buildArgs = (); my @buildArgs = ();
my @exprs = (); my @exprs = ();
my $shell = $ENV{SHELL} || "/bin/sh"; my $shell = $ENV{SHELL} || "/bin/sh";
my $envCommand = "p=\$PATH; source \$stdenv/setup; PATH=\$PATH:\$p; exec $shell"; my $envCommand = ""; # interactive shell
my @envExclude = (); my @envExclude = ();
my $myName = $runEnv ? "nix-shell" : "nix-build";
my $tmpDir = tempdir("nix-build.XXXXXX", CLEANUP => 1, TMPDIR => 1)
my $tmpDir = tempdir("$myName.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory"; or die "cannot create a temporary directory";
my $outLink = "./result"; my $outLink = "./result";
@@ -33,11 +37,11 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
my $arg = $ARGV[$n]; my $arg = $ARGV[$n];
if ($arg eq "--help") { if ($arg eq "--help") {
exec "man nix-build" or die; exec "man $myName" or die;
} }
elsif ($arg eq "--version") { elsif ($arg eq "--version") {
print "nix-build (Nix) $Nix::Config::version\n"; print "$myName (Nix) $Nix::Config::version\n";
exit 0; exit 0;
} }
@@ -87,7 +91,7 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
$n += 2; $n += 2;
} }
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores") { elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores" or $arg eq "--timeout") {
$n++; $n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV; die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
push @buildArgs, ($arg, $ARGV[$n]); push @buildArgs, ($arg, $ARGV[$n]);
@@ -117,14 +121,14 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
push @instArgs, $arg; push @instArgs, $arg;
} }
elsif ($arg eq "--run-env") { elsif ($arg eq "--run-env") { # obsolete
$runEnv = 1; $runEnv = 1;
} }
elsif ($arg eq "--command") { elsif ($arg eq "--command") {
$n++; $n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV; die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
$envCommand = $ARGV[$n]; $envCommand = "$ARGV[$n]\nexit $!";
} }
elsif ($arg eq "--exclude") { elsif ($arg eq "--exclude") {
@@ -133,6 +137,10 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
push @envExclude, $ARGV[$n]; push @envExclude, $ARGV[$n];
} }
elsif ($arg eq "--pure") {
$pure = 1;
}
elsif (substr($arg, 0, 1) eq "-") { elsif (substr($arg, 0, 1) eq "-") {
push @buildArgs, $arg; push @buildArgs, $arg;
} }
@@ -149,17 +157,22 @@ foreach my $expr (@exprs) {
# Instantiate. # Instantiate.
my @drvPaths; my @drvPaths;
# !!! would prefer the perl 5.8.0 pipe open feature here. if ($expr !~ /^\/.*\.drv$/) {
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr; # !!! would prefer the perl 5.8.0 pipe open feature here.
while (<DRVPATHS>) {chomp; push @drvPaths, $_;} my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
if (!close DRVPATHS) { while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127); if (!close DRVPATHS) {
exit 1; die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
exit 1;
}
} else {
push @drvPaths, $expr;
} }
if ($runEnv) { if ($runEnv) {
die "$0: --run-env requires a single derivation\n" if scalar @drvPaths != 1; die "$0: a single derivation is required\n" if scalar @drvPaths != 1;
my $drvPath = readlink $drvPaths[0] or die "cannot read symlink `$drvPaths[0]'"; my $drvPath = $drvPaths[0];
$drvPath = readlink $drvPath or die "cannot read symlink `$drvPath'" if -l $drvPath;
my $drv = derivationFromPath($drvPath); my $drv = derivationFromPath($drvPath);
# Build or fetch all dependencies of the derivation. # Build or fetch all dependencies of the derivation.
@@ -168,16 +181,40 @@ foreach my $expr (@exprs) {
or die "$0: failed to build all dependencies\n"; or die "$0: failed to build all dependencies\n";
# Set the environment. # Set the environment.
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} || "/tmp"; if ($pure) {
foreach (keys %{$drv->{env}}) { foreach my $name (keys %ENV) {
$ENV{$_} = $drv->{env}->{$_}; next if $name eq "HOME" || $name eq "USER" || $name eq "LOGNAME" || $name eq "DISPLAY" || $name eq "PATH";
delete $ENV{$name};
}
# NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
$ENV{'__ETC_PROFILE_SOURCED'} = 1;
} }
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} = $ENV{'TEMPDIR'} = $ENV{'TMP'} = $ENV{'TEMP'} = $ENV{'TMPDIR'} // "/tmp";
$ENV{'NIX_STORE'} = $Nix::Config::storeDir;
$ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}};
$ENV{'IN_NIX_SHELL'} = 1;
# Run a shell using the derivation's environment. For # Run a shell using the derivation's environment. For
# convenience, source $stdenv/setup to setup additional # convenience, source $stdenv/setup to setup additional
# environment variables. Also don't lose the current $PATH # environment variables and shell functions. Also don't lose
# directories. # the current $PATH directories.
exec($ENV{SHELL}, "-c", $envCommand); my $rcfile = "$tmpDir/rc";
writeFile(
$rcfile,
'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; ' .
'[ -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; die;
} }
@@ -206,7 +243,7 @@ foreach my $expr (@exprs) {
# Build. # Build.
my @outPaths; my @outPaths;
$pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r", my $pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
@buildArgs, @drvPaths2; @buildArgs, @drvPaths2;
while (<OUTPATHS>) {chomp; push @outPaths, $_;} while (<OUTPATHS>) {chomp; push @outPaths, $_;}
if (!close OUTPATHS) { if (!close OUTPATHS) {
+10 -2
View File
@@ -4,6 +4,7 @@ use Nix::SSH;
use Nix::Config; use Nix::Config;
use Nix::Store; use Nix::Store;
use Nix::CopyClosure; use Nix::CopyClosure;
use List::Util qw(sum);
if (scalar @ARGV < 1) { if (scalar @ARGV < 1) {
@@ -77,6 +78,8 @@ while (@ARGV) {
} }
} }
die "$0: you did not specify a host name\n" unless defined $sshHost;
openSSHConnection $sshHost or die "$0: unable to start SSH\n"; openSSHConnection $sshHost or die "$0: unable to start SSH\n";
@@ -104,6 +107,11 @@ else { # Copy FROM the remote machine.
close READ or die "nix-store on remote machine `$sshHost' failed: $?"; close READ or die "nix-store on remote machine `$sshHost' failed: $?";
my $missingSize = 0;
if ($progressViewer ne "") {
$missingSize = sum (split ' ', `set -f; ssh @sshOpts $sshHost nix-store -q --size @missing`) or die;
}
# Export the store paths on the remote machine and import them locally. # Export the store paths on the remote machine and import them locally.
if (scalar @missing > 0) { if (scalar @missing > 0) {
print STDERR "copying ", scalar @missing, " missing paths from $sshHost...\n"; print STDERR "copying ", scalar @missing, " missing paths from $sshHost...\n";
@@ -113,9 +121,9 @@ else { # Copy FROM the remote machine.
} }
$compressor = "| $compressor" if $compressor ne ""; $compressor = "| $compressor" if $compressor ne "";
$decompressor = "$decompressor |" if $decompressor ne ""; $decompressor = "$decompressor |" if $decompressor ne "";
$progressViewer = "$progressViewer |" if $progressViewer ne ""; $progressViewer = "$progressViewer -s $missingSize |" if $progressViewer ne "";
my $extraOpts = $sign ? "--sign" : ""; my $extraOpts = $sign ? "--sign" : "";
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $progressViewer $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0 system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $decompressor $progressViewer $Nix::Config::binDir/nix-store --import > /dev/null") == 0
or die "copying store paths from remote machine `$sshHost' failed: $?"; or die "copying store paths from remote machine `$sshHost' failed: $?";
} }
} }
+3 -1
View File
@@ -34,6 +34,8 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
exec "man nix-push" or die; exec "man nix-push" or die;
} elsif ($arg eq "--bzip2") { } elsif ($arg eq "--bzip2") {
$compressionType = "bzip2"; $compressionType = "bzip2";
} elsif ($arg eq "--none") {
$compressionType = "none";
} elsif ($arg eq "--force") { } elsif ($arg eq "--force") {
$force = 1; $force = 1;
} elsif ($arg eq "--dest") { } elsif ($arg eq "--dest") {
@@ -202,7 +204,7 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
$compressedHash =~ /^[0-9a-z]+$/ or die "invalid hash"; $compressedHash =~ /^[0-9a-z]+$/ or die "invalid hash";
close HASH; close HASH;
my $narName = "$compressedHash.nar." . ($compressionType eq "xz" ? "xz" : "bz2"); my $narName = "$compressedHash.nar" . ($compressionType eq "xz" ? ".xz" : $compressionType eq "bzip2" ? ".bz2" : "");
my $narFile = "$narDir/$narName"; my $narFile = "$narDir/$narName";
(-f $narFile) or die "NAR file for $storePath not found"; (-f $narFile) or die "NAR file for $storePath not found";
+25 -25
View File
@@ -6,9 +6,8 @@
namespace nix { namespace nix {
// !!! Shouldn't we return a pointer to a Value? Value * findAlongAttrPath(EvalState & state, const string & attrPath,
void findAlongAttrPath(EvalState & state, const string & attrPath, Bindings & autoArgs, Value & vIn)
Bindings & autoArgs, Expr * e, Value & v)
{ {
Strings tokens = tokenizeString<Strings>(attrPath, "."); Strings tokens = tokenizeString<Strings>(attrPath, ".");
@@ -17,8 +16,8 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
string curPath; string curPath;
state.mkThunk_(v, e); Value * v = &vIn;
foreach (Strings::iterator, i, tokens) { foreach (Strings::iterator, i, tokens) {
if (!curPath.empty()) curPath += "."; if (!curPath.empty()) curPath += ".";
@@ -27,47 +26,48 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
/* Is *i an index (integer) or a normal attribute name? */ /* Is *i an index (integer) or a normal attribute name? */
enum { apAttr, apIndex } apType = apAttr; enum { apAttr, apIndex } apType = apAttr;
string attr = *i; string attr = *i;
int attrIndex = -1; unsigned int attrIndex;
if (string2Int(attr, attrIndex)) apType = apIndex; if (string2Int(attr, attrIndex)) apType = apIndex;
/* Evaluate the expression. */ /* Evaluate the expression. */
Value vTmp; Value * vNew = state.allocValue();
state.autoCallFunction(autoArgs, v, vTmp); state.autoCallFunction(autoArgs, *v, *vNew);
v = vTmp; v = vNew;
state.forceValue(v); state.forceValue(*v);
/* It should evaluate to either an attribute set or an /* It should evaluate to either a set or an expression,
expression, according to what is specified in the according to what is specified in the attrPath. */
attrPath. */
if (apType == apAttr) { if (apType == apAttr) {
if (v.type != tAttrs) if (v->type != tAttrs)
throw TypeError( 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)); % curPath % showType(*v));
Bindings::iterator a = v.attrs->find(state.symbols.create(attr)); Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
if (a == v.attrs->end()) if (a == v->attrs->end())
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath); throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
v = *a->value; v = &*a->value;
} }
else if (apType == apIndex) { else if (apType == apIndex) {
if (v.type != tList) if (v->type != tList)
throw TypeError( throw TypeError(
format("the expression selected by the selection path `%1%' should be a list but is %2%") format("the expression selected by the selection path `%1%' should be a list but is %2%")
% curPath % showType(v)); % curPath % showType(*v));
if (attrIndex >= v.list.length) if (attrIndex >= v->list.length)
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath); throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
v = *v.list.elems[attrIndex]; v = v->list.elems[attrIndex];
} }
} }
return v;
} }
} }
+2 -2
View File
@@ -7,7 +7,7 @@
namespace nix { namespace nix {
void findAlongAttrPath(EvalState & state, const string & attrPath, Value * findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Expr * e, Value & v); Bindings & autoArgs, Value & vIn);
} }
+7 -4
View File
@@ -21,13 +21,16 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
void EvalState::forceValue(Value & v) void EvalState::forceValue(Value & v)
{ {
if (v.type == tThunk) { if (v.type == tThunk) {
ValueType saved = v.type; Env * env = v.thunk.env;
Expr * expr = v.thunk.expr;
try { try {
v.type = tBlackhole; v.type = tBlackhole;
//checkInterrupt(); //checkInterrupt();
v.thunk.expr->eval(*this, *v.thunk.env, v); expr->eval(*this, *env, v);
} catch (Error & e) { } catch (Error & e) {
v.type = saved; v.type = tThunk;
v.thunk.env = env;
v.thunk.expr = expr;
throw; throw;
} }
} }
@@ -42,7 +45,7 @@ inline void EvalState::forceAttrs(Value & v)
{ {
forceValue(v); forceValue(v);
if (v.type != tAttrs) 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));
} }
+159 -143
View File
@@ -44,7 +44,7 @@ void Bindings::sort()
{ {
std::sort(begin(), end()); std::sort(begin(), end());
} }
std::ostream & operator << (std::ostream & str, const Value & v) std::ostream & operator << (std::ostream & str, const Value & v)
{ {
@@ -116,7 +116,7 @@ string showType(const Value & v)
case tString: return "a string"; case tString: return "a string";
case tPath: return "a path"; case tPath: return "a path";
case tNull: return "null"; case tNull: return "null";
case tAttrs: return "an attribute set"; case tAttrs: return "a set";
case tList: return "a list"; case tList: return "a list";
case tThunk: return "a thunk"; case tThunk: return "a thunk";
case tApp: return "a function application"; case tApp: return "a function application";
@@ -136,15 +136,16 @@ EvalState::EvalState()
, sType(symbols.create("type")) , sType(symbols.create("type"))
, sMeta(symbols.create("meta")) , sMeta(symbols.create("meta"))
, sName(symbols.create("name")) , sName(symbols.create("name"))
, sValue(symbols.create("value"))
, sSystem(symbols.create("system")) , sSystem(symbols.create("system"))
, sOverrides(symbols.create("__overrides")) , sOverrides(symbols.create("__overrides"))
, sOutputs(symbols.create("outputs")) , sOutputs(symbols.create("outputs"))
, sOutputName(symbols.create("outputName")) , sOutputName(symbols.create("outputName"))
, sIgnoreNulls(symbols.create("__ignoreNulls")) , sIgnoreNulls(symbols.create("__ignoreNulls"))
, baseEnv(allocEnv(128))
, baseEnvDispl(0)
, staticBaseEnv(false, 0)
, repair(false) , repair(false)
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
, baseEnvDispl(0)
{ {
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0; nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0; nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
@@ -158,7 +159,7 @@ EvalState::EvalState()
physical RAM, up to a maximum of 384 MiB) so that in most physical RAM, up to a maximum of 384 MiB) so that in most
cases we don't need to garbage collect at all. (Collection cases we don't need to garbage collect at all. (Collection
has a fairly significant overhead.) The heap size can be has a fairly significant overhead.) The heap size can be
overriden through libgc's GC_INITIAL_HEAP_SIZE environment overridden through libgc's GC_INITIAL_HEAP_SIZE environment
variable. We should probably also provide a nix.conf variable. We should probably also provide a nix.conf
setting for this. Note that GC_expand_hp() causes a lot of setting for this. Note that GC_expand_hp() causes a lot of
virtual, but not physical (resident) memory to be virtual, but not physical (resident) memory to be
@@ -222,6 +223,12 @@ void EvalState::addPrimOp(const string & name,
} }
void EvalState::getBuiltin(const string & name, Value & v)
{
v = *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
}
/* Every "format" object (even temporary) takes up a few hundred bytes /* Every "format" object (even temporary) takes up a few hundred bytes
of stack space, which is a real killer in the recursive of stack space, which is a real killer in the recursive
evaluator. So here are some helper functions for throwing evaluator. So here are some helper functions for throwing
@@ -242,14 +249,9 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s))
throw TypeError(s); throw TypeError(s);
} }
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const string & s2)) LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2))
{ {
throw TypeError(format(s) % pos % 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)) LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
@@ -257,16 +259,16 @@ LocalNoInlineNoReturn(void throwAssertionError(const char * s, const Pos & pos))
throw AssertionError(format(s) % 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)) LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2))
{ {
e.addPrefix(format(s) % 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)) LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2, const Pos & pos))
{ {
e.addPrefix(format(s) % s2 % pos); e.addPrefix(format(s) % s2 % pos);
@@ -286,7 +288,7 @@ void mkString(Value & v, const string & s, const PathSet & context)
unsigned int n = 0; unsigned int n = 0;
v.string.context = (const char * *) v.string.context = (const char * *)
GC_MALLOC((context.size() + 1) * sizeof(char *)); GC_MALLOC((context.size() + 1) * sizeof(char *));
foreach (PathSet::const_iterator, i, context) foreach (PathSet::const_iterator, i, context)
v.string.context[n++] = GC_STRDUP(i->c_str()); v.string.context[n++] = GC_STRDUP(i->c_str());
v.string.context[n] = 0; v.string.context[n] = 0;
} }
@@ -299,23 +301,29 @@ void mkPath(Value & v, const char * s)
} }
inline Value * EvalState::lookupVar(Env * env, const VarRef & var) inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
{ {
for (unsigned int l = var.level; l; --l, env = env->up) ; for (unsigned int l = var.level; l; --l, env = env->up) ;
if (var.fromWith) { if (!var.fromWith) return env->values[var.displ];
while (1) {
Bindings::iterator j = env->values[0]->attrs->find(var.name); while (1) {
if (j != env->values[0]->attrs->end()) { if (!env->haveWithAttrs) {
if (countCalls && j->pos) attrSelects[*j->pos]++; if (noEval) return 0;
return j->value; Value * v = allocValue();
} evalAttrs(*env->up, (Expr *) env->values[0], *v);
if (env->prevWith == 0) env->values[0] = v;
throwEvalError("undefined variable `%1%'", var.name); env->haveWithAttrs = true;
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
} }
} else Bindings::iterator j = env->values[0]->attrs->find(var.name);
return env->values[var.displ]; if (j != env->values[0]->attrs->end()) {
if (countCalls && j->pos) attrSelects[*j->pos]++;
return j->value;
}
if (!env->prevWith)
throwUndefinedVarError("undefined variable `%1%' at %2%", var.name, var.pos);
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
}
} }
@@ -332,10 +340,10 @@ Env & EvalState::allocEnv(unsigned int size)
nrValuesInEnvs += size; nrValuesInEnvs += size;
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *)); Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
/* Clear the values because maybeThunk() expects this. */ /* Clear the values because maybeThunk() and lookupVar fromWith expects this. */
for (unsigned i = 0; i < size; ++i) for (unsigned i = 0; i < size; ++i)
env->values[i] = 0; env->values[i] = 0;
return *env; return *env;
} }
@@ -347,7 +355,7 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
return v; return v;
} }
void EvalState::mkList(Value & v, unsigned int length) void EvalState::mkList(Value & v, unsigned int length)
{ {
v.type = tList; v.type = tList;
@@ -400,7 +408,7 @@ unsigned long nrAvoided = 0;
Value * ExprVar::maybeThunk(EvalState & state, Env & env) Value * ExprVar::maybeThunk(EvalState & state, Env & env)
{ {
Value * v = state.lookupVar(&env, info); Value * v = state.lookupVar(&env, *this, true);
/* The value might not be initialised in the environment yet. /* The value might not be initialised in the environment yet.
In that case, ignore it. */ In that case, ignore it. */
if (v) { nrAvoided++; return v; } if (v) { nrAvoided++; return v; }
@@ -429,19 +437,35 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
void EvalState::evalFile(const Path & path, Value & v) void EvalState::evalFile(const Path & path, Value & v)
{ {
FileEvalCache::iterator i = fileEvalCache.find(path); FileEvalCache::iterator i;
if (i == fileEvalCache.end()) { if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
Expr * e = parseExprFromFile(path);
try {
eval(e, v);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
throw;
}
fileEvalCache[path] = v;
} else
v = i->second; v = i->second;
return;
}
Path path2 = resolveExprPath(path);
if ((i = fileEvalCache.find(path2)) != fileEvalCache.end()) {
v = i->second;
return;
}
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path2);
Expr * e = parseExprFromFile(path2);
try {
eval(e, v);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the file `%1%':\n", path2);
throw;
}
fileEvalCache[path2] = v;
if (path != path2) fileEvalCache[path] = v;
}
void EvalState::resetFileCache()
{
fileEvalCache.clear();
} }
@@ -465,7 +489,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
{ {
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type != tAttrs) 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));
} }
@@ -510,23 +534,17 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
environment, while the inherited attributes are evaluated environment, while the inherited attributes are evaluated
in the original environment. */ in the original environment. */
unsigned int displ = 0; unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs) {
if (i->second.inherited) { Value * vAttr;
/* !!! handle overrides? */ if (hasOverrides && !i->second.inherited) {
Value * vAttr = state.lookupVar(&env, i->second.var); vAttr = state.allocValue();
env2.values[displ++] = vAttr; mkThunk(*vAttr, env2, i->second.e);
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos)); } else
} else { vAttr = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
Value * vAttr; env2.values[displ++] = vAttr;
if (hasOverrides) { v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
vAttr = state.allocValue(); }
mkThunk(*vAttr, env2, i->second.e);
} else
vAttr = i->second.e->maybeThunk(state, env2);
env2.values[displ++] = vAttr;
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
}
/* If the rec contains an attribute called `__overrides', then /* If the rec contains an attribute called `__overrides', then
evaluate it, and add the attributes in that set to the rec. evaluate it, and add the attributes in that set to the rec.
This allows overriding of recursive attributes, which is This allows overriding of recursive attributes, which is
@@ -552,10 +570,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
else { else {
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
if (i->second.inherited) v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
v.attrs->push_back(Attr(i->first, state.lookupVar(&env, i->second.var), &i->second.pos));
else
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
} }
} }
@@ -572,10 +587,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
environment. */ environment. */
unsigned int displ = 0; unsigned int displ = 0;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs) foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
if (i->second.inherited) env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
env2.values[displ++] = state.lookupVar(&env, i->second.var);
else
env2.values[displ++] = i->second.e->maybeThunk(state, env2);
body->eval(state, env2, v); body->eval(state, env2, v);
} }
@@ -591,7 +603,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
void ExprVar::eval(EvalState & state, Env & env, Value & v) void ExprVar::eval(EvalState & state, Env & env, Value & v)
{ {
Value * v2 = state.lookupVar(&env, info); Value * v2 = state.lookupVar(&env, *this, false);
state.forceValue(*v2); state.forceValue(*v2);
v = *v2; v = *v2;
} }
@@ -608,7 +620,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
e->eval(state, env, vTmp); e->eval(state, env, vTmp);
try { try {
foreach (AttrPath::const_iterator, i, attrPath) { foreach (AttrPath::const_iterator, i, attrPath) {
nrLookups++; nrLookups++;
Bindings::iterator j; Bindings::iterator j;
@@ -629,17 +641,16 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
pos = j->pos; pos = j->pos;
if (state.countCalls && pos) state.attrSelects[*pos]++; if (state.countCalls && pos) state.attrSelects[*pos]++;
} }
state.forceValue(*vAttrs); state.forceValue(*vAttrs);
} catch (Error & e) { } catch (Error & e) {
if (pos) if (pos && pos->file != state.sDerivationNix)
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n", addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
showAttrPath(attrPath), *pos); showAttrPath(attrPath), *pos);
throw; throw;
} }
v = *vAttrs; v = *vAttrs;
} }
@@ -663,7 +674,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
vAttrs = j->value; vAttrs = j->value;
} }
} }
mkBool(v, true); mkBool(v, true);
} }
@@ -698,10 +709,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
assert(primOp->type == tPrimOp); assert(primOp->type == tPrimOp);
unsigned int arity = primOp->primOp->arity; unsigned int arity = primOp->primOp->arity;
unsigned int argsLeft = arity - argsDone; unsigned int argsLeft = arity - argsDone;
if (argsLeft == 1) { if (argsLeft == 1) {
/* We have all the arguments, so call the primop. */ /* We have all the arguments, so call the primop. */
/* Put all the arguments in an array. */ /* Put all the arguments in an array. */
Value * vArgs[arity]; Value * vArgs[arity];
unsigned int n = arity - 1; unsigned int n = arity - 1;
@@ -712,12 +723,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
/* And call the primop. */ /* And call the primop. */
nrPrimOpCalls++; nrPrimOpCalls++;
if (countCalls) primOpCalls[primOp->primOp->name]++; if (countCalls) primOpCalls[primOp->primOp->name]++;
try { primOp->primOp->fun(*this, vArgs, v);
primOp->primOp->fun(*this, vArgs, v);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the builtin function `%1%':\n", primOp->primOp->name);
throw;
}
} else { } else {
v.type = tPrimOpApp; v.type = tPrimOpApp;
v.primOpApp.left = allocValue(); v.primOpApp.left = allocValue();
@@ -726,7 +732,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
} }
return; return;
} }
if (fun.type != tLambda) if (fun.type != tLambda)
throwTypeError("attempt to call something which is not a function but %1%", throwTypeError("attempt to call something which is not a function but %1%",
showType(fun)); showType(fun));
@@ -744,7 +750,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
else { else {
forceAttrs(arg); forceAttrs(arg);
if (!fun.lambda.fun->arg.empty()) if (!fun.lambda.fun->arg.empty())
env2.values[displ++] = &arg; env2.values[displ++] = &arg;
@@ -755,8 +761,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) { foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
Bindings::iterator j = arg.attrs->find(i->name); Bindings::iterator j = arg.attrs->find(i->name);
if (j == arg.attrs->end()) { if (j == arg.attrs->end()) {
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'", if (!i->def) throwTypeError("%1% called without required argument `%2%'",
fun.lambda.fun->pos, i->name); fun.lambda.fun->showNamePos(), i->name);
env2.values[displ++] = i->def->maybeThunk(*this, env2); env2.values[displ++] = i->def->maybeThunk(*this, env2);
} else { } else {
attrsUsed++; attrsUsed++;
@@ -765,20 +771,24 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
} }
/* Check that each actual argument is listed as a formal /* Check that each actual argument is listed as a formal
argument (unless the attribute match specifies a `...'). argument (unless the attribute match specifies a `...'). */
TODO: show the names of the expected/unexpected if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) {
arguments. */ /* Nope, so show the first unexpected argument to the
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) user. */
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos); foreach (Bindings::iterator, i, *arg.attrs)
if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end())
throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name);
abort(); // can't happen
}
} }
nrFunctionCalls++; nrFunctionCalls++;
if (countCalls) functionCalls[fun.lambda.fun->pos]++; if (countCalls) functionCalls[fun.lambda.fun]++;
try { try {
fun.lambda.fun->body->eval(*this, env2, v); fun.lambda.fun->body->eval(*this, env2, v);
} catch (Error & e) { } catch (Error & e) {
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos); addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos());
throw; throw;
} }
} }
@@ -815,9 +825,8 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
Env & env2(state.allocEnv(1)); Env & env2(state.allocEnv(1));
env2.up = &env; env2.up = &env;
env2.prevWith = prevWith; env2.prevWith = prevWith;
env2.haveWithAttrs = false;
env2.values[0] = state.allocValue(); env2.values[0] = (Value *) attrs;
state.evalAttrs(env, attrs, *env2.values[0]);
body->eval(state, env2, v); body->eval(state, env2, v);
} }
@@ -828,7 +837,7 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v)
(state.evalBool(env, cond) ? then : else_)->eval(state, env, v); (state.evalBool(env, cond) ? then : else_)->eval(state, env, v);
} }
void ExprAssert::eval(EvalState & state, Env & env, Value & v) void ExprAssert::eval(EvalState & state, Env & env, Value & v)
{ {
if (!state.evalBool(env, cond)) if (!state.evalBool(env, cond))
@@ -836,7 +845,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
body->eval(state, env, v); body->eval(state, env, v);
} }
void ExprOpNot::eval(EvalState & state, Env & env, Value & v) void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
{ {
mkBool(v, !state.evalBool(env, e)); mkBool(v, !state.evalBool(env, e));
@@ -890,9 +899,8 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
state.mkAttrs(v, v1.attrs->size() + v2.attrs->size()); state.mkAttrs(v, v1.attrs->size() + v2.attrs->size());
/* Merge the attribute sets, preferring values from the second /* Merge the sets, preferring values from the second set. Make
set. Make sure to keep the resulting vector in sorted sure to keep the resulting vector in sorted order. */
order. */
Bindings::iterator i = v1.attrs->begin(); Bindings::iterator i = v1.attrs->begin();
Bindings::iterator j = v2.attrs->begin(); Bindings::iterator j = v2.attrs->begin();
@@ -909,7 +917,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
while (i != v1.attrs->end()) v.attrs->push_back(*i++); while (i != v1.attrs->end()) v.attrs->push_back(*i++);
while (j != v2.attrs->end()) v.attrs->push_back(*j++); while (j != v2.attrs->end()) v.attrs->push_back(*j++);
state.nrOpUpdateValuesCopied += v.attrs->size(); state.nrOpUpdateValuesCopied += v.attrs->size();
} }
@@ -954,31 +962,39 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
{ {
PathSet context; PathSet context;
std::ostringstream s; std::ostringstream s;
NixInt n = 0;
bool first = true, isPath = false;
Value vStr; bool first = !forceString;
ValueType firstType = tString;
foreach (vector<Expr *>::iterator, i, *es) { foreach (vector<Expr *>::iterator, i, *es) {
(*i)->eval(state, env, vStr); Value vTmp;
(*i)->eval(state, env, vTmp);
/* If the first element is a path, then the result will also /* If the first element is a path, then the result will also
be a path, we don't copy anything (yet - that's done later, be a path, we don't copy anything (yet - that's done later,
since paths are copied when they are used in a derivation), since paths are copied when they are used in a derivation),
and none of the strings are allowed to have contexts. */ and none of the strings are allowed to have contexts. */
if (first) { if (first) {
isPath = vStr.type == tPath; firstType = vTmp.type;
first = false; first = false;
} }
s << state.coerceToString(vStr, context, false, !isPath); 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 == tString);
} }
if (isPath && !context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
if (isPath) if (firstType == tInt)
mkInt(v, n);
else if (firstType == tPath) {
if (!context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
mkPath(v, s.str().c_str()); mkPath(v, s.str().c_str());
else } else
mkString(v, s.str(), context); mkString(v, s.str(), context);
} }
@@ -986,12 +1002,12 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
void EvalState::strictForceValue(Value & v) void EvalState::strictForceValue(Value & v)
{ {
forceValue(v); forceValue(v);
if (v.type == tAttrs) { if (v.type == tAttrs) {
foreach (Bindings::iterator, i, *v.attrs) foreach (Bindings::iterator, i, *v.attrs)
strictForceValue(*i->value); strictForceValue(*i->value);
} }
else if (v.type == tList) { else if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.list.length; ++n)
strictForceValue(*v.list.elems[n]); strictForceValue(*v.list.elems[n]);
@@ -999,7 +1015,7 @@ void EvalState::strictForceValue(Value & v)
} }
int EvalState::forceInt(Value & v) NixInt EvalState::forceInt(Value & v)
{ {
forceValue(v); forceValue(v);
if (v.type != tInt) if (v.type != tInt)
@@ -1037,7 +1053,7 @@ string EvalState::forceString(Value & v)
void copyContext(const Value & v, PathSet & context) void copyContext(const Value & v, PathSet & context)
{ {
if (v.string.context) if (v.string.context)
for (const char * * p = v.string.context; *p; ++p) for (const char * * p = v.string.context; *p; ++p)
context.insert(*p); context.insert(*p);
} }
@@ -1067,7 +1083,7 @@ bool EvalState::isDerivation(Value & v)
if (i == v.attrs->end()) return false; if (i == v.attrs->end()) return false;
forceValue(*i->value); forceValue(*i->value);
if (i->value->type != tString) return false; if (i->value->type != tString) return false;
return forceStringNoCtx(*i->value) == "derivation"; return strcmp(i->value->string.s, "derivation") == 0;
} }
@@ -1087,7 +1103,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
Path path(canonPath(v.path)); Path path(canonPath(v.path));
if (!copyToStore) return path; if (!copyToStore) return path;
if (nix::isDerivation(path)) if (nix::isDerivation(path))
throwEvalError("file names are not allowed to end in `%1%'", drvExtension); throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
@@ -1109,8 +1125,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (v.type == tAttrs) { if (v.type == tAttrs) {
Bindings::iterator i = v.attrs->find(sOutPath); Bindings::iterator i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string");
throwTypeError("cannot coerce an attribute set (except a derivation) to a string");
return coerceToString(*i->value, context, coerceMore, copyToStore); return coerceToString(*i->value, context, coerceMore, copyToStore);
} }
@@ -1136,7 +1151,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
return result; return result;
} }
} }
throwTypeError("cannot coerce %1% to a string", showType(v)); throwTypeError("cannot coerce %1% to a string", showType(v));
} }
@@ -1156,9 +1171,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
forceValue(v2); forceValue(v2);
/* !!! Hack to support some old broken code that relies on pointer /* !!! Hack to support some old broken code that relies on pointer
equality tests between attribute sets. (Specifically, equality tests between sets. (Specifically, builderDefs calls
builderDefs calls uniqList on a list of attribute sets.) Will uniqList on a list of sets.) Will remove this eventually. */
remove this eventually. */
if (&v1 == &v2) return true; if (&v1 == &v2) return true;
if (v1.type != v2.type) return false; if (v1.type != v2.type) return false;
@@ -1196,8 +1210,8 @@ bool EvalState::eqValues(Value & v1, Value & v2)
return true; return true;
case tAttrs: { case tAttrs: {
/* If both attribute sets denote a derivation (type = /* If both sets denote a derivation (type = "derivation"),
"derivation"), then compare their outPaths. */ then compare their outPaths. */
if (isDerivation(v1) && isDerivation(v2)) { if (isDerivation(v1) && isDerivation(v2)) {
Bindings::iterator i = v1.attrs->find(sOutPath); Bindings::iterator i = v1.attrs->find(sOutPath);
Bindings::iterator j = v2.attrs->find(sOutPath); Bindings::iterator j = v2.attrs->find(sOutPath);
@@ -1212,7 +1226,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
if (i->name != j->name || !eqValues(*i->value, *j->value)) if (i->name != j->name || !eqValues(*i->value, *j->value))
return false; return false;
return true; return true;
} }
@@ -1247,10 +1261,11 @@ void EvalState::printStats()
printMsg(v, format(" list concatenations: %1%") % nrListConcats); printMsg(v, format(" list concatenations: %1%") % nrListConcats);
printMsg(v, format(" values allocated: %1% (%2% bytes)") printMsg(v, format(" values allocated: %1% (%2% bytes)")
% nrValues % (nrValues * sizeof(Value))); % 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(" right-biased unions: %1%") % nrOpUpdates);
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied); printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
printMsg(v, format(" symbols in symbol table: %1%") % symbols.size()); 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: %1%") % nrThunks);
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided); printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
printMsg(v, format(" number of attr lookups: %1%") % nrLookups); printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
@@ -1258,26 +1273,27 @@ void EvalState::printStats()
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls); printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
if (countCalls) { if (countCalls) {
v = lvlInfo;
printMsg(v, format("calls to %1% primops:") % primOpCalls.size()); printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_; typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
std::multimap<unsigned int, Symbol> primOpCalls_; PrimOpCalls_ primOpCalls_;
foreach (PrimOpCalls::iterator, i, primOpCalls) foreach (PrimOpCalls::iterator, i, primOpCalls)
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first)); primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_) foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
printMsg(v, format("%1$10d %2%") % i->first % i->second); printMsg(v, format("%1$10d %2%") % i->first % i->second);
printMsg(v, format("calls to %1% functions:") % functionCalls.size()); printMsg(v, format("calls to %1% functions:") % functionCalls.size());
typedef std::multimap<unsigned int, Pos> FunctionCalls_; typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
std::multimap<unsigned int, Pos> functionCalls_; FunctionCalls_ functionCalls_;
foreach (FunctionCalls::iterator, i, functionCalls) foreach (FunctionCalls::iterator, i, functionCalls)
functionCalls_.insert(std::pair<unsigned int, Pos>(i->second, i->first)); functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_) foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
printMsg(v, format("%1$10d %2%") % i->first % i->second); printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size()); printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
typedef std::multimap<unsigned int, Pos> AttrSelects_; typedef std::multimap<unsigned int, Pos> AttrSelects_;
std::multimap<unsigned int, Pos> attrSelects_; AttrSelects_ attrSelects_;
foreach (AttrSelects::iterator, i, attrSelects) foreach (AttrSelects::iterator, i, attrSelects)
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first)); attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_) foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
+39 -30
View File
@@ -19,8 +19,8 @@ class EvalState;
struct Attr; struct Attr;
/* Attribute sets are represented as a vector of attributes, sorted by /* Sets are represented as a vector of attributes, sorted by symbol
symbol (i.e. pointer to the attribute name in the symbol table). */ (i.e. pointer to the attribute name in the symbol table). */
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
typedef std::vector<Attr, gc_allocator<Attr> > BindingsBase; typedef std::vector<Attr, gc_allocator<Attr> > BindingsBase;
#else #else
@@ -52,7 +52,8 @@ struct PrimOp
struct Env struct Env
{ {
Env * up; Env * up;
unsigned int prevWith; // nr of levels up to next `with' environment unsigned short prevWith; // nr of levels up to next `with' environment
bool haveWithAttrs;
Value * values[0]; Value * values[0];
}; };
@@ -87,23 +88,21 @@ struct EvalState;
std::ostream & operator << (std::ostream & str, const Value & v); std::ostream & operator << (std::ostream & str, const Value & v);
class EvalState class EvalState
{ {
public: public:
SymbolTable symbols; SymbolTable symbols;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls; sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they /* If set, force copying files to the Nix store even if they
already exist there. */ already exist there. */
bool repair; bool repair;
private: private:
SrcToStore srcToStore; SrcToStore srcToStore;
/* A cache from path names to parse trees. */
std::map<Path, Expr *> parseTrees;
/* A cache from path names to values. */ /* A cache from path names to values. */
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
@@ -118,23 +117,25 @@ private:
SearchPath::iterator searchPathInsertionPoint; SearchPath::iterator searchPathInsertionPoint;
public: public:
EvalState(); EvalState();
~EvalState(); ~EvalState();
void addToSearchPath(const string & s); void addToSearchPath(const string & s);
/* Parse a Nix expression from the specified file. If `path' /* Parse a Nix expression from the specified file. */
refers to a directory, then "/default.nix" is appended. */ Expr * parseExprFromFile(const Path & path);
Expr * parseExprFromFile(Path path);
/* Parse a Nix expression from the specified string. */ /* Parse a Nix expression from the specified string. */
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
Expr * parseExprFromString(const string & s, const Path & basePath); Expr * parseExprFromString(const string & s, const Path & basePath);
/* Evaluate an expression read from the given file to normal /* Evaluate an expression read from the given file to normal
form. */ form. */
void evalFile(const Path & path, Value & v); void evalFile(const Path & path, Value & v);
void resetFileCache();
/* Look up a file in the search path. */ /* Look up a file in the search path. */
Path findFile(const string & path); Path findFile(const string & path);
@@ -158,7 +159,7 @@ public:
void strictForceValue(Value & v); void strictForceValue(Value & v);
/* Force `v', and then verify that it has the expected type. */ /* Force `v', and then verify that it has the expected type. */
int forceInt(Value & v); NixInt forceInt(Value & v);
bool forceBool(Value & v); bool forceBool(Value & v);
inline void forceAttrs(Value & v); inline void forceAttrs(Value & v);
inline void forceList(Value & v); inline void forceList(Value & v);
@@ -174,7 +175,7 @@ public:
/* String coercion. Converts strings, paths and derivations to a /* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers, string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set, booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect.q */ referenced paths are copied to the Nix store as a side effect. */
string coerceToString(Value & v, PathSet & context, string coerceToString(Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true); bool coerceMore = false, bool copyToStore = true);
@@ -183,39 +184,43 @@ public:
path. Nothing is copied to the store. */ path. Nothing is copied to the store. */
Path coerceToPath(Value & v, PathSet & context); Path coerceToPath(Value & v, PathSet & context);
private: public:
/* The base environment, containing the builtin functions and /* The base environment, containing the builtin functions and
values. */ values. */
Env & baseEnv; Env & baseEnv;
unsigned int baseEnvDispl;
public:
/* The same, but used during parsing to resolve variables. */ /* The same, but used during parsing to resolve variables. */
StaticEnv staticBaseEnv; // !!! should be private StaticEnv staticBaseEnv; // !!! should be private
private: private:
unsigned int baseEnvDispl;
void createBaseEnv(); void createBaseEnv();
void addConstant(const string & name, Value & v); void addConstant(const string & name, Value & v);
void addPrimOp(const string & name, void addPrimOp(const string & name,
unsigned int arity, PrimOpFun primOp); unsigned int arity, PrimOpFun primOp);
inline Value * lookupVar(Env * env, const VarRef & var); public:
void getBuiltin(const string & name, Value & v);
private:
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
friend class ExprVar; friend class ExprVar;
friend class ExprAttrs; friend class ExprAttrs;
friend class ExprLet; friend class ExprLet;
Expr * parse(const char * text, Expr * parse(const char * text, const Path & path,
const Path & path, const Path & basePath); const Path & basePath, StaticEnv & staticEnv);
public: public:
/* Do a deep equality test between two values. That is, list /* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */ elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2); bool eqValues(Value & v1, Value & v2);
@@ -225,7 +230,7 @@ public:
/* Automatically call a function for which each argument has a /* Automatically call a function for which each argument has a
default value or has a binding in the `args' map. */ default value or has a binding in the `args' map. */
void autoCallFunction(Bindings & args, Value & fun, Value & res); void autoCallFunction(Bindings & args, Value & fun, Value & res);
/* Allocation primitives. */ /* Allocation primitives. */
Value * allocValue(); Value * allocValue();
Env & allocEnv(unsigned int size); Env & allocEnv(unsigned int size);
@@ -259,7 +264,7 @@ private:
typedef std::map<Symbol, unsigned int> PrimOpCalls; typedef std::map<Symbol, unsigned int> PrimOpCalls;
PrimOpCalls primOpCalls; PrimOpCalls primOpCalls;
typedef std::map<Pos, unsigned int> FunctionCalls; typedef std::map<ExprLambda *, unsigned int> FunctionCalls;
FunctionCalls functionCalls; FunctionCalls functionCalls;
typedef std::map<Pos, unsigned int> AttrSelects; typedef std::map<Pos, unsigned int> AttrSelects;
@@ -276,4 +281,8 @@ private:
string showType(const Value & v); string showType(const Value & v);
/* If `path' refers to a directory, then append "/default.nix". */
Path resolveExprPath(Path path);
} }
+10 -12
View File
@@ -41,7 +41,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
/* For each output... */ /* For each output... */
for (unsigned int j = 0; j < i->value->list.length; ++j) { 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]); string name = state.forceStringNoCtx(*i->value->list.elems[j]);
Bindings::iterator out = attrs->find(state.symbols.create(name)); Bindings::iterator out = attrs->find(state.symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error? if (out == attrs->end()) continue; // FIXME: throw error?
@@ -119,11 +119,10 @@ void DrvInfo::setMetaInfo(const MetaInfo & meta)
typedef set<Bindings *> Done; typedef set<Bindings *> Done;
/* Evaluate value `v'. If it evaluates to an attribute set of type /* Evaluate value `v'. If it evaluates to a set of type `derivation',
`derivation', then put information about it in `drvs' (unless it's then put information about it in `drvs' (unless it's already in
already in `doneExprs'). The result boolean indicates whether it `doneExprs'). The result boolean indicates whether it makes sense
makes sense for the caller to recursively search for derivations in for the caller to recursively search for derivations in `v'. */
`v'. */
static bool getDerivation(EvalState & state, Value & v, static bool getDerivation(EvalState & state, Value & v,
const string & attrPath, DrvInfos & drvs, Done & done, const string & attrPath, DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures) bool ignoreAssertionFailures)
@@ -132,8 +131,8 @@ static bool getDerivation(EvalState & state, Value & v,
state.forceValue(v); state.forceValue(v);
if (!state.isDerivation(v)) return true; if (!state.isDerivation(v)) return true;
/* Remove spurious duplicates (e.g., an attribute set like /* Remove spurious duplicates (e.g., a set like `rec { x =
`rec { x = derivation {...}; y = x;}'. */ derivation {...}; y = x;}'. */
if (done.find(v.attrs) != done.end()) return false; if (done.find(v.attrs) != done.end()) return false;
done.insert(v.attrs); done.insert(v.attrs);
@@ -218,10 +217,9 @@ static void getDerivations(EvalState & state, Value & vIn,
if (combineChannels) if (combineChannels)
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) { else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
/* If the value of this attribute is itself an /* If the value of this attribute is itself a set,
attribute set, should we recurse into it? => Only should we recurse into it? => Only if it has a
if it has a `recurseForDerivations = true' `recurseForDerivations = true' attribute. */
attribute. */
if (v2.type == tAttrs) { if (v2.type == tAttrs) {
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations")); Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
if (j != v2.attrs->end() && state.forceBool(*j->value)) if (j != v2.attrs->end() && state.forceBool(*j->value))
+11 -8
View File
@@ -9,21 +9,20 @@
%{ %{
#include "nixexpr.hh" #include "nixexpr.hh"
#define BISON_HEADER_HACK
#include "parser-tab.hh" #include "parser-tab.hh"
using namespace nix; using namespace nix;
namespace nix { namespace nix {
static void initLoc(YYLTYPE * loc) static void initLoc(YYLTYPE * loc)
{ {
loc->first_line = loc->last_line = 1; loc->first_line = loc->last_line = 1;
loc->first_column = loc->last_column = 1; loc->first_column = loc->last_column = 1;
} }
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{ {
loc->first_line = loc->last_line; loc->first_line = loc->last_line;
@@ -35,7 +34,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
if (*s == '\n') /* cr/lf */ if (*s == '\n') /* cr/lf */
s++; s++;
/* fall through */ /* fall through */
case '\n': case '\n':
++loc->last_line; ++loc->last_line;
loc->last_column = 1; loc->last_column = 1;
break; break;
@@ -69,7 +68,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
return new ExprString(symbols.create(t)); return new ExprString(symbols.create(t));
} }
} }
#define YY_USER_INIT initLoc(yylloc) #define YY_USER_INIT initLoc(yylloc)
@@ -102,6 +101,8 @@ or { return OR_KW; }
\=\= { return EQ; } \=\= { return EQ; }
\!\= { return NEQ; } \!\= { return NEQ; }
\<\= { return LEQ; }
\>\= { return GEQ; }
\&\& { return AND; } \&\& { return AND; }
\|\| { return OR; } \|\| { return OR; }
\-\> { return IMPL; } \-\> { return IMPL; }
@@ -109,8 +110,10 @@ or { return OR_KW; }
\+\+ { return CONCAT; } \+\+ { return CONCAT; }
{ID} { yylval->id = strdup(yytext); return ID; } {ID} { yylval->id = strdup(yytext); return ID; }
{INT} { int n = atoi(yytext); /* !!! overflow */ {INT} { errno = 0;
yylval->n = n; yylval->n = strtol(yytext, 0, 10);
if (errno != 0)
throw ParseError(format("invalid integer `%1%'") % yytext);
return INT; return INT;
} }
@@ -168,7 +171,7 @@ or { return OR_KW; }
namespace nix { namespace nix {
/* Horrible, disgusting hack: allow the parser to set the scanner /* Horrible, disgusting hack: allow the parser to set the scanner
start condition back to STRING. Necessary in interpolations like start condition back to STRING. Necessary in interpolations like
"foo${expr}bar"; after the close brace we have to go back to the "foo${expr}bar"; after the close brace we have to go back to the
+4 -5
View File
@@ -55,7 +55,7 @@ static string nextComponent(string::const_iterator & p,
else else
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-')) while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
s += *p++; s += *p++;
return s; return s;
} }
@@ -80,7 +80,7 @@ int compareVersions(const string & v1, const string & v2)
{ {
string::const_iterator p1 = v1.begin(); string::const_iterator p1 = v1.begin();
string::const_iterator p2 = v2.begin(); string::const_iterator p2 = v2.begin();
while (p1 != v1.end() || p2 != v2.end()) { while (p1 != v1.end() || p2 != v2.end()) {
string c1 = nextComponent(p1, v1.end()); string c1 = nextComponent(p1, v1.end());
string c2 = nextComponent(p2, v2.end()); string c2 = nextComponent(p2, v2.end());
@@ -95,11 +95,10 @@ int compareVersions(const string & v1, const string & v2)
DrvNames drvNamesFromArgs(const Strings & opArgs) DrvNames drvNamesFromArgs(const Strings & opArgs)
{ {
DrvNames result; DrvNames result;
for (Strings::const_iterator i = opArgs.begin(); foreach (Strings::const_iterator, i, opArgs)
i != opArgs.end(); ++i)
result.push_back(DrvName(*i)); result.push_back(DrvName(*i));
return result; return result;
} }
} }
+47 -23
View File
@@ -38,7 +38,7 @@ void ExprPath::show(std::ostream & str)
void ExprVar::show(std::ostream & str) void ExprVar::show(std::ostream & str)
{ {
str << info.name; str << name;
} }
void ExprSelect::show(std::ostream & str) 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, /* Check whether the variable appears in the environment. If so,
set its level and displacement. */ set its level and displacement. */
@@ -198,17 +198,12 @@ void VarRef::bind(const StaticEnv & env)
/* Otherwise, the variable must be obtained from the nearest /* Otherwise, the variable must be obtained from the nearest
enclosing `with'. If there is no `with', then we can issue an enclosing `with'. If there is no `with', then we can issue an
"undefined variable" error now. */ "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; fromWith = true;
this->level = withLevel; this->level = withLevel;
} }
void ExprVar::bindVars(const StaticEnv & env)
{
info.bind(env);
}
void ExprSelect::bindVars(const StaticEnv & env) void ExprSelect::bindVars(const StaticEnv & env)
{ {
e->bindVars(env); e->bindVars(env);
@@ -224,20 +219,18 @@ void ExprAttrs::bindVars(const StaticEnv & env)
{ {
if (recursive) { if (recursive) {
StaticEnv newEnv(false, &env); StaticEnv newEnv(false, &env);
unsigned int displ = 0; unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
newEnv.vars[i->first] = i->second.displ = displ++; newEnv.vars[i->first] = i->second.displ = displ++;
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
if (i->second.inherited) i->second.var.bind(env); i->second.e->bindVars(i->second.inherited ? env : newEnv);
else i->second.e->bindVars(newEnv);
} }
else else
foreach (AttrDefs::iterator, i, attrs) foreach (AttrDefs::iterator, i, attrs)
if (i->second.inherited) i->second.var.bind(env); i->second.e->bindVars(env);
else i->second.e->bindVars(env);
} }
void ExprList::bindVars(const StaticEnv & env) void ExprList::bindVars(const StaticEnv & env)
@@ -249,9 +242,9 @@ void ExprList::bindVars(const StaticEnv & env)
void ExprLambda::bindVars(const StaticEnv & env) void ExprLambda::bindVars(const StaticEnv & env)
{ {
StaticEnv newEnv(false, &env); StaticEnv newEnv(false, &env);
unsigned int displ = 0; unsigned int displ = 0;
if (!arg.empty()) newEnv.vars[arg] = displ++; if (!arg.empty()) newEnv.vars[arg] = displ++;
if (matchAttrs) { if (matchAttrs) {
@@ -268,15 +261,14 @@ void ExprLambda::bindVars(const StaticEnv & env)
void ExprLet::bindVars(const StaticEnv & env) void ExprLet::bindVars(const StaticEnv & env)
{ {
StaticEnv newEnv(false, &env); StaticEnv newEnv(false, &env);
unsigned int displ = 0; unsigned int displ = 0;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs) foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
newEnv.vars[i->first] = i->second.displ = displ++; newEnv.vars[i->first] = i->second.displ = displ++;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs) foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
if (i->second.inherited) i->second.var.bind(env); i->second.e->bindVars(i->second.inherited ? env : newEnv);
else i->second.e->bindVars(newEnv);
body->bindVars(newEnv); body->bindVars(newEnv);
} }
@@ -293,8 +285,8 @@ void ExprWith::bindVars(const StaticEnv & env)
prevWith = level; prevWith = level;
break; break;
} }
attrs->bindVars(env); attrs->bindVars(env);
StaticEnv newEnv(true, &env); StaticEnv newEnv(true, &env);
body->bindVars(newEnv); body->bindVars(newEnv);
} }
@@ -324,4 +316,36 @@ void ExprConcatStrings::bindVars(const StaticEnv & env)
} }
/* Storing function names. */
void Expr::setName(Symbol & name)
{
}
void ExprLambda::setName(Symbol & name)
{
this->name = name;
body->setName(name);
}
string ExprLambda::showNamePos()
{
return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str();
}
/* Symbol table. */
size_t SymbolTable::totalSize() const
{
size_t n = 0;
foreach (Symbols::const_iterator, i, symbols)
n += i->size();
return n;
}
} }
+23 -23
View File
@@ -16,20 +16,23 @@ MakeError(ThrownError, AssertionError)
MakeError(Abort, EvalError) MakeError(Abort, EvalError)
MakeError(TypeError, EvalError) MakeError(TypeError, EvalError)
MakeError(ImportError, EvalError) // error building an imported derivation MakeError(ImportError, EvalError) // error building an imported derivation
MakeError(UndefinedVarError, Error)
/* Position objects. */ /* Position objects. */
struct Pos struct Pos
{ {
string file; Symbol file;
unsigned int line, column; unsigned int line, column;
Pos() : line(0), column(0) { }; 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) { }; : file(file), line(line), column(column) { };
bool operator < (const Pos & p2) const 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 true;
if (d > 0) return false; if (d > 0) return false;
if (line < p2.line) return true; if (line < p2.line) return true;
@@ -63,6 +66,7 @@ struct Expr
virtual void bindVars(const StaticEnv & env); virtual void bindVars(const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v); virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol & name);
}; };
std::ostream & operator << (std::ostream & str, Expr & e); std::ostream & operator << (std::ostream & str, Expr & e);
@@ -74,9 +78,9 @@ std::ostream & operator << (std::ostream & str, Expr & e);
struct ExprInt : Expr struct ExprInt : Expr
{ {
int n; NixInt n;
Value v; Value v;
ExprInt(int n) : n(n) { mkInt(v, n); }; ExprInt(NixInt n) : n(n) { mkInt(v, n); };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@@ -106,32 +110,25 @@ struct ExprPath : Expr
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
struct VarRef struct ExprVar : Expr
{ {
Pos pos;
Symbol name; Symbol name;
/* Whether the variable comes from an environment (e.g. a rec, let /* Whether the variable comes from an environment (e.g. a rec, let
or function argument) or from a "with". */ or function argument) or from a "with". */
bool fromWith; bool fromWith;
/* In the former case, the value is obtained by going `level' /* In the former case, the value is obtained by going `level'
levels up from the current environment and getting the levels up from the current environment and getting the
`displ'th value in that environment. In the latter case, the `displ'th value in that environment. In the latter case, the
value is obtained by getting the attribute named `name' from value is obtained by getting the attribute named `name' from
the attribute set stored in the environment that is `level' the set stored in the environment that is `level' levels up
levels up from the current one.*/ from the current one.*/
unsigned int level; unsigned int level;
unsigned int displ; unsigned int displ;
VarRef() { }; ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
VarRef(const Symbol & name) : name(name) { };
void bind(const StaticEnv & env);
};
struct ExprVar : Expr
{
VarRef info;
ExprVar(const Symbol & name) : info(name) { };
COMMON_METHODS COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env); Value * maybeThunk(EvalState & state, Env & env);
}; };
@@ -158,12 +155,10 @@ struct ExprAttrs : Expr
bool recursive; bool recursive;
struct AttrDef { struct AttrDef {
bool inherited; bool inherited;
Expr * e; // if not inherited Expr * e;
VarRef var; // if inherited
Pos pos; Pos pos;
unsigned int displ; // displacement unsigned int displ; // displacement
AttrDef(Expr * e, const Pos & pos) : inherited(false), e(e), pos(pos) { }; AttrDef(Expr * e, const Pos & pos, bool inherited=false) : inherited(inherited), e(e), pos(pos) { };
AttrDef(const Symbol & name, const Pos & pos) : inherited(true), var(name), pos(pos) { };
AttrDef() { }; AttrDef() { };
}; };
typedef std::map<Symbol, AttrDef> AttrDefs; typedef std::map<Symbol, AttrDef> AttrDefs;
@@ -197,6 +192,7 @@ struct Formals
struct ExprLambda : Expr struct ExprLambda : Expr
{ {
Pos pos; Pos pos;
Symbol name;
Symbol arg; Symbol arg;
bool matchAttrs; bool matchAttrs;
Formals * formals; Formals * formals;
@@ -208,6 +204,8 @@ struct ExprLambda : Expr
throw ParseError(format("duplicate formal function argument `%1%' at %2%") throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% arg % pos); % arg % pos);
}; };
void setName(Symbol & name);
string showNamePos();
COMMON_METHODS COMMON_METHODS
}; };
@@ -277,8 +275,10 @@ MakeBinOp(OpConcatLists, "++")
struct ExprConcatStrings : Expr struct ExprConcatStrings : Expr
{ {
bool forceString;
vector<Expr *> * es; vector<Expr *> * es;
ExprConcatStrings(vector<Expr *> * es) : es(es) { }; ExprConcatStrings(bool forceString, vector<Expr *> * es)
: forceString(forceString), es(es) { };
COMMON_METHODS COMMON_METHODS
}; };
+65 -53
View File
@@ -4,32 +4,32 @@
%error-verbose %error-verbose
%defines %defines
/* %no-lines */ /* %no-lines */
%parse-param { yyscan_t scanner } %parse-param { void * scanner }
%parse-param { ParseData * data } %parse-param { nix::ParseData * data }
%lex-param { yyscan_t scanner } %lex-param { void * scanner }
%lex-param { ParseData * data } %lex-param { nix::ParseData * data }
%expect 1 %expect 1
%expect-rr 1 %expect-rr 1
%code requires { %code requires {
#ifndef BISON_HEADER #ifndef BISON_HEADER
#define BISON_HEADER #define BISON_HEADER
#include "util.hh" #include "util.hh"
#include "nixexpr.hh" #include "nixexpr.hh"
#include "eval.hh" #include "eval.hh"
namespace nix { namespace nix {
struct ParseData struct ParseData
{ {
EvalState & state; EvalState & state;
SymbolTable & symbols; SymbolTable & symbols;
Expr * result; Expr * result;
Path basePath; Path basePath;
Path path; Symbol path;
string error; string error;
Symbol sLetBody; Symbol sLetBody;
ParseData(EvalState & state) ParseData(EvalState & state)
@@ -38,7 +38,7 @@ namespace nix {
, sLetBody(symbols.create("<let-body>")) , sLetBody(symbols.create("<let-body>"))
{ }; { };
}; };
} }
#define YY_DECL int yylex \ #define YY_DECL int yylex \
@@ -52,7 +52,6 @@ namespace nix {
#include "parser-tab.hh" #include "parser-tab.hh"
#include "lexer-tab.hh" #include "lexer-tab.hh"
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -64,14 +63,14 @@ using namespace nix;
namespace nix { namespace nix {
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos) static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
{ {
throw ParseError(format("attribute `%1%' at %2% already defined at %3%") throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos); % showAttrPath(attrPath) % pos % prevPos);
} }
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos) static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
{ {
@@ -79,7 +78,7 @@ static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
throw ParseError(format("attribute `%1%' at %2% already defined at %3%") throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % pos % prevPos); % showAttrPath(attrPath) % pos % prevPos);
} }
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath, static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const Pos & pos) Expr * e, const Pos & pos)
@@ -105,6 +104,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
} }
} }
} }
e->setName(attrPath.back());
} }
@@ -121,7 +121,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es) static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
{ {
if (es.empty()) return new ExprString(symbols.create("")); if (es.empty()) return new ExprString(symbols.create(""));
/* Figure out the minimum indentation. Note that by design /* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So whitespace-only final lines are not taken into account. (So
the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */ the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
@@ -170,7 +170,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
es2->push_back(*i); es2->push_back(*i);
continue; continue;
} }
string s2; string s2;
for (unsigned int j = 0; j < e->s.size(); ++j) { for (unsigned int j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) { if (atStartOfLine) {
@@ -203,7 +203,8 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
es2->push_back(new ExprString(symbols.create(s2))); es2->push_back(new ExprString(symbols.create(s2)));
} }
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(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);
} }
@@ -232,13 +233,13 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%} %}
%union { %union {
// !!! We're probably leaking stuff here. // !!! We're probably leaking stuff here.
nix::Expr * e; nix::Expr * e;
nix::ExprList * list; nix::ExprList * list;
nix::ExprAttrs * attrs; nix::ExprAttrs * attrs;
nix::Formals * formals; nix::Formals * formals;
nix::Formal * formal; nix::Formal * formal;
int n; nix::NixInt n;
char * id; // !!! -> Symbol char * id; // !!! -> Symbol
char * path; char * path;
char * uri; char * uri;
@@ -269,12 +270,15 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%left OR %left OR
%left AND %left AND
%nonassoc EQ NEQ %nonassoc EQ NEQ
%left '<' '>' LEQ GEQ
%right UPDATE %right UPDATE
%left NEG %left NOT
%left '+' %left '+' '-'
%left '*' '/'
%right CONCAT %right CONCAT
%nonassoc '?' %nonassoc '?'
%nonassoc '~' %nonassoc '~'
%nonassoc NEGATE
%% %%
@@ -306,9 +310,14 @@ expr_if
; ;
expr_op expr_op
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); } : '!' expr_op %prec NOT { $$ = new ExprOpNot($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 EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($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 AND expr_op { $$ = new ExprOpAnd($1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
@@ -318,8 +327,11 @@ expr_op
{ vector<Expr *> * l = new vector<Expr *>; { vector<Expr *> * l = new vector<Expr *>;
l->push_back($1); l->push_back($1);
l->push_back($3); l->push_back($3);
$$ = new ExprConcatStrings(l); $$ = new ExprConcatStrings(false, l);
} }
| 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_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
| expr_app | expr_app
; ;
@@ -338,18 +350,18 @@ expr_select
| /* Backwards compatibility: because Nixpkgs has a rarely used | /* Backwards compatibility: because Nixpkgs has a rarely used
function named or, allow stuff like map or [...]. */ function named or, allow stuff like map or [...]. */
expr_simple OR_KW 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 { $$ = $1; }
; ;
expr_simple expr_simple
: ID { $$ = new ExprVar(data->symbols.create($1)); } : ID { $$ = new ExprVar(CUR_POS, data->symbols.create($1)); }
| INT { $$ = new ExprInt($1); } | INT { $$ = new ExprInt($1); }
| '"' string_parts '"' { | '"' string_parts '"' {
/* For efficiency, and to simplify parse trees a bit. */ /* For efficiency, and to simplify parse trees a bit. */
if ($2->empty()) $$ = new ExprString(data->symbols.create("")); 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($2); else $$ = new ExprConcatStrings(true, $2);
} }
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE { | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(data->symbols, *$2); $$ = stripIndentation(data->symbols, *$2);
@@ -361,10 +373,10 @@ expr_simple
/* The file wasn't found in the search path. However, we can't /* The file wasn't found in the search path. However, we can't
throw an error here, because the expression might never be throw an error here, because the expression might never be
evaluated. So return an expression that lazily calls evaluated. So return an expression that lazily calls
abort. */ throw. */
$$ = path2 == "" $$ = path2 == ""
? (Expr * ) new ExprApp( ? (Expr * ) new ExprApp(
new ExprVar(data->symbols.create("throw")), new ExprVar(noPos, data->symbols.create("throw")),
new ExprString(data->symbols.create( 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()))) (format("file `%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)") % path).str())))
: (Expr * ) new ExprPath(path2); : (Expr * ) new ExprPath(path2);
@@ -402,7 +414,7 @@ binds
if ($$->attrs.find(*i) != $$->attrs.end()) if ($$->attrs.find(*i) != $$->attrs.end())
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos); dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
Pos pos = makeCurPos(@3, data); Pos pos = makeCurPos(@3, data);
$$->attrs[*i] = ExprAttrs::AttrDef(*i, pos); $$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, *i), pos, true);
} }
} }
| binds INHERIT '(' expr ')' attrs ';' | binds INHERIT '(' expr ')' attrs ';'
@@ -454,7 +466,7 @@ formal
: ID { $$ = new Formal(data->symbols.create($1), 0); } : ID { $$ = new Formal(data->symbols.create($1), 0); }
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); } | ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
; ;
%% %%
@@ -467,34 +479,30 @@ formal
namespace nix { namespace nix {
Expr * EvalState::parse(const char * text, Expr * EvalState::parse(const char * text,
const Path & path, const Path & basePath) const Path & path, const Path & basePath, StaticEnv & staticEnv)
{ {
yyscan_t scanner; yyscan_t scanner;
ParseData data(*this); ParseData data(*this);
data.basePath = basePath; data.basePath = basePath;
data.path = path; data.path = data.symbols.create(path);
yylex_init(&scanner); yylex_init(&scanner);
yy_scan_string(text, scanner); yy_scan_string(text, scanner);
int res = yyparse(scanner, &data); int res = yyparse(scanner, &data);
yylex_destroy(scanner); yylex_destroy(scanner);
if (res) throw ParseError(data.error); if (res) throw ParseError(data.error);
try { data.result->bindVars(staticEnv);
data.result->bindVars(staticBaseEnv);
} catch (Error & e) {
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
}
return data.result; return data.result;
} }
Expr * EvalState::parseExprFromFile(Path path) Path resolveExprPath(Path path)
{ {
assert(path[0] == '/'); assert(path[0] == '/');
@@ -512,21 +520,25 @@ Expr * EvalState::parseExprFromFile(Path path)
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
path = canonPath(path + "/default.nix"); path = canonPath(path + "/default.nix");
/* Read and parse the input file, unless it's already in the parse return path;
tree cache. */ }
Expr * e = parseTrees[path];
if (!e) {
e = parse(readFile(path).c_str(), path, dirOf(path));
parseTrees[path] = e;
}
return e;
Expr * EvalState::parseExprFromFile(const Path & path)
{
return parse(readFile(path).c_str(), path, dirOf(path), staticBaseEnv);
}
Expr * EvalState::parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv)
{
return parse(s.c_str(), "(string)", basePath, staticEnv);
} }
Expr * EvalState::parseExprFromString(const string & s, const Path & basePath) Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
{ {
return parse(s.c_str(), "(string)", basePath); return parseExprFromString(s, basePath, staticBaseEnv);
} }
@@ -541,7 +553,7 @@ void EvalState::addToSearchPath(const string & s)
prefix = string(s, 0, pos); prefix = string(s, 0, pos);
path = string(s, pos + 1); path = string(s, pos + 1);
} }
path = absPath(path); path = absPath(path);
if (pathExists(path)) { if (pathExists(path)) {
debug(format("adding path `%1%' to the search path") % path); debug(format("adding path `%1%' to the search path") % path);
@@ -554,7 +566,7 @@ Path EvalState::findFile(const string & path)
{ {
foreach (SearchPath::iterator, i, searchPath) { foreach (SearchPath::iterator, i, searchPath) {
Path res; Path res;
if (i->first.empty()) if (i->first.empty())
res = i->second + "/" + path; res = i->second + "/" + path;
else { else {
if (path.compare(0, i->first.size(), i->first) != 0 || if (path.compare(0, i->first.size(), i->first) != 0 ||
+116 -64
View File
@@ -37,7 +37,7 @@ std::pair<string, string> decodeContext(const string & s)
/* Load and evaluate an expression from path specified by the /* Load and evaluate an expression from path specified by the
argument. */ argument. */
static void prim_import(EvalState & state, Value * * args, Value & v) static void prim_import(EvalState & state, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
@@ -56,7 +56,7 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
unsigned long long downloadSize, narSize; unsigned long long downloadSize, narSize;
queryMissing(*store, singleton<PathSet>(ctx), queryMissing(*store, singleton<PathSet>(ctx),
willBuild, willSubstitute, unknown, downloadSize, narSize); willBuild, willSubstitute, unknown, downloadSize, narSize);
/* !!! If using a substitute, we only need to fetch /* !!! If using a substitute, we only need to fetch
the selected output of this derivation. */ the selected output of this derivation. */
store->buildPaths(singleton<PathSet>(ctx)); store->buildPaths(singleton<PathSet>(ctx));
@@ -82,8 +82,7 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
} }
w.attrs->sort(); w.attrs->sort();
Value fun; Value fun;
state.mkThunk_(fun, state.evalFile(state.findFile("nix/imported-drv-to-derivation.nix"), fun);
state.parseExprFromFile(state.findFile("nix/imported-drv-to-derivation.nix")));
state.forceFunction(fun); state.forceFunction(fun);
mkApp(v, fun, w); mkApp(v, fun, w);
state.forceAttrs(v); state.forceAttrs(v);
@@ -93,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. */ /* Determine whether the argument is the null value. */
static void prim_isNull(EvalState & state, Value * * args, Value & v) static void prim_isNull(EvalState & state, Value * * args, Value & v)
{ {
@@ -109,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) static void prim_isInt(EvalState & state, Value * * args, Value & v)
{ {
state.forceValue(*args[0]); state.forceValue(*args[0]);
@@ -117,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) static void prim_isString(EvalState & state, Value * * args, Value & v)
{ {
state.forceValue(*args[0]); state.forceValue(*args[0]);
@@ -125,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) static void prim_isBool(EvalState & state, Value * * args, Value & v)
{ {
state.forceValue(*args[0]); state.forceValue(*args[0]);
@@ -135,24 +158,31 @@ static void prim_isBool(EvalState & state, Value * * args, Value & v)
struct CompareValues 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"); throw EvalError("cannot compare values of different types");
switch (v1.type) { switch (v1->type) {
case tInt: case tInt:
return v1.integer < v2.integer; return v1->integer < v2->integer;
case tString: case tString:
return strcmp(v1.string.s, v2.string.s) < 0; return strcmp(v1->string.s, v2->string.s) < 0;
case tPath: case tPath:
return strcmp(v1.path, v2.path) < 0; return strcmp(v1->path, v2->path) < 0;
default: 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) static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
{ {
startNest(nest, lvlDebug, "finding dependencies"); startNest(nest, lvlDebug, "finding dependencies");
@@ -166,7 +196,7 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
throw EvalError("attribute `startSet' required"); throw EvalError("attribute `startSet' required");
state.forceList(*startSet->value); state.forceList(*startSet->value);
list<Value *> workSet; ValueList workSet;
for (unsigned int n = 0; n < startSet->value->list.length; ++n) for (unsigned int n = 0; n < startSet->value->list.length; ++n)
workSet.push_back(startSet->value->list.elems[n]); workSet.push_back(startSet->value->list.elems[n]);
@@ -180,11 +210,13 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
/* Construct the closure by applying the operator to element of /* Construct the closure by applying the operator to element of
`workSet', adding the result to `workSet', continuing until `workSet', adding the result to `workSet', continuing until
no new elements are found. */ no new elements are found. */
list<Value> res; ValueList res;
set<Value, CompareValues> doneKeys; // !!! use Value *? // `doneKeys' doesn't need to be a GC root, because its values are
// reachable from res.
set<Value *, CompareValues> doneKeys;
while (!workSet.empty()) { while (!workSet.empty()) {
Value * e = *(workSet.begin()); Value * e = *(workSet.begin());
workSet.pop_front(); workSet.pop_front();
state.forceAttrs(*e); state.forceAttrs(*e);
@@ -194,10 +226,10 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
throw EvalError("attribute `key' required"); throw EvalError("attribute `key' required");
state.forceValue(*key->value); state.forceValue(*key->value);
if (doneKeys.find(*key->value) != doneKeys.end()) continue; if (doneKeys.find(key->value) != doneKeys.end()) continue;
doneKeys.insert(*key->value); doneKeys.insert(key->value);
res.push_back(*e); res.push_back(e);
/* Call the `operator' function with `e' as argument. */ /* Call the `operator' function with `e' as argument. */
Value call; Value call;
mkApp(call, *op->value, *e); mkApp(call, *op->value, *e);
@@ -213,8 +245,8 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
/* Create the result list. */ /* Create the result list. */
state.mkList(v, res.size()); state.mkList(v, res.size());
unsigned int n = 0; unsigned int n = 0;
foreach (list<Value>::iterator, i, res) foreach (ValueList::iterator, i, res)
*(v.list.elems[n++] = state.allocValue()) = *i; v.list.elems[n++] = *i;
} }
@@ -247,17 +279,17 @@ static void prim_addErrorContext(EvalState & state, Value * * args, Value & v)
} }
/* Try evaluating the argument. Success => {success=true; value=something;}, /* Try evaluating the argument. Success => {success=true; value=something;},
* else => {success=false; value=false;} */ * else => {success=false; value=false;} */
static void prim_tryEval(EvalState & state, Value * * args, Value & v) static void prim_tryEval(EvalState & state, Value * * args, Value & v)
{ {
state.mkAttrs(v, 2); state.mkAttrs(v, 2);
try { try {
state.forceValue(*args[0]); 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); mkBool(*state.allocAttr(v, state.symbols.create("success")), true);
} catch (AssertionError & e) { } 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); mkBool(*state.allocAttr(v, state.symbols.create("success")), false);
} }
v.attrs->sort(); v.attrs->sort();
@@ -395,10 +427,8 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
} }
} catch (Error & e) { } catch (Error & e) {
e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n") e.addPrefix(format("while evaluating the attribute `%1%' of the derivation `%2%' at %3%:\n")
% key % *i->pos); % key % drvName % posDrvName);
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
% drvName % posDrvName);
throw; throw;
} }
} }
@@ -634,7 +664,7 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name); throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name);
refs.insert(path); refs.insert(path);
} }
Path storePath = settings.readOnlyMode Path storePath = settings.readOnlyMode
? computeStorePathForText(name, contents, refs) ? computeStorePathForText(name, contents, refs)
: store->addTextToStore(name, contents, refs, state.repair); : store->addTextToStore(name, contents, refs, state.repair);
@@ -651,7 +681,7 @@ struct FilterFromExpr : PathFilter
{ {
EvalState & state; EvalState & state;
Value & filter; Value & filter;
FilterFromExpr(EvalState & state, Value & filter) FilterFromExpr(EvalState & state, Value & filter)
: state(state), filter(filter) : state(state), filter(filter)
{ {
@@ -672,12 +702,12 @@ struct FilterFromExpr : PathFilter
state.callFunction(filter, arg1, fun2); state.callFunction(filter, arg1, fun2);
Value arg2; Value arg2;
mkString(arg2, mkString(arg2,
S_ISREG(st.st_mode) ? "regular" : S_ISREG(st.st_mode) ? "regular" :
S_ISDIR(st.st_mode) ? "directory" : S_ISDIR(st.st_mode) ? "directory" :
S_ISLNK(st.st_mode) ? "symlink" : S_ISLNK(st.st_mode) ? "symlink" :
"unknown" /* not supported, will fail! */); "unknown" /* not supported, will fail! */);
Value res; Value res;
state.callFunction(fun2, arg2, res); state.callFunction(fun2, arg2, res);
@@ -708,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 /* Return the names of the attributes in a set as a sorted list of
list of strings. */ strings. */
static void prim_attrNames(EvalState & state, Value * * args, Value & v) static void prim_attrNames(EvalState & state, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0]); state.forceAttrs(*args[0]);
@@ -755,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) static void prim_isAttrs(EvalState & state, Value * * args, Value & v)
{ {
state.forceValue(*args[0]); state.forceValue(*args[0]);
@@ -786,10 +816,11 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
} }
/* Builds an attribute set from a list specifying (name, value) /* Builds a set from a list specifying (name, value) pairs. To be
pairs. To be precise, a list [{name = "name1"; value = value1;} precise, a list [{name = "name1"; value = value1;} ... {name =
... {name = "nameN"; value = valueN;}] is transformed to {name1 = "nameN"; value = valueN;}] is transformed to {name1 = value1;
value1; ... nameN = valueN;}. */ ... 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) static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
{ {
state.forceList(*args[0]); state.forceList(*args[0]);
@@ -801,36 +832,35 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
for (unsigned int i = 0; i < args[0]->list.length; ++i) { for (unsigned int i = 0; i < args[0]->list.length; ++i) {
Value & v2(*args[0]->list.elems[i]); Value & v2(*args[0]->list.elems[i]);
state.forceAttrs(v2); state.forceAttrs(v2);
Bindings::iterator j = v2.attrs->find(state.sName); Bindings::iterator j = v2.attrs->find(state.sName);
if (j == v2.attrs->end()) if (j == v2.attrs->end())
throw TypeError("`name' attribute missing in a call to `listToAttrs'"); throw TypeError("`name' attribute missing in a call to `listToAttrs'");
string name = state.forceStringNoCtx(*j->value); 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); Symbol sym = state.symbols.create(name);
if (seen.find(sym) == seen.end()) { 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)); v.attrs->push_back(Attr(sym, j2->value, j2->pos));
seen.insert(sym); seen.insert(sym);
} }
/* !!! Throw an error if `name' already exists? */
} }
v.attrs->sort(); v.attrs->sort();
} }
/* Return the right-biased intersection of two attribute sets as1 and /* Return the right-biased intersection of two sets as1 and as2,
as2, i.e. a set that contains every attribute from as2 that is also i.e. a set that contains every attribute from as2 that is also a
a member of as1. */ member of as1. */
static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v) static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0]); state.forceAttrs(*args[0]);
state.forceAttrs(*args[1]); state.forceAttrs(*args[1]);
state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size())); state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
foreach (Bindings::iterator, i, *args[0]->attrs) { foreach (Bindings::iterator, i, *args[0]->attrs) {
@@ -933,7 +963,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
state.mkList(v, args[1]->list.length); state.mkList(v, args[1]->list.length);
for (unsigned int n = 0; n < v.list.length; ++n) for (unsigned int n = 0; n < v.list.length; ++n)
mkApp(*(v.list.elems[n] = state.allocValue()), mkApp(*(v.list.elems[n] = state.allocValue()),
*args[0], *args[1]->list.elems[n]); *args[0], *args[1]->list.elems[n]);
} }
@@ -1024,7 +1054,7 @@ static void prim_mul(EvalState & state, Value * * args, Value & v)
static void prim_div(EvalState & state, Value * * args, Value & v) static void prim_div(EvalState & state, Value * * args, Value & v)
{ {
int i2 = state.forceInt(*args[1]); NixInt i2 = state.forceInt(*args[1]);
if (i2 == 0) throw EvalError("division by zero"); if (i2 == 0) throw EvalError("division by zero");
mkInt(v, state.forceInt(*args[0]) / i2); mkInt(v, state.forceInt(*args[0]) / i2);
} }
@@ -1032,7 +1062,10 @@ static void prim_div(EvalState & state, Value * * args, Value & v)
static void prim_lessThan(EvalState & state, Value * * args, Value & v) static void prim_lessThan(EvalState & state, Value * * args, Value & v)
{ {
mkBool(v, state.forceInt(*args[0]) < state.forceInt(*args[1])); state.forceValue(*args[0]);
state.forceValue(*args[1]);
CompareValues comp;
mkBool(v, comp(args[0], args[1]));
} }
@@ -1102,11 +1135,26 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args
if (p.at(0) == '=') p = "~" + string(p, 1); if (p.at(0) == '=') p = "~" + string(p, 1);
context2.insert(p); context2.insert(p);
} }
mkString(v, s, context2); mkString(v, s, context2);
} }
/* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, Value * * args, Value & v)
{
string type = state.forceStringNoCtx(*args[0]);
HashType ht = parseHashType(type);
if (ht == htUnknown)
throw Error(format("unknown hash type `%1%'") % type);
PathSet context; // discarded
string s = state.forceString(*args[1], context);
mkString(v, printHash(hashString(ht, s)), context);
};
/************************************************************* /*************************************************************
* Versions * Versions
*************************************************************/ *************************************************************/
@@ -1149,10 +1197,10 @@ void EvalState::createBaseEnv()
mkBool(v, true); mkBool(v, true);
addConstant("true", v); addConstant("true", v);
mkBool(v, false); mkBool(v, false);
addConstant("false", v); addConstant("false", v);
v.type = tNull; v.type = tNull;
addConstant("null", v); addConstant("null", v);
@@ -1174,6 +1222,7 @@ void EvalState::createBaseEnv()
// Miscellaneous // Miscellaneous
addPrimOp("import", 1, prim_import); addPrimOp("import", 1, prim_import);
addPrimOp("__typeOf", 1, prim_typeOf);
addPrimOp("isNull", 1, prim_isNull); addPrimOp("isNull", 1, prim_isNull);
addPrimOp("__isFunction", 1, prim_isFunction); addPrimOp("__isFunction", 1, prim_isFunction);
addPrimOp("__isString", 1, prim_isString); addPrimOp("__isString", 1, prim_isString);
@@ -1200,7 +1249,7 @@ void EvalState::createBaseEnv()
addPrimOp("__toFile", 2, prim_toFile); addPrimOp("__toFile", 2, prim_toFile);
addPrimOp("__filterSource", 2, prim_filterSource); addPrimOp("__filterSource", 2, prim_filterSource);
// Attribute sets // Sets
addPrimOp("__attrNames", 1, prim_attrNames); addPrimOp("__attrNames", 1, prim_attrNames);
addPrimOp("__getAttr", 2, prim_getAttr); addPrimOp("__getAttr", 2, prim_getAttr);
addPrimOp("__hasAttr", 2, prim_hasAttr); addPrimOp("__hasAttr", 2, prim_hasAttr);
@@ -1234,6 +1283,7 @@ void EvalState::createBaseEnv()
addPrimOp("__stringLength", 1, prim_stringLength); addPrimOp("__stringLength", 1, prim_stringLength);
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext); addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency); addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
addPrimOp("__hashString", 2, prim_hashString);
// Versions // Versions
addPrimOp("__parseDrvName", 1, prim_parseDrvName); addPrimOp("__parseDrvName", 1, prim_parseDrvName);
@@ -1244,11 +1294,13 @@ void EvalState::createBaseEnv()
/* Add a wrapper around the derivation primop that computes the /* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */ `drvPath' and `outPath' attributes lazily. */
mkThunk_(v, parseExprFromFile(findFile("nix/derivation.nix"))); string path = findFile("nix/derivation.nix");
sDerivationNix = symbols.create(path);
evalFile(path, v);
addConstant("derivation", v); addConstant("derivation", v);
/* Now that we've added all primops, sort the `builtins' attribute /* Now that we've added all primops, sort the `builtins' set,
set, because attribute lookups expect it to be sorted. */ because attribute lookups expect it to be sorted. */
baseEnv.values[0]->attrs->sort(); baseEnv.values[0]->attrs->sort();
} }
+15 -9
View File
@@ -13,11 +13,10 @@
namespace nix { namespace nix {
/* Symbol table used by the parser and evaluator to represent and look /* Symbol table used by the parser and evaluator to represent and look
up identifiers and attribute sets efficiently. up identifiers and attributes efficiently. SymbolTable::create()
SymbolTable::create() converts a string into a symbol. Symbols converts a string into a symbol. Symbols have the property that
have the property that they can be compared efficiently (using a they can be compared efficiently (using a pointer equality test),
pointer equality test), because the symbol table stores only one because the symbol table stores only one copy of each string. */
copy of each string. */
class Symbol class Symbol
{ {
@@ -28,17 +27,17 @@ private:
public: public:
Symbol() : s(0) { }; Symbol() : s(0) { };
bool operator == (const Symbol & s2) const bool operator == (const Symbol & s2) const
{ {
return s == s2.s; return s == s2.s;
} }
bool operator != (const Symbol & s2) const bool operator != (const Symbol & s2) const
{ {
return s != s2.s; return s != s2.s;
} }
bool operator < (const Symbol & s2) const bool operator < (const Symbol & s2) const
{ {
return s < s2.s; return s < s2.s;
@@ -49,6 +48,11 @@ public:
return *s; return *s;
} }
bool set() const
{
return s;
}
bool empty() const bool empty() const
{ {
return s->empty(); return s->empty();
@@ -66,7 +70,7 @@ inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
class SymbolTable class SymbolTable
{ {
private: private:
#if HAVE_TR1_UNORDERED_SET #if HAVE_TR1_UNORDERED_SET
typedef std::tr1::unordered_set<string> Symbols; typedef std::tr1::unordered_set<string> Symbols;
#else #else
typedef std::set<string> Symbols; typedef std::set<string> Symbols;
@@ -84,6 +88,8 @@ public:
{ {
return symbols.size(); return symbols.size();
} }
size_t totalSize() const;
}; };
} }
+12 -9
View File
@@ -31,17 +31,20 @@ struct PrimOp;
struct Symbol; struct Symbol;
typedef long NixInt;
struct Value struct Value
{ {
ValueType type; ValueType type;
union union
{ {
int integer; NixInt integer;
bool boolean; bool boolean;
/* Strings in the evaluator carry a so-called `context' (the /* Strings in the evaluator carry a so-called `context' which
ATermList) which is a list of strings representing store is a list of strings representing store paths. This is to
paths. This is to allow users to write things like allow users to write things like
"--with-freetype2-library=" + freetype + "/lib" "--with-freetype2-library=" + freetype + "/lib"
@@ -63,7 +66,7 @@ struct Value
const char * s; const char * s;
const char * * context; // must be in sorted order const char * * context; // must be in sorted order
} string; } string;
const char * path; const char * path;
Bindings * attrs; Bindings * attrs;
struct { struct {
@@ -97,7 +100,7 @@ static inline void clearValue(Value & v)
} }
static inline void mkInt(Value & v, int n) static inline void mkInt(Value & v, NixInt n)
{ {
clearValue(v); clearValue(v);
v.type = tInt; v.type = tInt;
@@ -131,7 +134,7 @@ static inline void mkStringNoCopy(Value & v, const char * s)
static inline void mkString(Value & v, const Symbol & s) static inline void mkString(Value & v, const Symbol & s)
{ {
mkStringNoCopy(v, ((string) s).c_str()); mkStringNoCopy(v, ((const string &) s).c_str());
} }
+1 -1
View File
@@ -1,6 +1,6 @@
pkglib_LTLIBRARIES = libmain.la pkglib_LTLIBRARIES = libmain.la
libmain_la_SOURCES = shared.cc libmain_la_SOURCES = shared.cc stack.cc
libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@ libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@
+18 -5
View File
@@ -10,8 +10,10 @@
#include <cctype> #include <cctype>
#include <exception> #include <exception>
#include <sys/time.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h>
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
#include <gc/gc.h> #include <gc/gc.h>
@@ -99,6 +101,9 @@ string getArg(const string & opt,
} }
void detectStackOverflow();
/* Initialize and reorder arguments, then call the actual argument /* Initialize and reorder arguments, then call the actual argument
processor. */ processor. */
static void initAndRun(int argc, char * * argv) static void initAndRun(int argc, char * * argv)
@@ -130,11 +135,19 @@ static void initAndRun(int argc, char * * argv)
if (sigaction(SIGCHLD, &act, 0)) if (sigaction(SIGCHLD, &act, 0))
throw SysError("resetting SIGCHLD"); throw SysError("resetting SIGCHLD");
/* Register a SIGSEGV handler to detect stack overflows. */
detectStackOverflow();
/* There is no privacy in the Nix system ;-) At least not for /* There is no privacy in the Nix system ;-) At least not for
now. In particular, store objects should be readable by now. In particular, store objects should be readable by
everybody. */ everybody. */
umask(0022); umask(0022);
/* Initialise the PRNG. */
struct timeval tv;
gettimeofday(&tv, 0);
srandom(tv.tv_usec);
/* Process the NIX_LOG_TYPE environment variable. */ /* Process the NIX_LOG_TYPE environment variable. */
string lt = getEnv("NIX_LOG_TYPE"); string lt = getEnv("NIX_LOG_TYPE");
if (lt != "") setLogType(lt); if (lt != "") setLogType(lt);
@@ -162,11 +175,10 @@ static void initAndRun(int argc, char * * argv)
remaining.clear(); remaining.clear();
/* Process default options. */ /* Process default options. */
int verbosityDelta = lvlInfo;
for (Strings::iterator i = args.begin(); i != args.end(); ++i) { for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
string arg = *i; string arg = *i;
if (arg == "--verbose" || arg == "-v") verbosityDelta++; if (arg == "--verbose" || arg == "-v") verbosity = (Verbosity) (verbosity + 1);
else if (arg == "--quiet") verbosityDelta--; else if (arg == "--quiet") verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
else if (arg == "--log-type") { else if (arg == "--log-type") {
string s = getArg(arg, i, args.end()); string s = getArg(arg, i, args.end());
setLogType(s); setLogType(s);
@@ -213,8 +225,6 @@ static void initAndRun(int argc, char * * argv)
else remaining.push_back(arg); else remaining.push_back(arg);
} }
verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta);
settings.update(); settings.update();
run(remaining); run(remaining);
@@ -293,6 +303,9 @@ int main(int argc, char * * argv)
if (e.prefix() != "" && !showTrace) if (e.prefix() != "" && !showTrace)
printMsg(lvlError, "(use `--show-trace' to show detailed location information)"); printMsg(lvlError, "(use `--show-trace' to show detailed location information)");
return e.status; return e.status;
} catch (std::bad_alloc & e) {
printMsg(lvlError, "error: out of memory");
return 1;
} catch (std::exception & e) { } catch (std::exception & e) {
printMsg(lvlError, format("error: %1%") % e.what()); printMsg(lvlError, format("error: %1%") % e.what());
return 1; return 1;
+72
View File
@@ -0,0 +1,72 @@
#include "config.h"
#include "types.hh"
#include <cstring>
#include <cstddef>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
namespace nix {
static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
{
/* Detect stack overflows by comparing the faulting address with
the stack pointer. Unfortunately, getting the stack pointer is
not portable. */
bool haveSP = true;
char * sp;
#if defined(__x86_64__) && defined(REG_RSP)
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_RSP];
#elif defined(REG_ESP)
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_ESP];
#else
haveSP = false;
#endif
if (haveSP) {
ptrdiff_t diff = (char *) info->si_addr - sp;
if (diff < 0) diff = -diff;
if (diff < 4096) {
char msg[] = "error: stack overflow (possible infinite recursion)\n";
write(2, msg, strlen(msg));
_exit(1); // maybe abort instead?
}
}
/* Restore default behaviour (i.e. segfault and dump core). */
struct sigaction act;
sigfillset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(SIGSEGV, &act, 0)) abort();
}
void detectStackOverflow()
{
#if defined(SA_SIGINFO) && defined (SA_ONSTACK)
/* Install a SIGSEGV handler to detect stack overflows. This
requires an alternative stack, otherwise the signal cannot be
delivered when we're out of stack space. */
stack_t stack;
stack.ss_size = 4096 * 4 + MINSIGSTKSZ;
stack.ss_sp = new char[stack.ss_size];
if (!stack.ss_sp) throw Error("cannot allocate alternative stack");
stack.ss_flags = 0;
if (sigaltstack(&stack, 0) == -1) throw SysError("cannot set alternative stack");
struct sigaction act;
sigfillset(&act.sa_mask);
act.sa_sigaction = sigsegvHandler;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (sigaction(SIGSEGV, &act, 0))
throw SysError("resetting SIGCHLD");
#endif
}
}
+205 -126
View File
@@ -7,7 +7,7 @@
#include "local-store.hh" #include "local-store.hh"
#include "util.hh" #include "util.hh"
#include "archive.hh" #include "archive.hh"
#include "immutable.hh" #include "affinity.hh"
#include <map> #include <map>
#include <sstream> #include <sstream>
@@ -21,6 +21,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/utsname.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@@ -44,6 +45,12 @@
#include <sched.h> #include <sched.h>
#endif #endif
/* In GNU libc 2.11, <sys/mount.h> does not define `MS_PRIVATE', but
<linux/fs.h> does. */
#if !defined MS_PRIVATE && defined HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) #define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS)
#if CHROOT_ENABLED #if CHROOT_ENABLED
@@ -93,7 +100,7 @@ typedef map<Path, WeakGoalPtr> WeakGoalMap;
class Goal : public boost::enable_shared_from_this<Goal> class Goal : public boost::enable_shared_from_this<Goal>
{ {
public: public:
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters} ExitCode; typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
protected: protected:
@@ -114,6 +121,10 @@ protected:
failed because there are no substituters. */ failed because there are no substituters. */
unsigned int nrNoSubstituters; unsigned int nrNoSubstituters;
/* Number of substitution goals we are/were waiting for that
failed because othey had unsubstitutable references. */
unsigned int nrIncompleteClosure;
/* Name of this goal for debugging purposes. */ /* Name of this goal for debugging purposes. */
string name; string name;
@@ -122,7 +133,7 @@ protected:
Goal(Worker & worker) : worker(worker) Goal(Worker & worker) : worker(worker)
{ {
nrFailed = nrNoSubstituters = 0; nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
exitCode = ecBusy; exitCode = ecBusy;
} }
@@ -163,7 +174,7 @@ public:
/* Cancel the goal. It should wake up its waiters, get rid of any /* Cancel the goal. It should wake up its waiters, get rid of any
running child processes that are being monitored by the worker running child processes that are being monitored by the worker
(important!), etc. */ (important!), etc. */
virtual void cancel() = 0; virtual void cancel(bool timeout) = 0;
protected: protected:
void amDone(ExitCode result); void amDone(ExitCode result);
@@ -177,9 +188,10 @@ struct Child
{ {
WeakGoalPtr goal; WeakGoalPtr goal;
set<int> fds; set<int> fds;
bool monitorForSilence; bool respectTimeouts;
bool inBuildSlot; bool inBuildSlot;
time_t lastOutput; /* time we last got output on stdout/stderr */ time_t lastOutput; /* time we last got output on stdout/stderr */
time_t timeStarted;
}; };
typedef map<pid_t, Child> Children; typedef map<pid_t, Child> Children;
@@ -223,9 +235,6 @@ private:
/* Last time the goals in `waitingForAWhile' where woken up. */ /* Last time the goals in `waitingForAWhile' where woken up. */
time_t lastWokenUp; time_t lastWokenUp;
/* Last time `waitForInput' was last called. */
time_t lastWait;
public: public:
/* Set if at least one derivation had a BuildError (i.e. permanent /* Set if at least one derivation had a BuildError (i.e. permanent
@@ -257,7 +266,7 @@ public:
/* Registers a running child process. `inBuildSlot' means that /* Registers a running child process. `inBuildSlot' means that
the process counts towards the jobs limit. */ the process counts towards the jobs limit. */
void childStarted(GoalPtr goal, pid_t pid, void childStarted(GoalPtr goal, pid_t pid,
const set<int> & fds, bool inBuildSlot, bool monitorForSilence); const set<int> & fds, bool inBuildSlot, bool respectTimeouts);
/* Unregisters a running child process. `wakeSleepers' should be /* Unregisters a running child process. `wakeSleepers' should be
false if there is no sense in waking up goals that are sleeping false if there is no sense in waking up goals that are sleeping
@@ -307,10 +316,12 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
trace(format("waitee `%1%' done; %2% left") % trace(format("waitee `%1%' done; %2% left") %
waitee->name % waitees.size()); waitee->name % waitees.size());
if (result == ecFailed || result == ecNoSubstituters) ++nrFailed; if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
if (result == ecNoSubstituters) ++nrNoSubstituters; if (result == ecNoSubstituters) ++nrNoSubstituters;
if (result == ecIncompleteClosure) ++nrIncompleteClosure;
if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) { if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
/* If we failed and keepGoing is not set, we remove all /* If we failed and keepGoing is not set, we remove all
@@ -333,7 +344,7 @@ void Goal::amDone(ExitCode result)
{ {
trace("done"); trace("done");
assert(exitCode == ecBusy); assert(exitCode == ecBusy);
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters); assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
exitCode = result; exitCode = result;
foreach (WeakGoals::iterator, i, waiters) { foreach (WeakGoals::iterator, i, waiters) {
GoalPtr goal = i->lock(); GoalPtr goal = i->lock();
@@ -357,6 +368,8 @@ void Goal::trace(const format & f)
/* Common initialisation performed in child processes. */ /* Common initialisation performed in child processes. */
static void commonChildInit(Pipe & logPipe) static void commonChildInit(Pipe & logPipe)
{ {
restoreAffinity();
/* Put the child in a separate session (and thus a separate /* Put the child in a separate session (and thus a separate
process group) so that it has no controlling terminal (meaning process group) so that it has no controlling terminal (meaning
that e.g. ssh cannot open /dev/tty) and it doesn't receive that e.g. ssh cannot open /dev/tty) and it doesn't receive
@@ -559,6 +572,7 @@ static void runSetuidHelper(const string & command,
args.push_back(0); args.push_back(0);
restoreSIGPIPE(); restoreSIGPIPE();
restoreAffinity();
execve(program.c_str(), (char * *) &args[0], 0); execve(program.c_str(), (char * *) &args[0], 0);
throw SysError(format("executing `%1%'") % program); throw SysError(format("executing `%1%'") % program);
@@ -710,13 +724,7 @@ HookInstance::HookInstance()
HookInstance::~HookInstance() HookInstance::~HookInstance()
{ {
try { try {
/* Cleanly shut down the hook by closing its stdin if it's not pid.kill();
already building. Otherwise pid's destructor will kill
it. */
if (pid != -1 && toHook.writeSide != -1) {
toHook.writeSide.close();
pid.wait(true);
}
} catch (...) { } catch (...) {
ignoreException(); ignoreException();
} }
@@ -763,6 +771,10 @@ private:
/* Whether additional wanted outputs have been added. */ /* Whether additional wanted outputs have been added. */
bool needRestart; bool needRestart;
/* Whether to retry substituting the outputs after building the
inputs. */
bool retrySubstitution;
/* The derivation stored at drvPath. */ /* The derivation stored at drvPath. */
Derivation drv; Derivation drv;
@@ -781,6 +793,12 @@ private:
/* Outputs that are already valid. */ /* Outputs that are already valid. */
PathSet validPaths; PathSet validPaths;
/* Outputs that are corrupt or not valid. */
PathSet missingPaths;
/* Paths that have been subject to hash rewriting. */
PathSet rewrittenPaths;
/* User selected for running the builder. */ /* User selected for running the builder. */
UserLock buildUser; UserLock buildUser;
@@ -795,6 +813,9 @@ private:
BZFILE * bzLogFile; BZFILE * bzLogFile;
AutoCloseFD fdLogFile; AutoCloseFD fdLogFile;
/* Number of bytes received from the builder's stdout/stderr. */
unsigned long logSize;
/* Pipe for the builder's standard output/error. */ /* Pipe for the builder's standard output/error. */
Pipe builderOut; Pipe builderOut;
@@ -819,7 +840,8 @@ private:
GoalState state; GoalState state;
/* Stuff we need to pass to initChild(). */ /* Stuff we need to pass to initChild(). */
PathSet dirsInChroot; typedef map<Path, Path> DirsInChroot; // maps target path to source path
DirsInChroot dirsInChroot;
typedef map<string, string> Environment; typedef map<string, string> Environment;
Environment env; Environment env;
@@ -832,6 +854,11 @@ private:
bool repair; bool repair;
map<Path, Path> redirectedBadOutputs; map<Path, Path> redirectedBadOutputs;
/* Set of inodes seen during calls to canonicalisePathMetaData()
for this build's outputs. This needs to be shared between
outputs to allow hard links between outputs. */
InodesSeen inodesSeen;
/* Magic exit code denoting that setting up the child environment /* Magic exit code denoting that setting up the child environment
failed. (It's possible that the child actually returns the failed. (It's possible that the child actually returns the
exit code, but ah well.) */ exit code, but ah well.) */
@@ -841,7 +868,7 @@ public:
DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, bool repair = false); DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, bool repair = false);
~DerivationGoal(); ~DerivationGoal();
void cancel(); void cancel(bool timeout);
void work(); void work();
@@ -911,6 +938,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const StringSet & wantedOut
: Goal(worker) : Goal(worker)
, wantedOutputs(wantedOutputs) , wantedOutputs(wantedOutputs)
, needRestart(false) , needRestart(false)
, retrySubstitution(false)
, fLogFile(0) , fLogFile(0)
, bzLogFile(0) , bzLogFile(0)
, useChroot(false) , useChroot(false)
@@ -966,8 +994,10 @@ void DerivationGoal::killChild()
} }
void DerivationGoal::cancel() void DerivationGoal::cancel(bool timeout)
{ {
if (settings.printBuildTrace && timeout)
printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
killChild(); killChild();
amDone(ecFailed); amDone(ecFailed);
} }
@@ -1052,7 +1082,7 @@ void DerivationGoal::haveDerivation()
/* We are first going to try to create the invalid output paths /* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build through substitutes. If that doesn't work, we'll build
them. */ them. */
if (settings.useSubstitutes) if (settings.useSubstitutes && !willBuildLocally(drv))
foreach (PathSet::iterator, i, invalidOutputs) foreach (PathSet::iterator, i, invalidOutputs)
addWaitee(worker.makeSubstitutionGoal(*i, repair)); addWaitee(worker.makeSubstitutionGoal(*i, repair));
@@ -1067,10 +1097,15 @@ void DerivationGoal::outputsSubstituted()
{ {
trace("all outputs substituted (maybe)"); trace("all outputs substituted (maybe)");
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback) if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback)
throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath); throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
nrFailed = nrNoSubstituters = 0; /* If the substitutes form an incomplete closure, then we should
build the dependencies of this derivation, but after that, we
can still use the substitutes for this derivation itself. */
if (nrIncompleteClosure > 0 && !retrySubstitution) retrySubstitution = true;
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
if (needRestart) { if (needRestart) {
needRestart = false; needRestart = false;
@@ -1176,6 +1211,11 @@ void DerivationGoal::inputsRealised()
return; return;
} }
if (retrySubstitution) {
haveDerivation();
return;
}
/* Gather information necessary for computing the closure and/or /* Gather information necessary for computing the closure and/or
running the build hook. */ running the build hook. */
@@ -1233,6 +1273,13 @@ PathSet outputPaths(const DerivationOutputs & outputs)
} }
static string get(const StringPairs & map, const string & key)
{
StringPairs::const_iterator i = map.find(key);
return i == map.end() ? (string) "" : i->second;
}
static bool canBuildLocally(const string & platform) static bool canBuildLocally(const string & platform)
{ {
return platform == settings.thisSystem return platform == settings.thisSystem
@@ -1243,6 +1290,12 @@ static bool canBuildLocally(const string & platform)
} }
bool willBuildLocally(const Derivation & drv)
{
return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv.platform);
}
void DerivationGoal::tryToBuild() void DerivationGoal::tryToBuild()
{ {
trace("trying to build"); trace("trying to build");
@@ -1286,6 +1339,9 @@ void DerivationGoal::tryToBuild()
return; return;
} }
missingPaths = outputPaths(drv.outputs);
foreach (PathSet::iterator, i, validPaths) missingPaths.erase(*i);
/* If any of the outputs already exist but are not valid, delete /* If any of the outputs already exist but are not valid, delete
them. */ them. */
foreach (DerivationOutputs::iterator, i, drv.outputs) { foreach (DerivationOutputs::iterator, i, drv.outputs) {
@@ -1304,11 +1360,10 @@ void DerivationGoal::tryToBuild()
/* Don't do a remote build if the derivation has the attribute /* Don't do a remote build if the derivation has the attribute
`preferLocalBuild' set. */ `preferLocalBuild' set. */
bool preferLocalBuild = bool buildLocally = willBuildLocally(drv);
drv.env["preferLocalBuild"] == "1" && canBuildLocally(drv.platform);
/* Is the build hook willing to accept this job? */ /* Is the build hook willing to accept this job? */
if (!preferLocalBuild) { if (!buildLocally) {
switch (tryBuildHook()) { switch (tryBuildHook()) {
case rpAccept: case rpAccept:
/* Yes, it has started doing so. Wait until we get /* Yes, it has started doing so. Wait until we get
@@ -1331,7 +1386,7 @@ void DerivationGoal::tryToBuild()
derivation prefers to be done locally, do it even if derivation prefers to be done locally, do it even if
maxBuildJobs is 0. */ maxBuildJobs is 0. */
unsigned int curBuilds = worker.getNrLocalBuilds(); unsigned int curBuilds = worker.getNrLocalBuilds();
if (curBuilds >= settings.maxBuildJobs && !(preferLocalBuild && curBuilds == 0)) { if (curBuilds >= settings.maxBuildJobs && !(buildLocally && curBuilds == 0)) {
worker.waitForBuildSlot(shared_from_this()); worker.waitForBuildSlot(shared_from_this());
outputLocks.unlock(); outputLocks.unlock();
return; return;
@@ -1347,8 +1402,8 @@ void DerivationGoal::tryToBuild()
outputLocks.unlock(); outputLocks.unlock();
buildUser.release(); buildUser.release();
if (settings.printBuildTrace) if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%") printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
% drvPath % drv.outputs["out"].path % 0 % e.msg()); % drvPath % 0 % e.msg());
worker.permanentFailure = true; worker.permanentFailure = true;
amDone(ecFailed); amDone(ecFailed);
return; return;
@@ -1367,10 +1422,8 @@ void replaceValidPath(const Path & storePath, const Path tmpPath)
way first. We'd better not be interrupted here, because if way first. We'd better not be interrupted here, because if
we're repairing (say) Glibc, we end up with a broken system. */ we're repairing (say) Glibc, we end up with a broken system. */
Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str(); Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str();
if (pathExists(storePath)) { if (pathExists(storePath))
makeMutable(storePath);
rename(storePath.c_str(), oldPath.c_str()); rename(storePath.c_str(), oldPath.c_str());
}
if (rename(tmpPath.c_str(), storePath.c_str()) == -1) if (rename(tmpPath.c_str(), storePath.c_str()) == -1)
throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath); throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath);
if (pathExists(oldPath)) if (pathExists(oldPath))
@@ -1425,8 +1478,8 @@ void DerivationGoal::buildDone()
/* Some cleanup per path. We do this here and not in /* Some cleanup per path. We do this here and not in
computeClosure() for convenience when the build has computeClosure() for convenience when the build has
failed. */ failed. */
foreach (DerivationOutputs::iterator, i, drv.outputs) { foreach (PathSet::iterator, i, missingPaths) {
Path path = i->second.path; Path path = *i;
/* If the output was already valid, just skip (discard) it. */ /* If the output was already valid, just skip (discard) it. */
if (validPaths.find(path) != validPaths.end()) continue; if (validPaths.find(path) != validPaths.end()) continue;
@@ -1463,6 +1516,12 @@ void DerivationGoal::buildDone()
/* Apply hash rewriting if necessary. */ /* Apply hash rewriting if necessary. */
if (!rewritesFromTmp.empty()) { if (!rewritesFromTmp.empty()) {
printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path); printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
/* Canonicalise first. This ensures that the path
we're rewriting doesn't contain a hard link to
/etc/shadow or something like that. */
canonicalisePathMetaData(path, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen);
/* FIXME: this is in-memory. */ /* FIXME: this is in-memory. */
StringSink sink; StringSink sink;
dumpPath(path, sink); dumpPath(path, sink);
@@ -1470,6 +1529,8 @@ void DerivationGoal::buildDone()
sink.s = rewriteHashes(sink.s, rewritesFromTmp); sink.s = rewriteHashes(sink.s, rewritesFromTmp);
StringSource source(sink.s); StringSource source(sink.s);
restorePath(path, source); restorePath(path, source);
rewrittenPaths.insert(path);
} }
/* Gain ownership of the build result using the setuid /* Gain ownership of the build result using the setuid
@@ -1522,11 +1583,11 @@ void DerivationGoal::buildDone()
if (settings.printBuildTrace) { if (settings.printBuildTrace) {
if (hook && hookError) if (hook && hookError)
printMsg(lvlError, format("@ hook-failed %1% %2% %3% %4%") printMsg(lvlError, format("@ hook-failed %1% - %2% %3%")
% drvPath % drv.outputs["out"].path % status % e.msg()); % drvPath % status % e.msg());
else else
printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%") printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
% drvPath % drv.outputs["out"].path % 1 % e.msg()); % drvPath % 1 % e.msg());
} }
/* Register the outputs of this build as "failed" so we won't /* Register the outputs of this build as "failed" so we won't
@@ -1548,10 +1609,8 @@ void DerivationGoal::buildDone()
/* Release the build user, if applicable. */ /* Release the build user, if applicable. */
buildUser.release(); buildUser.release();
if (settings.printBuildTrace) { if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-succeeded %1% %2%") printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
% drvPath % drv.outputs["out"].path);
}
amDone(ecSuccess); amDone(ecSuccess);
} }
@@ -1567,7 +1626,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Tell the hook about system features (beyond the system type) /* Tell the hook about system features (beyond the system type)
required from the build machine. (The hook could parse the required from the build machine. (The hook could parse the
drv file itself, but this is easier.) */ drv file itself, but this is easier.) */
Strings features = tokenizeString<Strings>(drv.env["requiredSystemFeatures"]); Strings features = tokenizeString<Strings>(get(drv.env, "requiredSystemFeatures"));
foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */ foreach (Strings::iterator, i, features) checkStoreName(*i); /* !!! abuse */
/* Send the request to the hook. */ /* Send the request to the hook. */
@@ -1632,8 +1691,8 @@ HookReply DerivationGoal::tryBuildHook()
worker.childStarted(shared_from_this(), hook->pid, fds, false, false); worker.childStarted(shared_from_this(), hook->pid, fds, false, false);
if (settings.printBuildTrace) if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-started %1% %2% %3% %4%") printMsg(lvlError, format("@ build-started %1% - %2% %3%")
% drvPath % drv.outputs["out"].path % drv.platform % logFile); % drvPath % drv.platform % logFile);
return rpAccept; return rpAccept;
} }
@@ -1655,9 +1714,7 @@ int childEntry(void * arg)
void DerivationGoal::startBuilder() void DerivationGoal::startBuilder()
{ {
PathSet missing = outputPaths(drv.outputs); startNest(nest, lvlInfo, format(repair ? "repairing path(s) %1%" : "building path(s) %1%") % showPaths(missingPaths));
foreach (PathSet::iterator, i, validPaths) missing.erase(*i);
startNest(nest, lvlInfo, format("building path(s) %1%") % showPaths(missing));
/* Right platform? */ /* Right platform? */
if (!canBuildLocally(drv.platform)) if (!canBuildLocally(drv.platform))
@@ -1678,7 +1735,8 @@ void DerivationGoal::startBuilder()
if HOME is not set, but they will just assume that the settings file if HOME is not set, but they will just assume that the settings file
they are looking for does not exist if HOME is set but points to some they are looking for does not exist if HOME is set but points to some
non-existing path. */ non-existing path. */
env["HOME"] = "/homeless-shelter"; Path homeDir = "/homeless-shelter";
env["HOME"] = homeDir;
/* Tell the builder where the Nix store is. Usually they /* Tell the builder where the Nix store is. Usually they
shouldn't care, but this is useful for purity checking (e.g., shouldn't care, but this is useful for purity checking (e.g.,
@@ -1726,7 +1784,7 @@ void DerivationGoal::startBuilder()
fixed-output derivations is by definition pure (since we fixed-output derivations is by definition pure (since we
already know the cryptographic hash of the output). */ already know the cryptographic hash of the output). */
if (fixedOutput) { if (fixedOutput) {
Strings varNames = tokenizeString<Strings>(drv.env["impureEnvVars"]); Strings varNames = tokenizeString<Strings>(get(drv.env, "impureEnvVars"));
foreach (Strings::iterator, i, varNames) env[*i] = getEnv(*i); foreach (Strings::iterator, i, varNames) env[*i] = getEnv(*i);
} }
@@ -1737,7 +1795,7 @@ void DerivationGoal::startBuilder()
temporary build directory. The text files have the format used temporary build directory. The text files have the format used
by `nix-store --register-validity'. However, the deriver by `nix-store --register-validity'. However, the deriver
fields are left empty. */ fields are left empty. */
string s = drv.env["exportReferencesGraph"]; string s = get(drv.env, "exportReferencesGraph");
Strings ss = tokenizeString<Strings>(s); Strings ss = tokenizeString<Strings>(s);
if (ss.size() % 2 != 0) if (ss.size() % 2 != 0)
throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s); throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s);
@@ -1823,7 +1881,7 @@ void DerivationGoal::startBuilder()
if (fixedOutput) useChroot = false; if (fixedOutput) useChroot = false;
/* Hack to allow derivations to disable chroot builds. */ /* Hack to allow derivations to disable chroot builds. */
if (drv.env["__noChroot"] == "1") useChroot = false; if (get(drv.env, "__noChroot") == "1") useChroot = false;
if (useChroot) { if (useChroot) {
#if CHROOT_ENABLED #if CHROOT_ENABLED
@@ -1869,8 +1927,14 @@ void DerivationGoal::startBuilder()
/* Bind-mount a user-configurable set of directories from the /* Bind-mount a user-configurable set of directories from the
host file system. */ host file system. */
dirsInChroot = settings.dirsInChroot; foreach (StringSet::iterator, i, settings.dirsInChroot) {
dirsInChroot.insert(tmpDir); size_t p = i->find('=');
if (p == string::npos)
dirsInChroot[*i] = *i;
else
dirsInChroot[string(*i, 0, p)] = string(*i, p + 1);
}
dirsInChroot[tmpDir] = tmpDir;
/* Make the closure of the inputs available in the chroot, /* Make the closure of the inputs available in the chroot,
rather than the whole Nix store. This prevents any access rather than the whole Nix store. This prevents any access
@@ -1887,12 +1951,8 @@ void DerivationGoal::startBuilder()
if (lstat(i->c_str(), &st)) if (lstat(i->c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % *i); throw SysError(format("getting attributes of path `%1%'") % *i);
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
dirsInChroot.insert(*i); dirsInChroot[*i] = *i;
else { else {
/* Creating a hard link to *i is impossible if its
immutable bit is set. So clear it first. */
makeMutable(*i);
Path p = chrootRootDir + *i; Path p = chrootRootDir + *i;
if (link(i->c_str(), p.c_str()) == -1) { if (link(i->c_str(), p.c_str()) == -1) {
/* Hard-linking fails if we exceed the maximum /* Hard-linking fails if we exceed the maximum
@@ -1925,6 +1985,9 @@ void DerivationGoal::startBuilder()
else { else {
if (pathExists(homeDir))
throw Error(format("directory `%1%' exists; please remove it") % homeDir);
/* We're not doing a chroot build, but we have some valid /* We're not doing a chroot build, but we have some valid
output paths. Since we can't just overwrite or delete output paths. Since we can't just overwrite or delete
them, we have to do hash rewriting: i.e. in the them, we have to do hash rewriting: i.e. in the
@@ -1942,7 +2005,7 @@ void DerivationGoal::startBuilder()
/* If we're repairing, then we don't want to delete the /* If we're repairing, then we don't want to delete the
corrupt outputs in advance. So rewrite them as well. */ corrupt outputs in advance. So rewrite them as well. */
if (repair) if (repair)
foreach (PathSet::iterator, i, missing) foreach (PathSet::iterator, i, missingPaths)
if (worker.store.isValidPath(*i) && pathExists(*i)) if (worker.store.isValidPath(*i) && pathExists(*i))
redirectedBadOutputs[*i] = addHashRewrite(*i); redirectedBadOutputs[*i] = addHashRewrite(*i);
} }
@@ -2008,8 +2071,8 @@ void DerivationGoal::startBuilder()
singleton<set<int> >(builderOut.readSide), true, true); singleton<set<int> >(builderOut.readSide), true, true);
if (settings.printBuildTrace) { if (settings.printBuildTrace) {
printMsg(lvlError, format("@ build-started %1% %2% %3% %4%") printMsg(lvlError, format("@ build-started %1% - %2% %3%")
% drvPath % drv.outputs["out"].path % drv.platform % logFile); % drvPath % drv.platform % logFile);
} }
} }
@@ -2062,12 +2125,20 @@ void DerivationGoal::initChild()
/* Bind-mount all the directories from the "host" /* Bind-mount all the directories from the "host"
filesystem that we want in the chroot filesystem that we want in the chroot
environment. */ environment. */
foreach (PathSet::iterator, i, dirsInChroot) { foreach (DirsInChroot::iterator, i, dirsInChroot) {
Path source = *i; struct stat st;
Path target = chrootRootDir + source; Path source = i->second;
Path target = chrootRootDir + i->first;
if (source == "/proc") continue; // backwards compatibility if (source == "/proc") continue; // backwards compatibility
debug(format("bind mounting `%1%' to `%2%'") % source % target); debug(format("bind mounting `%1%' to `%2%'") % source % target);
createDirs(target); if (stat(source.c_str(), &st) == -1)
throw SysError(format("getting attributes of path `%1%'") % source);
if (S_ISDIR(st.st_mode))
createDirs(target);
else {
createDirs(dirOf(target));
writeFile(target, "");
}
if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1) if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target); throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
} }
@@ -2105,7 +2176,11 @@ void DerivationGoal::initChild()
#ifdef CAN_DO_LINUX32_BUILDS #ifdef CAN_DO_LINUX32_BUILDS
/* Change the personality to 32-bit if we're doing an /* Change the personality to 32-bit if we're doing an
i686-linux build on an x86_64-linux machine. */ i686-linux build on an x86_64-linux machine. */
if (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux") { struct utsname utsbuf;
uname(&utsbuf);
if (drv.platform == "i686-linux" &&
(settings.thisSystem == "x86_64-linux" ||
(!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64")))) {
if (personality(0x0008 | 0x8000000 /* == PER_LINUX32_3GB */) == -1) if (personality(0x0008 | 0x8000000 /* == PER_LINUX32_3GB */) == -1)
throw SysError("cannot set i686-linux personality"); throw SysError("cannot set i686-linux personality");
} }
@@ -2227,6 +2302,8 @@ void DerivationGoal::computeClosure()
output paths read-only. */ output paths read-only. */
foreach (DerivationOutputs::iterator, i, drv.outputs) { foreach (DerivationOutputs::iterator, i, drv.outputs) {
Path path = i->second.path; Path path = i->second.path;
if (missingPaths.find(path) == missingPaths.end()) continue;
if (!pathExists(path)) { if (!pathExists(path)) {
throw BuildError( throw BuildError(
format("builder for `%1%' failed to produce output path `%2%'") format("builder for `%1%' failed to produce output path `%2%'")
@@ -2265,8 +2342,10 @@ void DerivationGoal::computeClosure()
% path % i->second.hashAlgo % printHash16or32(h) % printHash16or32(h2)); % path % i->second.hashAlgo % printHash16or32(h) % printHash16or32(h2));
} }
/* Get rid of all weird permissions. */ /* Get rid of all weird permissions. This also checks that
canonicalisePathMetaData(path); all files are owned by the build user, if applicable. */
canonicalisePathMetaData(path,
buildUser.enabled() && rewrittenPaths.find(path) == rewrittenPaths.end() ? buildUser.getUID() : -1, inodesSeen);
/* For this output path, find the references to other paths /* For this output path, find the references to other paths
contained in it. Compute the SHA-256 NAR hash at the same contained in it. Compute the SHA-256 NAR hash at the same
@@ -2293,7 +2372,7 @@ void DerivationGoal::computeClosure()
refer to), check that all references are in that list. !!! refer to), check that all references are in that list. !!!
allowedReferences should really be per-output. */ allowedReferences should really be per-output. */
if (drv.env.find("allowedReferences") != drv.env.end()) { if (drv.env.find("allowedReferences") != drv.env.end()) {
PathSet allowed = parseReferenceSpecifiers(drv, drv.env["allowedReferences"]); PathSet allowed = parseReferenceSpecifiers(drv, get(drv.env, "allowedReferences"));
foreach (PathSet::iterator, i, references) foreach (PathSet::iterator, i, references)
if (allowed.find(*i) == allowed.end()) if (allowed.find(*i) == allowed.end())
throw BuildError(format("output is not allowed to refer to path `%1%'") % *i); throw BuildError(format("output is not allowed to refer to path `%1%'") % *i);
@@ -2308,12 +2387,12 @@ void DerivationGoal::computeClosure()
paths referenced by each of them. If there are cycles in the paths referenced by each of them. If there are cycles in the
outputs, this will fail. */ outputs, this will fail. */
ValidPathInfos infos; ValidPathInfos infos;
foreach (DerivationOutputs::iterator, i, drv.outputs) { foreach (PathSet::iterator, i, missingPaths) {
ValidPathInfo info; ValidPathInfo info;
info.path = i->second.path; info.path = *i;
info.hash = contentHashes[i->second.path].first; info.hash = contentHashes[*i].first;
info.narSize = contentHashes[i->second.path].second; info.narSize = contentHashes[*i].second;
info.references = allReferences[i->second.path]; info.references = allReferences[*i];
info.deriver = drvPath; info.deriver = drvPath;
infos.push_back(info); infos.push_back(info);
} }
@@ -2326,15 +2405,19 @@ string drvsLogDir = "drvs";
Path DerivationGoal::openLogFile() Path DerivationGoal::openLogFile()
{ {
logSize = 0;
if (!settings.keepLog) return ""; if (!settings.keepLog) return "";
string baseName = baseNameOf(drvPath);
/* Create a log file. */ /* Create a log file. */
Path dir = (format("%1%/%2%") % settings.nixLogDir % drvsLogDir).str(); Path dir = (format("%1%/%2%/%3%/") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2)).str();
createDirs(dir); createDirs(dir);
if (settings.compressLog) { if (settings.compressLog) {
Path logFileName = (format("%1%/%2%.bz2") % dir % baseNameOf(drvPath)).str(); Path logFileName = (format("%1%/%2%.bz2") % dir % string(baseName, 2)).str();
AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName); if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName);
closeOnExec(fd); closeOnExec(fd);
@@ -2349,7 +2432,7 @@ Path DerivationGoal::openLogFile()
return logFileName; return logFileName;
} else { } else {
Path logFileName = (format("%1%/%2%") % dir % baseNameOf(drvPath)).str(); Path logFileName = (format("%1%/%2%") % dir % string(baseName, 2)).str();
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (fdLogFile == -1) throw SysError(format("creating log file `%1%'") % logFileName); if (fdLogFile == -1) throw SysError(format("creating log file `%1%'") % logFileName);
closeOnExec(fdLogFile); closeOnExec(fdLogFile);
@@ -2399,6 +2482,14 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
if ((hook && fd == hook->builderOut.readSide) || if ((hook && fd == hook->builderOut.readSide) ||
(!hook && fd == builderOut.readSide)) (!hook && fd == builderOut.readSide))
{ {
logSize += data.size();
if (settings.maxLogSize && logSize > settings.maxLogSize) {
printMsg(lvlError,
format("%1% killed after writing more than %2% bytes of log output")
% getName() % settings.maxLogSize);
cancel(true); // not really a timeout, but close enough
return;
}
if (verbosity >= settings.buildVerbosity) if (verbosity >= settings.buildVerbosity)
writeToStderr(data); writeToStderr(data);
if (bzLogFile) { if (bzLogFile) {
@@ -2443,7 +2534,7 @@ bool DerivationGoal::pathFailed(const Path & path)
printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path); printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path);
if (settings.printBuildTrace) if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% %2% cached") % drvPath % path); printMsg(lvlError, format("@ build-failed %1% - cached") % drvPath);
worker.permanentFailure = true; worker.permanentFailure = true;
amDone(ecFailed); amDone(ecFailed);
@@ -2514,7 +2605,7 @@ public:
SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false); SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false);
~SubstitutionGoal(); ~SubstitutionGoal();
void cancel(); void cancel(bool timeout);
void work(); void work();
@@ -2555,8 +2646,10 @@ SubstitutionGoal::~SubstitutionGoal()
} }
void SubstitutionGoal::cancel() void SubstitutionGoal::cancel(bool timeout)
{ {
if (settings.printBuildTrace && timeout)
printMsg(lvlError, format("@ substituter-failed %1% timeout") % storePath);
if (pid != -1) { if (pid != -1) {
pid_t savedPid = pid; pid_t savedPid = pid;
pid.kill(); pid.kill();
@@ -2638,7 +2731,7 @@ void SubstitutionGoal::referencesValid()
if (nrFailed > 0) { if (nrFailed > 0) {
debug(format("some references of path `%1%' could not be realised") % storePath); debug(format("some references of path `%1%' could not be realised") % storePath);
amDone(ecFailed); amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
return; return;
} }
@@ -2747,10 +2840,8 @@ void SubstitutionGoal::tryToRun()
state = &SubstitutionGoal::finished; state = &SubstitutionGoal::finished;
if (settings.printBuildTrace) { if (settings.printBuildTrace)
printMsg(lvlError, format("@ substituter-started %1% %2%") printMsg(lvlError, format("@ substituter-started %1% %2%") % storePath % sub);
% storePath % sub);
}
} }
@@ -2816,7 +2907,7 @@ void SubstitutionGoal::finished()
return; return;
} }
canonicalisePathMetaData(destPath); canonicalisePathMetaData(destPath, -1);
worker.store.optimisePath(destPath); // FIXME: combine with hashPath() worker.store.optimisePath(destPath); // FIXME: combine with hashPath()
@@ -2837,9 +2928,8 @@ void SubstitutionGoal::finished()
printMsg(lvlChatty, printMsg(lvlChatty,
format("substitution of path `%1%' succeeded") % storePath); format("substitution of path `%1%' succeeded") % storePath);
if (settings.printBuildTrace) { if (settings.printBuildTrace)
printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath); printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
}
amDone(ecSuccess); amDone(ecSuccess);
} }
@@ -2967,14 +3057,14 @@ unsigned Worker::getNrLocalBuilds()
void Worker::childStarted(GoalPtr goal, void Worker::childStarted(GoalPtr goal,
pid_t pid, const set<int> & fds, bool inBuildSlot, pid_t pid, const set<int> & fds, bool inBuildSlot,
bool monitorForSilence) bool respectTimeouts)
{ {
Child child; Child child;
child.goal = goal; child.goal = goal;
child.fds = fds; child.fds = fds;
child.lastOutput = time(0); child.timeStarted = child.lastOutput = time(0);
child.inBuildSlot = inBuildSlot; child.inBuildSlot = inBuildSlot;
child.monitorForSilence = monitorForSilence; child.respectTimeouts = respectTimeouts;
children[pid] = child; children[pid] = child;
if (inBuildSlot) nrLocalBuilds++; if (inBuildSlot) nrLocalBuilds++;
} }
@@ -3090,31 +3180,22 @@ void Worker::waitForInput()
timeout.tv_usec = 0; timeout.tv_usec = 0;
time_t before = time(0); time_t before = time(0);
/* If a global timeout has been set, sleep until it's done. */ /* If we're monitoring for silence on stdout/stderr, or if there
if (settings.buildTimeout != 0) { is a build timeout, then wait for input until the first
useTimeout = true; deadline for any child. */
if (lastWait == 0 || lastWait > before) lastWait = before; assert(sizeof(time_t) >= sizeof(long));
timeout.tv_sec = std::max((time_t) 0, lastWait + settings.buildTimeout - before); time_t nearest = LONG_MAX; // nearest deadline
foreach (Children::iterator, i, children) {
if (!i->second.respectTimeouts) continue;
if (settings.maxSilentTime != 0)
nearest = std::min(nearest, i->second.lastOutput + settings.maxSilentTime);
if (settings.buildTimeout != 0)
nearest = std::min(nearest, i->second.timeStarted + settings.buildTimeout);
} }
if (nearest != LONG_MAX) {
/* If we're monitoring for silence on stdout/stderr, sleep until timeout.tv_sec = std::max((time_t) 1, nearest - before);
the first deadline for any child. */ useTimeout = true;
if (settings.maxSilentTime != 0) { printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
time_t oldest = 0;
foreach (Children::iterator, i, children) {
if (i->second.monitorForSilence) {
oldest = oldest == 0 || i->second.lastOutput < oldest
? i->second.lastOutput : oldest;
}
}
if (oldest) {
time_t silenceTimeout = std::max((time_t) 0, oldest + settings.maxSilentTime - before);
timeout.tv_sec = useTimeout
? std::min(silenceTimeout, timeout.tv_sec)
: silenceTimeout;
useTimeout = true;
printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
}
} }
/* If we are polling goals that are waiting for a lock, then wake /* If we are polling goals that are waiting for a lock, then wake
@@ -3124,7 +3205,7 @@ void Worker::waitForInput()
if (lastWokenUp == 0) if (lastWokenUp == 0)
printMsg(lvlError, "waiting for locks or build slots..."); printMsg(lvlError, "waiting for locks or build slots...");
if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before; if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
timeout.tv_sec = std::max((time_t) 0, (time_t) (lastWokenUp + settings.pollInterval - before)); timeout.tv_sec = std::max((time_t) 1, (time_t) (lastWokenUp + settings.pollInterval - before));
} else lastWokenUp = 0; } else lastWokenUp = 0;
using namespace std; using namespace std;
@@ -3148,9 +3229,6 @@ void Worker::waitForInput()
time_t after = time(0); time_t after = time(0);
/* Keep track of when we were last called. */
lastWait = after;
/* Process all available file descriptors. */ /* Process all available file descriptors. */
/* Since goals may be canceled from inside the loop below (causing /* Since goals may be canceled from inside the loop below (causing
@@ -3191,22 +3269,23 @@ void Worker::waitForInput()
} }
if (settings.maxSilentTime != 0 && if (settings.maxSilentTime != 0 &&
j->second.monitorForSilence && j->second.respectTimeouts &&
after - j->second.lastOutput >= (time_t) settings.maxSilentTime) after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
{ {
printMsg(lvlError, printMsg(lvlError,
format("%1% timed out after %2% seconds of silence") format("%1% timed out after %2% seconds of silence")
% goal->getName() % settings.maxSilentTime); % goal->getName() % settings.maxSilentTime);
goal->cancel(); goal->cancel(true);
} }
if (settings.buildTimeout != 0 && if (settings.buildTimeout != 0 &&
after - before >= (time_t) settings.buildTimeout) j->second.respectTimeouts &&
after - j->second.timeStarted >= (time_t) settings.buildTimeout)
{ {
printMsg(lvlError, printMsg(lvlError,
format("%1% timed out after %2% seconds of activity") format("%1% timed out after %2% seconds")
% goal->getName() % settings.buildTimeout); % goal->getName() % settings.buildTimeout);
goal->cancel(); goal->cancel(true);
} }
} }
+186 -182
View File
@@ -1,7 +1,6 @@
#include "globals.hh" #include "globals.hh"
#include "misc.hh" #include "misc.hh"
#include "local-store.hh" #include "local-store.hh"
#include "immutable.hh"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
@@ -259,8 +258,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
only succeed if the owning process has died. In that case only succeed if the owning process has died. In that case
we don't care about its temporary roots. */ we don't care about its temporary roots. */
if (lockFile(*fd, ltWrite, false)) { if (lockFile(*fd, ltWrite, false)) {
printMsg(lvlError, format("removing stale temporary roots file `%1%'") printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
% path);
unlink(path.c_str()); unlink(path.c_str());
writeFull(*fd, (const unsigned char *) "d", 1); writeFull(*fd, (const unsigned char *) "d", 1);
continue; continue;
@@ -291,46 +289,47 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
} }
static void findRoots(StoreAPI & store, const Path & path, static void foundRoot(StoreAPI & store,
bool recurseSymlinks, bool deleteStale, Roots & roots) const Path & path, const Path & target, Roots & roots)
{
Path storePath = toStorePath(target);
if (store.isValidPath(storePath))
roots[path] = storePath;
else
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
}
static void findRoots(StoreAPI & store, const Path & path, Roots & roots)
{ {
try { try {
struct stat st; struct stat st = lstat(path);
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path);
printMsg(lvlVomit, format("looking at `%1%'") % path);
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path); Strings names = readDirectory(path);
foreach (Strings::iterator, i, names) foreach (Strings::iterator, i, names)
findRoots(store, path + "/" + *i, recurseSymlinks, deleteStale, roots); findRoots(store, path + "/" + *i, roots);
} }
else if (S_ISLNK(st.st_mode)) { else if (S_ISLNK(st.st_mode)) {
Path target = absPath(readLink(path), dirOf(path)); Path target = readLink(path);
if (isInStore(target))
foundRoot(store, path, target, roots);
if (isInStore(target)) { /* Handle indirect roots. */
debug(format("found root `%1%' in `%2%'") else {
% target % path); target = absPath(target, dirOf(path));
Path storePath = toStorePath(target); if (!pathExists(target)) {
if (store.isValidPath(storePath)) if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
roots[path] = storePath; printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
else unlink(path.c_str());
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") }
% path % storePath); } else {
} struct stat st2 = lstat(target);
if (!S_ISLNK(st2.st_mode)) return;
else if (recurseSymlinks) { Path target2 = readLink(target);
if (pathExists(target)) if (isInStore(target2)) foundRoot(store, target, target2, roots);
findRoots(store, target, false, deleteStale, roots);
else if (deleteStale) {
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
/* Note that we only delete when recursing, i.e.,
when we are still in the `gcroots' tree. We
never delete stuff outside that tree. */
unlink(path.c_str());
} }
} }
} }
@@ -347,18 +346,16 @@ static void findRoots(StoreAPI & store, const Path & path,
} }
static Roots findRoots(StoreAPI & store, bool deleteStale)
{
Roots roots;
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
findRoots(store, rootsDir, true, deleteStale, roots);
return roots;
}
Roots LocalStore::findRoots() Roots LocalStore::findRoots()
{ {
return nix::findRoots(*this, false); Roots roots;
/* Process direct roots in {gcroots,manifests,profiles}. */
nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, roots);
nix::findRoots(*this, settings.nixStateDir + "/manifests", roots);
nix::findRoots(*this, settings.nixStateDir + "/profiles", roots);
return roots;
} }
@@ -373,9 +370,9 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
string result = runProgram(rootFinder); string result = runProgram(rootFinder);
Strings paths = tokenizeString<Strings>(result, "\n"); StringSet paths = tokenizeString<StringSet>(result, "\n");
foreach (Strings::iterator, i, paths) { foreach (StringSet::iterator, i, paths) {
if (isInStore(*i)) { if (isInStore(*i)) {
Path path = toStorePath(*i); Path path = toStorePath(*i);
if (roots.find(path) == roots.end() && store.isValidPath(path)) { if (roots.find(path) == roots.end() && store.isValidPath(path)) {
@@ -396,24 +393,17 @@ struct LocalStore::GCState
GCResults & results; GCResults & results;
PathSet roots; PathSet roots;
PathSet tempRoots; PathSet tempRoots;
PathSet deleted; PathSet dead;
PathSet live; PathSet alive;
PathSet busy;
PathSet invalidated;
bool gcKeepOutputs; bool gcKeepOutputs;
bool gcKeepDerivations; bool gcKeepDerivations;
unsigned long long bytesInvalidated; unsigned long long bytesInvalidated;
Path trashDir;
bool shouldDelete;
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { } GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
}; };
static bool shouldDelete(GCOptions::GCAction action)
{
return action == GCOptions::gcDeleteDead
|| action == GCOptions::gcDeleteSpecific;
}
bool LocalStore::isActiveTempFile(const GCState & state, bool LocalStore::isActiveTempFile(const GCState & state,
const Path & path, const string & suffix) const Path & path, const string & suffix)
{ {
@@ -424,152 +414,149 @@ bool LocalStore::isActiveTempFile(const GCState & state,
void LocalStore::deleteGarbage(GCState & state, const Path & path) void LocalStore::deleteGarbage(GCState & state, const Path & path)
{ {
printMsg(lvlInfo, format("deleting `%1%'") % path);
unsigned long long bytesFreed; unsigned long long bytesFreed;
deletePathWrapped(path, bytesFreed); deletePathWrapped(path, bytesFreed);
state.results.bytesFreed += bytesFreed; state.results.bytesFreed += bytesFreed;
} }
bool LocalStore::tryToDelete(GCState & state, const Path & path) void LocalStore::deletePathRecursive(GCState & state, const Path & path)
{ {
checkInterrupt(); checkInterrupt();
if (path == linksDir) return true; unsigned long long size = 0;
if (isValidPath(path)) {
PathSet referrers;
queryReferrers(path, referrers);
foreach (PathSet::iterator, i, referrers)
if (*i != path) deletePathRecursive(state, *i);
size = queryPathInfo(path).narSize;
invalidatePathChecked(path);
}
struct stat st; struct stat st;
if (lstat(path.c_str(), &st)) { if (lstat(path.c_str(), &st)) {
if (errno == ENOENT) return true; if (errno == ENOENT) return;
throw SysError(format("getting status of %1%") % path); throw SysError(format("getting status of %1%") % path);
} }
if (state.deleted.find(path) != state.deleted.end()) return true; printMsg(lvlInfo, format("deleting `%1%'") % path);
if (state.live.find(path) != state.live.end()) return false;
state.results.paths.insert(path);
/* If the path is not a regular file or symlink, move it to the
trash directory. The move is to ensure that later (when we're
not holding the global GC lock) we can delete the path without
being afraid that the path has become alive again. Otherwise
delete it right away. */
if (S_ISDIR(st.st_mode)) {
// Estimate the amount freed using the narSize field. FIXME:
// if the path was not valid, need to determine the actual
// size.
state.bytesInvalidated += size;
// Mac OS X cannot rename directories if they are read-only.
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % path);
Path tmp = state.trashDir + "/" + baseNameOf(path);
if (rename(path.c_str(), tmp.c_str()))
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
} else
deleteGarbage(state, path);
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
throw GCLimitReached();
}
}
bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
{
if (visited.find(path) != visited.end()) return false;
if (state.alive.find(path) != state.alive.end()) {
return true;
}
if (state.dead.find(path) != state.dead.end()) {
return false;
}
if (state.roots.find(path) != state.roots.end()) {
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
state.alive.insert(path);
return true;
}
visited.insert(path);
if (!isValidPath(path)) return false;
PathSet incoming;
/* Don't delete this path if any of its referrers are alive. */
queryReferrers(path, incoming);
/* If gc-keep-derivations is set and this is a derivation, then
don't delete the derivation if any of the outputs are alive. */
if (state.gcKeepDerivations && isDerivation(path)) {
PathSet outputs = queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (isValidPath(*i) && queryDeriver(*i) == path)
incoming.insert(*i);
}
/* If gc-keep-outputs is set, then don't delete this path if there
are derivers of this path that are not garbage. */
if (state.gcKeepOutputs) {
PathSet derivers = queryValidDerivers(path);
foreach (PathSet::iterator, i, derivers)
incoming.insert(*i);
}
foreach (PathSet::iterator, i, incoming)
if (*i != path)
if (canReachRoot(state, visited, *i)) {
state.alive.insert(path);
return true;
}
return false;
}
void LocalStore::tryToDelete(GCState & state, const Path & path)
{
checkInterrupt();
if (path == linksDir || path == state.trashDir) return;
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path); startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
/* If gc-keep-outputs and gc-keep-derivations are both set, we can if (!isValidPath(path)) {
have cycles in the liveness graph, so we need to treat such
strongly connected components as a single unit (paths). That
is, we can delete the elements of paths only if all referrers
of paths are garbage. */
PathSet paths, referrers;
Paths pathsSorted;
if (isValidPath(path)) {
/* Add derivers and outputs of path to paths. */
PathSet todo;
todo.insert(path);
while (!todo.empty()) {
Path p = *todo.begin();
assertStorePath(p);
todo.erase(p);
if (paths.find(p) != paths.end()) continue;
paths.insert(p);
/* If gc-keep-derivations is set and this is a derivation,
then don't delete the derivation if any of the outputs
are live. */
if (state.gcKeepDerivations && isDerivation(p)) {
PathSet outputs = queryDerivationOutputs(p);
foreach (PathSet::iterator, i, outputs)
if (isValidPath(*i)) todo.insert(*i);
}
/* If gc-keep-outputs is set, then don't delete this path
if there are derivers of this path that are not
garbage. */
if (state.gcKeepOutputs) {
PathSet derivers = queryValidDerivers(p);
foreach (PathSet::iterator, i, derivers) todo.insert(*i);
}
}
}
else {
/* A lock file belonging to a path that we're building right /* A lock file belonging to a path that we're building right
now isn't garbage. */ now isn't garbage. */
if (isActiveTempFile(state, path, ".lock")) return false; if (isActiveTempFile(state, path, ".lock")) return;
/* Don't delete .chroot directories for derivations that are /* Don't delete .chroot directories for derivations that are
currently being built. */ currently being built. */
if (isActiveTempFile(state, path, ".chroot")) return false; if (isActiveTempFile(state, path, ".chroot")) return;
paths.insert(path);
} }
/* Check if any path in paths is a root. */ PathSet visited;
foreach (PathSet::iterator, i, paths)
if (state.roots.find(*i) != state.roots.end()) {
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % *i);
goto isLive;
}
/* Recursively try to delete the referrers of this strongly if (canReachRoot(state, visited, path)) {
connected component. If any referrer can't be deleted, then printMsg(lvlDebug, format("cannot delete `%1%' because it's still reachable") % path);
these paths can't be deleted either. */ } else {
foreach (PathSet::iterator, i, paths) /* No path we visited was a root, so everything is garbage.
if (isValidPath(*i)) queryReferrers(*i, referrers); But we only delete path and its referrers here so that
nix-store --delete doesn't have the unexpected effect of
foreach (PathSet::iterator, i, referrers) recursing into derivations and outputs. */
if (paths.find(*i) == paths.end() && !tryToDelete(state, *i)) { state.dead.insert(visited.begin(), visited.end());
printMsg(lvlDebug, format("cannot delete `%1%' because it has live referrers") % *i); if (state.shouldDelete)
goto isLive; deletePathRecursive(state, path);
}
/* The paths are garbage, so delete them. */
pathsSorted = topoSortPaths(*this, paths);
foreach (Paths::iterator, i, pathsSorted) {
if (shouldDelete(state.options.action)) {
/* If it's a valid path that's not a regular file or
symlink, invalidate it, rename it, and schedule it for
deletion. The renaming is to ensure that later (when
we're not holding the global GC lock) we can delete the
path without being afraid that the path has become
alive again. Otherwise delete it right away. */
if (isValidPath(*i)) {
if (S_ISDIR(st.st_mode)) {
printMsg(lvlInfo, format("invalidating `%1%'") % *i);
// Estimate the amount freed using the narSize field.
state.bytesInvalidated += queryPathInfo(*i).narSize;
invalidatePathChecked(*i);
makeMutable(i->c_str());
// Mac OS X cannot rename directories if they are read-only.
if (chmod(i->c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % *i);
Path tmp = (format("%1%-gc-%2%") % *i % getpid()).str();
if (rename(i->c_str(), tmp.c_str()))
throw SysError(format("unable to rename `%1%' to `%2%'") % *i % tmp);
state.invalidated.insert(tmp);
} else {
invalidatePathChecked(*i);
deleteGarbage(state, *i);
}
} else
deleteGarbage(state, *i);
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
throw GCLimitReached();
}
} else
printMsg(lvlTalkative, format("would delete `%1%'") % *i);
state.deleted.insert(*i);
if (state.options.action != GCOptions::gcReturnLive)
state.results.paths.insert(*i);
} }
return true;
isLive:
foreach (PathSet::iterator, i, paths) {
state.live.insert(*i);
if (state.options.action == GCOptions::gcReturnLive)
state.results.paths.insert(*i);
}
return false;
} }
@@ -625,7 +612,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
{ {
GCState state(results); GCState state(results);
state.options = options; state.options = options;
state.trashDir = settings.nixStore + "/trash";
state.gcKeepOutputs = settings.gcKeepOutputs; state.gcKeepOutputs = settings.gcKeepOutputs;
state.gcKeepDerivations = settings.gcKeepDerivations; state.gcKeepDerivations = settings.gcKeepDerivations;
@@ -638,6 +625,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
state.gcKeepDerivations = false; state.gcKeepDerivations = false;
} }
state.shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific;
/* Acquire the global GC root. This prevents /* Acquire the global GC root. This prevents
a) New roots from being added. a) New roots from being added.
b) Processes from creating new temporary root files. */ b) Processes from creating new temporary root files. */
@@ -646,7 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Find the roots. Since we've grabbed the GC lock, the set of /* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */ permanent roots cannot increase now. */
printMsg(lvlError, format("finding garbage collector roots...")); printMsg(lvlError, format("finding garbage collector roots..."));
Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(*this, true); Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second); foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
@@ -668,6 +657,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
increase, since we hold locks on everything. So everything increase, since we hold locks on everything. So everything
that is not reachable from `roots'. */ that is not reachable from `roots'. */
if (state.shouldDelete) {
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
createDirs(state.trashDir);
}
/* Now either delete all garbage paths, or just the specified /* Now either delete all garbage paths, or just the specified
paths (for gcDeleteSpecific). */ paths (for gcDeleteSpecific). */
@@ -675,13 +669,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
foreach (PathSet::iterator, i, options.pathsToDelete) { foreach (PathSet::iterator, i, options.pathsToDelete) {
assertStorePath(*i); assertStorePath(*i);
if (!tryToDelete(state, *i)) tryToDelete(state, *i);
if (state.dead.find(*i) == state.dead.end())
throw Error(format("cannot delete path `%1%' since it is still alive") % *i); throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
} }
} else if (options.maxFreed > 0) { } else if (options.maxFreed > 0) {
if (shouldDelete(state.options.action)) if (state.shouldDelete)
printMsg(lvlError, format("deleting garbage...")); printMsg(lvlError, format("deleting garbage..."));
else else
printMsg(lvlError, format("determining live/dead paths...")); printMsg(lvlError, format("determining live/dead paths..."));
@@ -727,13 +722,22 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
} }
} }
if (state.options.action == GCOptions::gcReturnLive) {
state.results.paths = state.alive;
return;
}
if (state.options.action == GCOptions::gcReturnDead) {
state.results.paths = state.dead;
return;
}
/* Allow other processes to add to the store from here on. */ /* Allow other processes to add to the store from here on. */
fdGCLock.close(); fdGCLock.close();
/* Delete the invalidated paths now that the lock has been /* Delete the trash directory. */
released. */ printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);
foreach (PathSet::iterator, i, state.invalidated) deleteGarbage(state, state.trashDir);
deleteGarbage(state, *i);
/* Clean up the links directory. */ /* Clean up the links directory. */
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) { if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
+15 -1
View File
@@ -10,6 +10,14 @@
namespace nix { namespace nix {
/* The default location of the daemon socket, relative to nixStateDir.
The socket is in a directory to allow you to control access to the
Nix daemon by setting the mode/ownership of the directory
appropriately. (This wouldn't work on the socket itself since it
must be deleted and recreated on startup.) */
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
Settings settings; Settings settings;
@@ -38,6 +46,7 @@ Settings::Settings()
impersonateLinux26 = false; impersonateLinux26 = false;
keepLog = true; keepLog = true;
compressLog = true; compressLog = true;
maxLogSize = 0;
cacheFailure = false; cacheFailure = false;
pollInterval = 5; pollInterval = 5;
checkRootReachability = false; checkRootReachability = false;
@@ -45,6 +54,7 @@ Settings::Settings()
gcKeepDerivations = true; gcKeepDerivations = true;
autoOptimiseStore = false; autoOptimiseStore = false;
envKeepDerivations = false; envKeepDerivations = false;
lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
} }
@@ -58,11 +68,14 @@ void Settings::processEnvironment()
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR)); nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)); nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)); nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
string subs = getEnv("NIX_SUBSTITUTERS", "default"); string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") { if (subs == "default") {
#if 0
if (getEnv("NIX_OTHER_STORES") != "") if (getEnv("NIX_OTHER_STORES") != "")
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl"); substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
#endif
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl"); substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl"); substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
} else } else
@@ -129,6 +142,7 @@ void Settings::update()
get(impersonateLinux26, "build-impersonate-linux-26"); get(impersonateLinux26, "build-impersonate-linux-26");
get(keepLog, "build-keep-log"); get(keepLog, "build-keep-log");
get(compressLog, "build-compress-log"); get(compressLog, "build-compress-log");
get(maxLogSize, "build-max-log-size");
get(cacheFailure, "build-cache-failure"); get(cacheFailure, "build-cache-failure");
get(pollInterval, "build-poll-interval"); get(pollInterval, "build-poll-interval");
get(checkRootReachability, "gc-check-reachability"); get(checkRootReachability, "gc-check-reachability");
@@ -158,7 +172,7 @@ void Settings::get(bool & res, const string & name)
} }
void Settings::get(PathSet & res, const string & name) void Settings::get(StringSet & res, const string & name)
{ {
SettingsMap::iterator i = settings.find(name); SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return; if (i == settings.end()) return;
+12 -2
View File
@@ -50,6 +50,9 @@ struct Settings {
/* The directory where the main programs are stored. */ /* The directory where the main programs are stored. */
Path nixBinDir; Path nixBinDir;
/* File name of the socket the daemon listens to. */
Path nixDaemonSocketFile;
/* Whether to keep temporary directories of failed builds. */ /* Whether to keep temporary directories of failed builds. */
bool keepFailed; bool keepFailed;
@@ -139,7 +142,7 @@ struct Settings {
/* The directories from the host filesystem to be included in the /* The directories from the host filesystem to be included in the
chroot. */ chroot. */
PathSet dirsInChroot; StringSet dirsInChroot;
/* Whether to impersonate a Linux 2.6 machine on newer kernels. */ /* Whether to impersonate a Linux 2.6 machine on newer kernels. */
bool impersonateLinux26; bool impersonateLinux26;
@@ -150,6 +153,10 @@ struct Settings {
/* Whether to compress logs. */ /* Whether to compress logs. */
bool compressLog; bool compressLog;
/* Maximum number of bytes a builder can write to stdout/stderr
before being killed (0 means no limit). */
unsigned long maxLogSize;
/* Whether to cache build failures. */ /* Whether to cache build failures. */
bool cacheFailure; bool cacheFailure;
@@ -176,12 +183,15 @@ struct Settings {
(to prevent them from being GCed). */ (to prevent them from being GCed). */
bool envKeepDerivations; bool envKeepDerivations;
/* Whether to lock the Nix client and worker to the same CPU. */
bool lockCPU;
private: private:
SettingsMap settings, overrides; SettingsMap settings, overrides;
void get(string & res, const string & name); void get(string & res, const string & name);
void get(bool & res, const string & name); void get(bool & res, const string & name);
void get(PathSet & res, const string & name); void get(StringSet & res, const string & name);
template<class N> void get(N & res, const string & name); template<class N> void get(N & res, const string & name);
}; };
File diff suppressed because it is too large Load Diff
+28 -10
View File
@@ -17,8 +17,8 @@ namespace nix {
/* Nix store and database schema version. Version 1 (or 0) was Nix <= /* Nix store and database schema version. Version 1 (or 0) was Nix <=
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10. 0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is
Nix 1.0. */ Nix 1.0. Version 7 is Nix 1.3. */
const int nixSchemaVersion = 6; const int nixSchemaVersion = 7;
extern string drvsLogDir; extern string drvsLogDir;
@@ -44,8 +44,12 @@ struct OptimiseStats
struct RunningSubstituter struct RunningSubstituter
{ {
Path program;
Pid pid; Pid pid;
AutoCloseFD to, from, error; AutoCloseFD to, from, error;
FdSource fromBuf;
bool disabled;
RunningSubstituter() : disabled(false) { };
}; };
@@ -111,10 +115,6 @@ public:
Path queryDeriver(const Path & path); Path queryDeriver(const Path & path);
/* Return all currently valid derivations that have `path' as an
output. (Note that the result of `queryDeriver()' is the
derivation that was actually used to produce `path', which may
not exist anymore.) */
PathSet queryValidDerivers(const Path & path); PathSet queryValidDerivers(const Path & path);
PathSet queryDerivationOutputs(const Path & path); PathSet queryDerivationOutputs(const Path & path);
@@ -177,7 +177,7 @@ public:
/* Register the validity of a path, i.e., that `path' exists, that /* Register the validity of a path, i.e., that `path' exists, that
the paths referenced by it exists, and in the case of an output the paths referenced by it exists, and in the case of an output
path of a derivation, that it has been produced by a succesful path of a derivation, that it has been produced by a successful
execution of the derivation (or something equivalent). Also execution of the derivation (or something equivalent). Also
register the hash of the file system contents of the path. The register the hash of the file system contents of the path. The
hash must be a SHA-256 hash. */ hash must be a SHA-256 hash. */
@@ -269,6 +269,7 @@ private:
void updatePathInfo(const ValidPathInfo & info); void updatePathInfo(const ValidPathInfo & info);
void upgradeStore6(); void upgradeStore6();
void upgradeStore7();
PathSet queryValidPathsOld(); PathSet queryValidPathsOld();
ValidPathInfo queryPathInfoOld(const Path & path); ValidPathInfo queryPathInfoOld(const Path & path);
@@ -276,7 +277,11 @@ private:
void deleteGarbage(GCState & state, const Path & path); void deleteGarbage(GCState & state, const Path & path);
bool tryToDelete(GCState & state, const Path & path); void tryToDelete(GCState & state, const Path & path);
bool canReachRoot(GCState & state, PathSet & visited, const Path & path);
void deletePathRecursive(GCState & state, const Path & path);
bool isActiveTempFile(const GCState & state, bool isActiveTempFile(const GCState & state,
const Path & path, const string & suffix); const Path & path, const string & suffix);
@@ -288,6 +293,10 @@ private:
void startSubstituter(const Path & substituter, void startSubstituter(const Path & substituter,
RunningSubstituter & runningSubstituter); RunningSubstituter & runningSubstituter);
string getLineFromSubstituter(RunningSubstituter & run);
template<class T> T getIntLineFromSubstituter(RunningSubstituter & run);
Path createTempDirInStore(); Path createTempDirInStore();
Path importPath(bool requireSignature, Source & source); Path importPath(bool requireSignature, Source & source);
@@ -295,9 +304,17 @@ private:
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv); void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
void optimisePath_(OptimiseStats & stats, const Path & path); 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);
}; };
typedef std::pair<dev_t, ino_t> Inode;
typedef set<Inode> InodesSeen;
/* "Fix", or canonicalise, the meta-data of the files in a store path /* "Fix", or canonicalise, the meta-data of the files in a store path
after it has been built. In particular: after it has been built. In particular:
- the last modification date on each file is set to 1 (i.e., - the last modification date on each file is set to 1 (i.e.,
@@ -306,9 +323,10 @@ private:
without execute permission; setuid bits etc. are cleared) without execute permission; setuid bits etc. are cleared)
- the owner and group are set to the Nix user and group, if we're - the owner and group are set to the Nix user and group, if we're
in a setuid Nix installation. */ in a setuid Nix installation. */
void canonicalisePathMetaData(const Path & path); void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen);
void canonicalisePathMetaData(const Path & path, uid_t fromUid);
void canonicalisePathMetaData(const Path & path, bool recurse); void canonicaliseTimestampAndPermissions(const Path & path);
MakeError(PathInUse, Error); MakeError(PathInUse, Error);
+39 -18
View File
@@ -15,27 +15,47 @@ Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
} }
void computeFSClosure(StoreAPI & store, const Path & storePath, void computeFSClosure(StoreAPI & store, const Path & path,
PathSet & paths, bool flipDirection, bool includeOutputs) PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
{ {
if (paths.find(storePath) != paths.end()) return; if (paths.find(path) != paths.end()) return;
paths.insert(storePath); paths.insert(path);
PathSet references; PathSet edges;
if (flipDirection)
store.queryReferrers(storePath, references);
else
store.queryReferences(storePath, references);
if (includeOutputs && isDerivation(storePath)) { if (flipDirection) {
PathSet outputs = store.queryDerivationOutputs(storePath); store.queryReferrers(path, edges);
foreach (PathSet::iterator, i, outputs)
if (store.isValidPath(*i)) if (includeOutputs) {
computeFSClosure(store, *i, paths, flipDirection, true); PathSet derivers = store.queryValidDerivers(path);
foreach (PathSet::iterator, i, derivers)
edges.insert(*i);
}
if (includeDerivers && isDerivation(path)) {
PathSet outputs = store.queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (store.isValidPath(*i) && store.queryDeriver(*i) == path)
edges.insert(*i);
}
} else {
store.queryReferences(path, edges);
if (includeOutputs && isDerivation(path)) {
PathSet outputs = store.queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (store.isValidPath(*i)) edges.insert(*i);
}
if (includeDerivers) {
Path deriver = store.queryDeriver(path);
if (store.isValidPath(deriver)) edges.insert(deriver);
}
} }
foreach (PathSet::iterator, i, references) foreach (PathSet::iterator, i, edges)
computeFSClosure(store, *i, paths, flipDirection, includeOutputs); computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
} }
@@ -100,7 +120,8 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
if (invalid.empty()) continue; if (invalid.empty()) continue;
todoDrv.insert(*i); todoDrv.insert(*i);
if (settings.useSubstitutes) query.insert(invalid.begin(), invalid.end()); if (settings.useSubstitutes && !willBuildLocally(drv))
query.insert(invalid.begin(), invalid.end());
} }
else { else {
@@ -123,7 +144,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
PathSet outputs; PathSet outputs;
bool mustBuild = false; bool mustBuild = false;
if (settings.useSubstitutes) { if (settings.useSubstitutes && !willBuildLocally(drv)) {
foreach (DerivationOutputs::iterator, j, drv.outputs) { foreach (DerivationOutputs::iterator, j, drv.outputs) {
if (!wantOutput(j->first, i2.second)) continue; if (!wantOutput(j->first, i2.second)) continue;
if (!store.isValidPath(j->second.path)) { if (!store.isValidPath(j->second.path)) {
+4 -2
View File
@@ -17,9 +17,9 @@ Derivation derivationFromPath(StoreAPI & store, const Path & drvPath);
`storePath' is returned; that is, the closures under the `storePath' is returned; that is, the closures under the
`referrers' relation instead of the `references' relation is `referrers' relation instead of the `references' relation is
returned. */ returned. */
void computeFSClosure(StoreAPI & store, const Path & storePath, void computeFSClosure(StoreAPI & store, const Path & path,
PathSet & paths, bool flipDirection = false, PathSet & paths, bool flipDirection = false,
bool includeOutputs = false); bool includeOutputs = false, bool includeDerivers = false);
/* Return the path corresponding to the output identifier `id' in the /* Return the path corresponding to the output identifier `id' in the
given derivation. */ given derivation. */
@@ -32,5 +32,7 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown, PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize); unsigned long long & downloadSize, unsigned long long & narSize);
bool willBuildLocally(const Derivation & drv);
} }
+1 -10
View File
@@ -2,7 +2,6 @@
#include "util.hh" #include "util.hh"
#include "local-store.hh" #include "local-store.hh"
#include "immutable.hh"
#include "globals.hh" #include "globals.hh"
#include <sys/types.h> #include <sys/types.h>
@@ -20,7 +19,6 @@ static void makeWritable(const Path & path)
struct stat st; struct stat st;
if (lstat(path.c_str(), &st)) if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path); throw SysError(format("getting attributes of path `%1%'") % path);
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("changing writability of `%1%'") % path); throw SysError(format("changing writability of `%1%'") % path);
} }
@@ -34,7 +32,7 @@ struct MakeReadOnly
{ {
try { try {
/* This will make the path read-only. */ /* This will make the path read-only. */
if (path != "") canonicalisePathMetaData(path, false); if (path != "") canonicaliseTimestampAndPermissions(path);
} catch (...) { } catch (...) {
ignoreException(); ignoreException();
} }
@@ -91,7 +89,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
if (!pathExists(linkPath)) { if (!pathExists(linkPath)) {
/* Nope, create a hard link in the links directory. */ /* Nope, create a hard link in the links directory. */
makeMutable(path);
if (link(path.c_str(), linkPath.c_str()) == 0) return; if (link(path.c_str(), linkPath.c_str()) == 0) return;
if (errno != EEXIST) if (errno != EEXIST)
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path); throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
@@ -123,12 +120,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
its timestamp back to 0. */ its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : ""); MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
/* If linkPath is immutable, we can't create hard links to it,
so make it mutable first. We also have to make path mutable,
otherwise rename() will fail to delete it. */
makeMutable(path);
makeMutable(linkPath);
Path tempLink = (format("%1%/.tmp-link-%2%-%3%") Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% settings.nixStore % getpid() % rand()).str(); % settings.nixStore % getpid() % rand()).str();
+33 -3
View File
@@ -3,6 +3,7 @@
#include "remote-store.hh" #include "remote-store.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "archive.hh" #include "archive.hh"
#include "affinity.hh"
#include "globals.hh" #include "globals.hh"
#include <sys/types.h> #include <sys/types.h>
@@ -15,7 +16,6 @@
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
namespace nix { namespace nix {
@@ -71,8 +71,19 @@ void RemoteStore::openConnection(bool reserveSpace)
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
throw Error("Nix daemon protocol version not supported"); throw Error("Nix daemon protocol version not supported");
writeInt(PROTOCOL_VERSION, to); writeInt(PROTOCOL_VERSION, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
if (cpu != -1) {
writeInt(1, to);
writeInt(cpu, to);
} else
writeInt(0, to);
}
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
writeInt(reserveSpace, to); writeInt(reserveSpace, to);
processStderr(); processStderr();
} }
catch (Error & e) { catch (Error & e) {
@@ -91,7 +102,7 @@ void RemoteStore::connectToDaemon()
throw SysError("cannot create Unix domain socket"); throw SysError("cannot create Unix domain socket");
closeOnExec(fdSocket); closeOnExec(fdSocket);
string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH; string socketPath = settings.nixDaemonSocketFile;
/* Urgh, sockaddr_un allows path names of only 108 characters. So /* Urgh, sockaddr_un allows path names of only 108 characters. So
chdir to the socket directory so that we can pass a relative chdir to the socket directory so that we can pass a relative
@@ -334,6 +345,16 @@ Path RemoteStore::queryDeriver(const Path & path)
} }
PathSet RemoteStore::queryValidDerivers(const Path & path)
{
openConnection();
writeInt(wopQueryValidDerivers, to);
writeString(path, to);
processStderr();
return readStorePaths<PathSet>(from);
}
PathSet RemoteStore::queryDerivationOutputs(const Path & path) PathSet RemoteStore::queryDerivationOutputs(const Path & path)
{ {
openConnection(); openConnection();
@@ -431,7 +452,16 @@ void RemoteStore::buildPaths(const PathSet & drvPaths, bool repair)
if (repair) throw Error("repairing is not supported when building through the Nix daemon"); if (repair) throw Error("repairing is not supported when building through the Nix daemon");
openConnection(); openConnection();
writeInt(wopBuildPaths, to); writeInt(wopBuildPaths, to);
writeStrings(drvPaths, to); if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
writeStrings(drvPaths, to);
else {
/* For backwards compatibility with old daemons, strip output
identifiers. */
PathSet drvPaths2;
foreach (PathSet::const_iterator, i, drvPaths)
drvPaths2.insert(string(*i, 0, i->find('!')));
writeStrings(drvPaths2, to);
}
processStderr(); processStderr();
readInt(from); readInt(from);
} }
+2
View File
@@ -40,6 +40,8 @@ public:
Path queryDeriver(const Path & path); Path queryDeriver(const Path & path);
PathSet queryValidDerivers(const Path & path);
PathSet queryDerivationOutputs(const Path & path); PathSet queryDerivationOutputs(const Path & path);
StringSet queryDerivationOutputNames(const Path & path); StringSet queryDerivationOutputNames(const Path & path);
+1 -4
View File
@@ -18,10 +18,7 @@ GCOptions::GCOptions()
bool isInStore(const Path & path) bool isInStore(const Path & path)
{ {
return path[0] == '/' return isInDir(path, settings.nixStore);
&& string(path, 0, settings.nixStore.size()) == settings.nixStore
&& path.size() >= settings.nixStore.size() + 2
&& path[settings.nixStore.size()] == '/';
} }
+7 -1
View File
@@ -133,6 +133,12 @@ public:
no deriver has been set. */ no deriver has been set. */
virtual Path queryDeriver(const Path & path) = 0; virtual Path queryDeriver(const Path & path) = 0;
/* Return all currently valid derivations that have `path' as an
output. (Note that the result of `queryDeriver()' is the
derivation that was actually used to produce `path', which may
not exist anymore.) */
virtual PathSet queryValidDerivers(const Path & path) = 0;
/* Query the outputs of the derivation denoted by `path'. */ /* Query the outputs of the derivation denoted by `path'. */
virtual PathSet queryDerivationOutputs(const Path & path) = 0; virtual PathSet queryDerivationOutputs(const Path & path) = 0;
@@ -322,7 +328,7 @@ Path addPermRoot(StoreAPI & store, const Path & storePath,
/* Sort a set of paths topologically under the references relation. /* Sort a set of paths topologically under the references relation.
If p refers to q, then p follows q in this list. */ If p refers to q, then p preceeds q in this list. */
Paths topoSortPaths(StoreAPI & store, const PathSet & paths); Paths topoSortPaths(StoreAPI & store, const PathSet & paths);
+2 -9
View File
@@ -6,7 +6,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f #define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION 0x10c #define PROTOCOL_VERSION 0x10e
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
@@ -42,6 +42,7 @@ typedef enum {
wopQuerySubstitutablePathInfos = 30, wopQuerySubstitutablePathInfos = 30,
wopQueryValidPaths = 31, wopQueryValidPaths = 31,
wopQuerySubstitutablePaths = 32, wopQuerySubstitutablePaths = 32,
wopQueryValidDerivers = 33,
} WorkerOp; } WorkerOp;
@@ -52,14 +53,6 @@ typedef enum {
#define STDERR_ERROR 0x63787470 #define STDERR_ERROR 0x63787470
/* The default location of the daemon socket, relative to nixStateDir.
The socket is in a directory to allow you to control access to the
Nix daemon by setting the mode/ownership of the directory
appropriately. (This wouldn't work on the socket itself since it
must be deleted and recreated on startup.) */
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
Path readStorePath(Source & from); Path readStorePath(Source & from);
template<class T> T readStorePaths(Source & from); template<class T> T readStorePaths(Source & from);
+2 -2
View File
@@ -1,12 +1,12 @@
pkglib_LTLIBRARIES = libutil.la pkglib_LTLIBRARIES = libutil.la
libutil_la_SOURCES = util.cc hash.cc serialise.cc \ libutil_la_SOURCES = util.cc hash.cc serialise.cc \
archive.cc xml-writer.cc immutable.cc archive.cc xml-writer.cc affinity.cc
libutil_la_LIBADD = ../boost/format/libformat.la libutil_la_LIBADD = ../boost/format/libformat.la
pkginclude_HEADERS = util.hh hash.hh serialise.hh \ pkginclude_HEADERS = util.hh hash.hh serialise.hh \
archive.hh xml-writer.hh types.hh immutable.hh archive.hh xml-writer.hh types.hh affinity.hh
if !HAVE_OPENSSL if !HAVE_OPENSSL
libutil_la_SOURCES += \ libutil_la_SOURCES += \
+55
View File
@@ -0,0 +1,55 @@
#include "types.hh"
#include "util.hh"
#include "affinity.hh"
#if HAVE_SCHED_H
#include <sched.h>
#endif
namespace nix {
#if HAVE_SCHED_SETAFFINITY
static bool didSaveAffinity = false;
static cpu_set_t savedAffinity;
#endif
void setAffinityTo(int cpu)
{
#if HAVE_SCHED_SETAFFINITY
if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
didSaveAffinity = true;
printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
cpu_set_t newAffinity;
CPU_ZERO(&newAffinity);
CPU_SET(cpu, &newAffinity);
if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu);
#endif
}
int lockToCurrentCPU()
{
#if HAVE_SCHED_SETAFFINITY
int cpu = sched_getcpu();
if (cpu != -1) setAffinityTo(cpu);
return cpu;
#else
return -1;
#endif
}
void restoreAffinity()
{
#if HAVE_SCHED_SETAFFINITY
if (!didSaveAffinity) return;
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
printMsg(lvlError, "failed to restore affinity %1%");
#endif
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
namespace nix {
void setAffinityTo(int cpu);
int lockToCurrentCPU();
void restoreAffinity();
}
+1
View File
@@ -282,6 +282,7 @@ struct RestoreSink : ParseSink
void createRegularFile(const Path & path) void createRegularFile(const Path & path)
{ {
Path p = dstPath + path; Path p = dstPath + path;
fd.close();
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666); fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd == -1) throw SysError(format("creating file `%1%'") % p); if (fd == -1) throw SysError(format("creating file `%1%'") % p);
} }
-49
View File
@@ -1,49 +0,0 @@
#include "config.h"
#include "immutable.hh"
#include "util.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if HAVE_LINUX_FS_H
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#endif
namespace nix {
void makeMutable(const Path & path)
{
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
/* Don't even try if we're not root. One day we should support
the CAP_LINUX_IMMUTABLE capability. */
if (getuid() != 0) return;
/* The O_NOFOLLOW is important to prevent us from changing the
mutable bit on the target of a symlink (which would be a
security hole). */
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
if (errno == ELOOP) return; // it's a symlink
throw SysError(format("opening file `%1%'") % path);
}
unsigned int flags = 0, old;
/* Silently ignore errors getting/setting the immutable flag so
that we work correctly on filesystems that don't support it. */
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
old = flags;
flags &= ~FS_IMMUTABLE_FL;
if (old == flags) return;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
#endif
}
}
-10
View File
@@ -1,10 +0,0 @@
#pragma once
#include <types.hh>
namespace nix {
/* Make the given path mutable. */
void makeMutable(const Path & path);
}
+6
View File
@@ -90,6 +90,12 @@ size_t BufferedSource::read(unsigned char * data, size_t len)
} }
bool BufferedSource::hasData()
{
return bufPosOut < bufPosIn;
}
size_t FdSource::readUnbuffered(unsigned char * data, size_t len) size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
{ {
ssize_t n; ssize_t n;
+3 -1
View File
@@ -61,8 +61,10 @@ struct BufferedSource : Source
size_t read(unsigned char * data, size_t len); size_t read(unsigned char * data, size_t len);
/* Underlying read call, to be overriden. */ /* Underlying read call, to be overridden. */
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0; virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
bool hasData();
}; };
+69 -57
View File
@@ -12,8 +12,11 @@
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#ifdef __APPLE__
#include <sys/syscall.h>
#endif
#include "util.hh" #include "util.hh"
#include "immutable.hh"
extern char * * environ; extern char * * environ;
@@ -100,7 +103,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
i++; i++;
/* If `..', delete the last component. */ /* If `..', delete the last component. */
else if (*i == '.' && i + 1 < end && i[1] == '.' && else if (*i == '.' && i + 1 < end && i[1] == '.' &&
(i + 2 == end || i[2] == '/')) (i + 2 == end || i[2] == '/'))
{ {
if (!s.empty()) s.erase(s.rfind('/')); if (!s.empty()) s.erase(s.rfind('/'));
@@ -149,6 +152,15 @@ string baseNameOf(const Path & path)
} }
bool isInDir(const Path & path, const Path & dir)
{
return path[0] == '/'
&& string(path, 0, dir.size()) == dir
&& path.size() >= dir.size() + 2
&& path[dir.size()] == '/';
}
struct stat lstat(const Path & path) struct stat lstat(const Path & path)
{ {
struct stat st; struct stat st;
@@ -215,7 +227,7 @@ string readFile(int fd)
struct stat st; struct stat st;
if (fstat(fd, &st) == -1) if (fstat(fd, &st) == -1)
throw SysError("statting file"); throw SysError("statting file");
unsigned char * buf = new unsigned char[st.st_size]; unsigned char * buf = new unsigned char[st.st_size];
AutoDeleteArray<unsigned char> d(buf); AutoDeleteArray<unsigned char> d(buf);
readFull(fd, buf, st.st_size); readFull(fd, buf, st.st_size);
@@ -280,9 +292,9 @@ static void _computePathSize(const Path & path,
blocks += st.st_blocks; blocks += st.st_blocks;
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path); Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i) for (Strings::iterator i = names.begin(); i != names.end(); ++i)
_computePathSize(path + "/" + *i, bytes, blocks); _computePathSize(path + "/" + *i, bytes, blocks);
} }
} }
@@ -305,21 +317,19 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
struct stat st = lstat(path); struct stat st = lstat(path);
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
bytesFreed += st.st_blocks * 512; bytesFreed += st.st_blocks * 512;
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path); Strings names = readDirectory(path);
/* Make the directory writable. */ /* Make the directory writable. */
if (!(st.st_mode & S_IWUSR)) { if (!(st.st_mode & S_IWUSR)) {
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % path); throw SysError(format("making `%1%' writable") % path);
} }
for (Strings::iterator i = names.begin(); i != names.end(); ++i) for (Strings::iterator i = names.begin(); i != names.end(); ++i)
_deletePath(path + "/" + *i, bytesFreed); _deletePath(path + "/" + *i, bytesFreed);
} }
@@ -351,14 +361,14 @@ void makePathReadOnly(const Path & path)
struct stat st = lstat(path); struct stat st = lstat(path);
if (!S_ISLNK(st.st_mode) && (st.st_mode & S_IWUSR)) { if (!S_ISLNK(st.st_mode) && (st.st_mode & S_IWUSR)) {
if (chmod(path.c_str(), st.st_mode & ~S_IWUSR) == -1) if (chmod(path.c_str(), st.st_mode & ~S_IWUSR) == -1)
throw SysError(format("making `%1%' read-only") % path); throw SysError(format("making `%1%' read-only") % path);
} }
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path); Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i) for (Strings::iterator i = names.begin(); i != names.end(); ++i)
makePathReadOnly(path + "/" + *i); makePathReadOnly(path + "/" + *i);
} }
} }
@@ -380,25 +390,25 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
static int globalCounter = 0; static int globalCounter = 0;
int localCounter = 0; int localCounter = 0;
int & counter(useGlobalCounter ? globalCounter : localCounter); int & counter(useGlobalCounter ? globalCounter : localCounter);
while (1) { while (1) {
checkInterrupt(); checkInterrupt();
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter); Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
if (mkdir(tmpDir.c_str(), mode) == 0) { if (mkdir(tmpDir.c_str(), mode) == 0) {
/* Explicitly set the group of the directory. This is to /* Explicitly set the group of the directory. This is to
work around around problems caused by BSD's group work around around problems caused by BSD's group
ownership semantics (directories inherit the group of ownership semantics (directories inherit the group of
the parent). For instance, the group of /tmp on the parent). For instance, the group of /tmp on
FreeBSD is "wheel", so all directories created in /tmp FreeBSD is "wheel", so all directories created in /tmp
will be owned by "wheel"; but if the user is not in will be owned by "wheel"; but if the user is not in
"wheel", then "tar" will fail to unpack archives that "wheel", then "tar" will fail to unpack archives that
have the setgid bit set on directories. */ have the setgid bit set on directories. */
if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0) if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
throw SysError(format("setting group of directory `%1%'") % tmpDir); throw SysError(format("setting group of directory `%1%'") % tmpDir);
return tmpDir; return tmpDir;
} }
if (errno != EEXIST) if (errno != EEXIST)
throw SysError(format("creating directory `%1%'") % tmpDir); throw SysError(format("creating directory `%1%'") % tmpDir);
} }
} }
@@ -418,7 +428,7 @@ Paths createDirs(const Path & path)
} }
if (!S_ISDIR(st.st_mode)) throw Error(format("`%1%' is not a directory") % path); if (!S_ISDIR(st.st_mode)) throw Error(format("`%1%' is not a directory") % path);
return created; return created;
} }
@@ -619,8 +629,8 @@ AutoCloseFD::AutoCloseFD(int fd)
AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd) AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd)
{ {
/* Copying a AutoCloseFD isn't allowed (who should get to close /* Copying an AutoCloseFD isn't allowed (who should get to close
it?). But as a edge case, allow copying of closed it?). But as an edge case, allow copying of closed
AutoCloseFDs. This is necessary due to tiresome reasons AutoCloseFDs. This is necessary due to tiresome reasons
involving copy constructor use on default object values in STL involving copy constructor use on default object values in STL
containers (like when you do `map[value]' where value isn't in containers (like when you do `map[value]' where value isn't in
@@ -727,8 +737,8 @@ AutoCloseDir::operator DIR *()
void AutoCloseDir::close() void AutoCloseDir::close()
{ {
if (dir) { if (dir) {
closedir(dir); closedir(dir);
dir = 0; dir = 0;
} }
} }
@@ -793,6 +803,7 @@ void Pid::kill()
int Pid::wait(bool block) int Pid::wait(bool block)
{ {
assert(pid != -1);
while (1) { while (1) {
int status; int status;
int res = waitpid(pid, &status, block ? 0 : WNOHANG); int res = waitpid(pid, &status, block ? 0 : WNOHANG);
@@ -843,12 +854,21 @@ void killUser(uid_t uid)
if (setuid(uid) == -1) if (setuid(uid) == -1)
throw SysError("setting uid"); throw SysError("setting uid");
while (true) { while (true) {
if (kill(-1, SIGKILL) == 0) break; #ifdef __APPLE__
if (errno == ESRCH) break; /* no more processes */ /* OSX's kill syscall takes a third parameter that, among other
if (errno != EINTR) things, determines if kill(-1, signo) affects the calling
throw SysError(format("cannot kill processes for uid `%1%'") % uid); process. In the OSX libc, it's set to true, which means
} "follow POSIX", which we don't want here
*/
if (syscall(SYS_kill, -1, SIGKILL, false) == 0) break;
#else
if (kill(-1, SIGKILL) == 0) break;
#endif
if (errno == ESRCH) break; /* no more processes */
if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
} catch (std::exception & e) { } catch (std::exception & e) {
writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str()); writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str());
@@ -856,7 +876,7 @@ void killUser(uid_t uid)
} }
_exit(0); _exit(0);
} }
/* parent */ /* parent */
int status = pid.wait(true); int status = pid.wait(true);
if (status != 0) if (status != 0)
@@ -1043,14 +1063,14 @@ string statusToString(int status)
if (WIFEXITED(status)) if (WIFEXITED(status))
return (format("failed with exit code %1%") % WEXITSTATUS(status)).str(); return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
else if (WIFSIGNALED(status)) { else if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status); int sig = WTERMSIG(status);
#if HAVE_STRSIGNAL #if HAVE_STRSIGNAL
const char * description = strsignal(sig); const char * description = strsignal(sig);
return (format("failed due to signal %1% (%2%)") % sig % description).str(); return (format("failed due to signal %1% (%2%)") % sig % description).str();
#else #else
return (format("failed due to signal %1%") % sig).str(); return (format("failed due to signal %1%") % sig).str();
#endif #endif
} }
else else
return "died abnormally"; return "died abnormally";
} else return "succeeded"; } else return "succeeded";
@@ -1063,14 +1083,6 @@ bool statusOk(int status)
} }
string int2String(int n)
{
std::ostringstream str;
str << n;
return str.str();
}
bool hasSuffix(const string & s, const string & suffix) bool hasSuffix(const string & s, const string & suffix)
{ {
return s.size() >= suffix.size() && string(s, s.size() - suffix.size()) == suffix; return s.size() >= suffix.size() && string(s, s.size() - suffix.size()) == suffix;
+12 -3
View File
@@ -45,6 +45,10 @@ Path dirOf(const Path & path);
following the final `/'. */ following the final `/'. */
string baseNameOf(const Path & path); string baseNameOf(const Path & path);
/* Check whether a given path is a descendant of the given
directory. */
bool isInDir(const Path & path, const Path & dir);
/* Get status of `path'. */ /* Get status of `path'. */
struct stat lstat(const Path & path); struct stat lstat(const Path & path);
@@ -174,7 +178,7 @@ struct AutoDeleteArray
{ {
T * p; T * p;
AutoDeleteArray(T * p) : p(p) { } AutoDeleteArray(T * p) : p(p) { }
~AutoDeleteArray() ~AutoDeleteArray()
{ {
delete [] p; delete [] p;
} }
@@ -185,7 +189,7 @@ class AutoDelete
{ {
Path path; Path path;
bool del; bool del;
bool recursive; bool recursive;
public: public:
AutoDelete(const Path & p, bool recursive = true); AutoDelete(const Path & p, bool recursive = true);
~AutoDelete(); ~AutoDelete();
@@ -315,7 +319,12 @@ template<class N> bool string2Int(const string & s, N & n)
return str && str.get() == EOF; return str && str.get() == EOF;
} }
string int2String(int n); template<class N> string int2String(N n)
{
std::ostringstream str;
str << n;
return str.str();
}
/* Return true iff `s' ends in `suffix'. */ /* Return true iff `s' ends in `suffix'. */
+31 -12
View File
@@ -4,6 +4,7 @@
#include "serialise.hh" #include "serialise.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include "archive.hh" #include "archive.hh"
#include "affinity.hh"
#include "globals.hh" #include "globals.hh"
#include <cstring> #include <cstring>
@@ -25,8 +26,13 @@ using namespace nix;
that lack it, we only notice the disconnection the next time we try that lack it, we only notice the disconnection the next time we try
to write to the client. So if you have a builder that never to write to the client. So if you have a builder that never
generates output on stdout/stderr, the daemon will never notice generates output on stdout/stderr, the daemon will never notice
that the client has disconnected until the builder terminates. */ that the client has disconnected until the builder terminates.
#ifdef O_ASYNC
GNU/Hurd does have O_ASYNC, but its Unix-domain socket translator
(pflocal) does not implement F_SETOWN. See
<http://lists.gnu.org/archive/html/bug-guix/2013-07/msg00021.html> for
details.*/
#if defined O_ASYNC && !defined __GNU__
#define HAVE_HUP_NOTIFICATION #define HAVE_HUP_NOTIFICATION
#ifndef SIGPOLL #ifndef SIGPOLL
#define SIGPOLL SIGIO #define SIGPOLL SIGIO
@@ -273,7 +279,7 @@ struct SavingSourceAdapter : Source
}; };
static void performOp(unsigned int clientVersion, static void performOp(bool trusted, unsigned int clientVersion,
Source & from, Sink & to, unsigned int op) Source & from, Sink & to, unsigned int op)
{ {
switch (op) { switch (op) {
@@ -334,6 +340,7 @@ static void performOp(unsigned int clientVersion,
case wopQueryReferences: case wopQueryReferences:
case wopQueryReferrers: case wopQueryReferrers:
case wopQueryValidDerivers:
case wopQueryDerivationOutputs: { case wopQueryDerivationOutputs: {
Path path = readStorePath(from); Path path = readStorePath(from);
startWork(); startWork();
@@ -342,6 +349,8 @@ static void performOp(unsigned int clientVersion,
store->queryReferences(path, paths); store->queryReferences(path, paths);
else if (op == wopQueryReferrers) else if (op == wopQueryReferrers)
store->queryReferrers(path, paths); store->queryReferrers(path, paths);
else if (op == wopQueryValidDerivers)
paths = store->queryValidDerivers(path);
else paths = store->queryDerivationOutputs(path); else paths = store->queryDerivationOutputs(path);
stopWork(); stopWork();
writeStrings(paths, to); writeStrings(paths, to);
@@ -548,7 +557,10 @@ static void performOp(unsigned int clientVersion,
for (unsigned int i = 0; i < n; i++) { for (unsigned int i = 0; i < n; i++) {
string name = readString(from); string name = readString(from);
string value = readString(from); string value = readString(from);
settings.set("untrusted-" + name, value); if (name == "build-timeout")
string2Int(value, settings.buildTimeout);
else
settings.set(trusted ? name : "untrusted-" + name, value);
} }
} }
startWork(); startWork();
@@ -637,7 +649,7 @@ static void performOp(unsigned int clientVersion,
} }
static void processConnection() static void processConnection(bool trusted)
{ {
canSendStderr = false; canSendStderr = false;
myPid = getpid(); myPid = getpid();
@@ -660,6 +672,9 @@ static void processConnection()
to.flush(); to.flush();
unsigned int clientVersion = readInt(from); unsigned int clientVersion = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
setAffinityTo(readInt(from));
bool reserveSpace = true; bool reserveSpace = true;
if (GET_PROTOCOL_MINOR(clientVersion) >= 11) if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
reserveSpace = readInt(from) != 0; reserveSpace = readInt(from) != 0;
@@ -705,9 +720,9 @@ static void processConnection()
opCount++; opCount++;
try { try {
performOp(clientVersion, from, to, op); performOp(trusted, clientVersion, from, to, op);
} catch (Error & e) { } 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 something went wrong processing the input of the
client. This can happen especially if I/O errors occur client. This can happen especially if I/O errors occur
during addTextToStore() / importPath(). If that during addTextToStore() / importPath(). If that
@@ -716,6 +731,10 @@ static void processConnection()
if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg()); if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0); stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
if (!errorAllowed) break; 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(); to.flush();
@@ -771,7 +790,7 @@ static void daemonLoop()
if (fdSocket == -1) if (fdSocket == -1)
throw SysError("cannot create Unix domain socket"); throw SysError("cannot create Unix domain socket");
string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH; string socketPath = settings.nixDaemonSocketFile;
createDirs(dirOf(socketPath)); createDirs(dirOf(socketPath));
@@ -833,6 +852,7 @@ static void daemonLoop()
/* Get the identity of the caller, if possible. */ /* Get the identity of the caller, if possible. */
uid_t clientUid = -1; uid_t clientUid = -1;
pid_t clientPid = -1; pid_t clientPid = -1;
bool trusted = false;
#if defined(SO_PEERCRED) #if defined(SO_PEERCRED)
ucred cred; ucred cred;
@@ -840,6 +860,7 @@ static void daemonLoop()
if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) != -1) { if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) != -1) {
clientPid = cred.pid; clientPid = cred.pid;
clientUid = cred.uid; clientUid = cred.uid;
if (clientUid == 0) trusted = true;
} }
#endif #endif
@@ -873,10 +894,10 @@ static void daemonLoop()
/* Handle the connection. */ /* Handle the connection. */
from.fd = remote; from.fd = remote;
to.fd = remote; to.fd = remote;
processConnection(); processConnection(trusted);
} catch (std::exception & e) { } catch (std::exception & e) {
writeToStderr("child error: " + string(e.what()) + "\n"); writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
} }
exit(0); exit(0);
} }
@@ -892,8 +913,6 @@ static void daemonLoop()
void run(Strings args) void run(Strings args)
{ {
bool daemon = false;
for (Strings::iterator i = args.begin(); i != args.end(); ) { for (Strings::iterator i = args.begin(); i != args.end(); ) {
string arg = *i++; string arg = *i++;
if (arg == "--daemon") /* ignored for backwards compatibility */; if (arg == "--daemon") /* ignored for backwards compatibility */;
+83 -60
View File
@@ -54,6 +54,7 @@ struct Globals
EvalState state; EvalState state;
bool dryRun; bool dryRun;
bool preserveInstalled; bool preserveInstalled;
bool removeAll;
string forceName; string forceName;
bool prebuiltOnly; bool prebuiltOnly;
}; };
@@ -94,18 +95,14 @@ static bool parseInstallSourceOptions(Globals & globals,
} }
static bool isNixExpr(const Path & path) static bool isNixExpr(const Path & path, struct stat & st)
{ {
struct stat st; return S_ISREG(st.st_mode) || (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix"));
if (stat(path.c_str(), &st) == -1)
throw SysError(format("getting information about `%1%'") % path);
return !S_ISDIR(st.st_mode) || pathExists(path + "/default.nix");
} }
static void getAllExprs(EvalState & state, static void getAllExprs(EvalState & state,
const Path & path, ExprAttrs & attrs) const Path & path, StringSet & attrs, Value & v)
{ {
Strings names = readDirectory(path); Strings names = readDirectory(path);
StringSet namesSorted(names.begin(), names.end()); StringSet namesSorted(names.begin(), names.end());
@@ -122,7 +119,7 @@ static void getAllExprs(EvalState & state,
if (stat(path2.c_str(), &st) == -1) if (stat(path2.c_str(), &st) == -1)
continue; // ignore dangling symlinks in ~/.nix-defexpr continue; // ignore dangling symlinks in ~/.nix-defexpr
if (isNixExpr(path2)) { if (isNixExpr(path2, st) && (!S_ISREG(st.st_mode) || hasSuffix(path2, ".nix"))) {
/* Strip off the `.nix' filename suffix (if applicable), /* Strip off the `.nix' filename suffix (if applicable),
otherwise the attribute cannot be selected with the otherwise the attribute cannot be selected with the
`-A' option. Useful if you want to stick a Nix `-A' option. Useful if you want to stick a Nix
@@ -130,32 +127,50 @@ static void getAllExprs(EvalState & state,
string attrName = *i; string attrName = *i;
if (hasSuffix(attrName, ".nix")) if (hasSuffix(attrName, ".nix"))
attrName = string(attrName, 0, attrName.size() - 4); attrName = string(attrName, 0, attrName.size() - 4);
attrs.attrs[state.symbols.create(attrName)] = if (attrs.find(attrName) != attrs.end()) {
ExprAttrs::AttrDef(state.parseExprFromFile(absPath(path2)), noPos); printMsg(lvlError, format("warning: name collision in input Nix expressions, skipping `%1%'") % path2);
continue;
}
attrs.insert(attrName);
/* Load the expression on demand. */
Value & vFun(*state.allocValue());
Value & vArg(*state.allocValue());
state.getBuiltin("import", vFun);
mkString(vArg, path2);
mkApp(*state.allocAttr(v, state.symbols.create(attrName)), vFun, vArg);
} }
else else if (S_ISDIR(st.st_mode))
/* `path2' is a directory (with no default.nix in it); /* `path2' is a directory (with no default.nix in it);
recurse into it. */ recurse into it. */
getAllExprs(state, path2, attrs); getAllExprs(state, path2, attrs, v);
} }
} }
static Expr * loadSourceExpr(EvalState & state, const Path & path) static void loadSourceExpr(EvalState & state, const Path & path, Value & v)
{ {
if (isNixExpr(path)) return state.parseExprFromFile(absPath(path)); struct stat st;
if (stat(path.c_str(), &st) == -1)
throw SysError(format("getting information about `%1%'") % path);
if (isNixExpr(path, st)) {
state.evalFile(path, v);
return;
}
/* The path is a directory. Put the Nix expressions in the /* The path is a directory. Put the Nix expressions in the
directory in an attribute set, with the file name of each directory in a set, with the file name of each expression as
expression as the attribute name. Recurse into subdirectories the attribute name. Recurse into subdirectories (but keep the
(but keep the attribute set flat, not nested, to make it easier set flat, not nested, to make it easier for a user to have a
for a user to have a ~/.nix-defexpr directory that includes ~/.nix-defexpr directory that includes some system-wide
some system-wide directory). */ directory). */
ExprAttrs * attrs = new ExprAttrs; if (S_ISDIR(st.st_mode)) {
attrs->attrs[state.symbols.create("_combineChannels")] = state.mkAttrs(v, 16);
ExprAttrs::AttrDef(new ExprList(), noPos); state.mkList(*state.allocAttr(v, state.symbols.create("_combineChannels")), 0);
getAllExprs(state, path, *attrs); StringSet attrs;
return attrs; getAllExprs(state, path, attrs, v);
v.attrs->sort();
}
} }
@@ -163,8 +178,10 @@ static void loadDerivations(EvalState & state, Path nixExprPath,
string systemFilter, Bindings & autoArgs, string systemFilter, Bindings & autoArgs,
const string & pathPrefix, DrvInfos & elems) const string & pathPrefix, DrvInfos & elems)
{ {
Value v; Value vRoot;
findAlongAttrPath(state, pathPrefix, autoArgs, loadSourceExpr(state, nixExprPath), v); loadSourceExpr(state, nixExprPath, vRoot);
Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot));
getDerivations(state, v, pathPrefix, autoArgs, elems, true); getDerivations(state, v, pathPrefix, autoArgs, elems, true);
@@ -227,13 +244,13 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
const Strings & args, bool newestOnly) const Strings & args, bool newestOnly)
{ {
DrvNames selectors = drvNamesFromArgs(args); DrvNames selectors = drvNamesFromArgs(args);
if (selectors.empty())
selectors.push_back(DrvName("*"));
DrvInfos elems; DrvInfos elems;
set<unsigned int> done; set<unsigned int> done;
for (DrvNames::iterator i = selectors.begin(); foreach (DrvNames::iterator, i, selectors) {
i != selectors.end(); ++i)
{
typedef list<std::pair<DrvInfo, unsigned int> > Matches; typedef list<std::pair<DrvInfo, unsigned int> > Matches;
Matches matches; Matches matches;
unsigned int n = 0; unsigned int n = 0;
@@ -305,8 +322,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
} }
/* Check that all selectors have been used. */ /* Check that all selectors have been used. */
for (DrvNames::iterator i = selectors.begin(); foreach (DrvNames::iterator, i, selectors)
i != selectors.end(); ++i)
if (i->hits == 0 && i->fullName != "*") if (i->hits == 0 && i->fullName != "*")
throw Error(format("selector `%1%' matches no derivations") throw Error(format("selector `%1%' matches no derivations")
% i->fullName); % i->fullName);
@@ -356,13 +372,15 @@ static void queryInstSources(EvalState & state,
(import ./foo.nix)' = `(import ./foo.nix).bar'. */ (import ./foo.nix)' = `(import ./foo.nix).bar'. */
case srcNixExprs: { case srcNixExprs: {
Expr * e1 = loadSourceExpr(state, instSource.nixExprPath); Value vArg;
loadSourceExpr(state, instSource.nixExprPath, vArg);
foreach (Strings::const_iterator, i, args) { foreach (Strings::const_iterator, i, args) {
Expr * e2 = state.parseExprFromString(*i, absPath(".")); Expr * eFun = state.parseExprFromString(*i, absPath("."));
Expr * call = new ExprApp(e2, e1); Value vFun, vTmp;
Value v; state.eval(call, v); state.eval(eFun, vFun);
getDerivations(state, v, "", instSource.autoArgs, elems, true); mkApp(vTmp, vFun, vArg);
getDerivations(state, vTmp, "", instSource.autoArgs, elems, true);
} }
break; break;
@@ -413,10 +431,10 @@ static void queryInstSources(EvalState & state,
} }
case srcAttrPath: { case srcAttrPath: {
Value vRoot;
loadSourceExpr(state, instSource.nixExprPath, vRoot);
foreach (Strings::const_iterator, i, args) { foreach (Strings::const_iterator, i, args) {
Value v; Value & v(*findAlongAttrPath(state, *i, instSource.autoArgs, vRoot));
findAlongAttrPath(state, *i, instSource.autoArgs,
loadSourceExpr(state, instSource.nixExprPath), v);
getDerivations(state, v, "", instSource.autoArgs, elems, true); getDerivations(state, v, "", instSource.autoArgs, elems, true);
} }
break; break;
@@ -472,29 +490,31 @@ static void installDerivations(Globals & globals,
newNames.insert(DrvName(i->name).name); newNames.insert(DrvName(i->name).name);
} }
/* Add in the already installed derivations, unless they have the
same name as a to-be-installed element. */
while (true) { while (true) {
string lockToken = optimisticLockProfile(profile); string lockToken = optimisticLockProfile(profile);
DrvInfos installedElems = queryInstalled(globals.state, profile);
DrvInfos allElems(newElems); DrvInfos allElems(newElems);
foreach (DrvInfos::iterator, i, installedElems) {
DrvName drvName(i->name);
MetaInfo meta = i->queryMetaInfo(globals.state);
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
!keep(meta))
printMsg(lvlInfo,
format("replacing old `%1%'") % i->name);
else
allElems.push_back(*i);
}
foreach (DrvInfos::iterator, i, newElems) /* Add in the already installed derivations, unless they have
printMsg(lvlInfo, format("installing `%1%'") % i->name); the same name as a to-be-installed element. */
if (!globals.removeAll) {
DrvInfos installedElems = queryInstalled(globals.state, profile);
foreach (DrvInfos::iterator, i, installedElems) {
DrvName drvName(i->name);
MetaInfo meta = i->queryMetaInfo(globals.state);
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
!keep(meta))
printMsg(lvlInfo, format("replacing old `%1%'") % i->name);
else
allElems.push_back(*i);
}
foreach (DrvInfos::iterator, i, newElems)
printMsg(lvlInfo, format("installing `%1%'") % i->name);
}
printMissing(globals.state, newElems); printMissing(globals.state, newElems);
@@ -514,6 +534,8 @@ static void opInstall(Globals & globals,
if (parseInstallSourceOptions(globals, i, opFlags, arg)) ; if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
else if (arg == "--preserve-installed" || arg == "-P") else if (arg == "--preserve-installed" || arg == "-P")
globals.preserveInstalled = true; globals.preserveInstalled = true;
else if (arg == "--remove-all" || arg == "-r")
globals.removeAll = true;
else throw UsageError(format("unknown flag `%1%'") % arg); else throw UsageError(format("unknown flag `%1%'") % arg);
} }
@@ -804,7 +826,7 @@ void printTable(Table & table)
} }
/* This function compares the version of a element against the /* This function compares the version of an element against the
versions in the given set of elements. `cvLess' means that only versions in the given set of elements. `cvLess' means that only
lower versions are in the set, `cvEqual' means that at most an lower versions are in the set, `cvEqual' means that at most an
equal version is in the set, and `cvGreater' means that there is at equal version is in the set, and `cvGreater' means that there is at
@@ -895,9 +917,6 @@ static void opQuery(Globals & globals,
else remaining.push_back(arg); else remaining.push_back(arg);
} }
if (remaining.size() == 0)
printMsg(lvlInfo, "warning: you probably meant to specify the argument '*' to show all packages");
/* Obtain derivation information from the specified source. */ /* Obtain derivation information from the specified source. */
DrvInfos availElems, installedElems; DrvInfos availElems, installedElems;
@@ -1284,6 +1303,7 @@ void run(Strings args)
globals.dryRun = false; globals.dryRun = false;
globals.preserveInstalled = false; globals.preserveInstalled = false;
globals.removeAll = false;
globals.prebuiltOnly = false; globals.prebuiltOnly = false;
for (Strings::iterator i = args.begin(); i != args.end(); ) { for (Strings::iterator i = args.begin(); i != args.end(); ) {
@@ -1350,6 +1370,9 @@ void run(Strings args)
if (!op) throw UsageError("no operation specified"); if (!op) throw UsageError("no operation specified");
if (globals.profile == "")
globals.profile = getEnv("NIX_PROFILE", "");
if (globals.profile == "") { if (globals.profile == "") {
Path profileLink = getHomeDir() + "/.nix-profile"; Path profileLink = getHomeDir() + "/.nix-profile";
globals.profile = pathExists(profileLink) globals.profile = pathExists(profileLink)
+35 -31
View File
@@ -39,44 +39,48 @@ void processExpr(EvalState & state, const Strings & attrPaths,
bool parseOnly, bool strict, Bindings & autoArgs, bool parseOnly, bool strict, Bindings & autoArgs,
bool evalOnly, bool xmlOutput, bool location, Expr * e) bool evalOnly, bool xmlOutput, bool location, Expr * e)
{ {
if (parseOnly) if (parseOnly) {
std::cout << format("%1%\n") % *e; std::cout << format("%1%\n") % *e;
else return;
foreach (Strings::const_iterator, i, attrPaths) { }
Value v;
findAlongAttrPath(state, *i, autoArgs, e, v);
state.forceValue(v);
PathSet context; Value vRoot;
if (evalOnly) state.eval(e, vRoot);
if (xmlOutput)
printValueAsXML(state, strict, location, v, std::cout, context); foreach (Strings::const_iterator, i, attrPaths) {
else { Value & v(*findAlongAttrPath(state, *i, autoArgs, vRoot));
if (strict) state.strictForceValue(v); state.forceValue(v);
std::cout << v << std::endl;
} PathSet context;
if (evalOnly)
if (xmlOutput)
printValueAsXML(state, strict, location, v, std::cout, context);
else { else {
DrvInfos drvs; if (strict) state.strictForceValue(v);
getDerivations(state, v, "", autoArgs, drvs, false); std::cout << v << std::endl;
foreach (DrvInfos::iterator, i, drvs) { }
Path drvPath = i->queryDrvPath(state); else {
DrvInfos drvs;
getDerivations(state, v, "", autoArgs, drvs, false);
foreach (DrvInfos::iterator, i, drvs) {
Path drvPath = i->queryDrvPath(state);
/* What output do we want? */ /* What output do we want? */
string outputName = i->queryOutputName(state); string outputName = i->queryOutputName(state);
if (outputName == "") if (outputName == "")
throw Error(format("derivation `%1%' lacks an `outputName' attribute ") % drvPath); throw Error(format("derivation `%1%' lacks an `outputName' attribute ") % drvPath);
if (gcRoot == "") if (gcRoot == "")
printGCWarning(); printGCWarning();
else { else {
Path rootName = gcRoot; Path rootName = gcRoot;
if (++rootNr > 1) rootName += "-" + int2String(rootNr); if (++rootNr > 1) rootName += "-" + int2String(rootNr);
drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot); drvPath = addPermRoot(*store, drvPath, rootName, indirectRoot);
}
std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : "");
} }
std::cout << format("%1%%2%\n") % drvPath % (outputName != "out" ? "!" + outputName : "");
} }
} }
}
} }
@@ -160,7 +164,7 @@ void run(Strings args)
files.push_back("./default.nix"); files.push_back("./default.nix");
foreach (Strings::iterator, i, files) { foreach (Strings::iterator, i, files) {
Expr * e = state.parseExprFromFile(lookupFileArg(state, *i)); Expr * e = state.parseExprFromFile(resolveExprPath(lookupFileArg(state, *i)));
processExpr(state, attrPaths, parseOnly, strict, autoArgs, processExpr(state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, xmlOutput, xmlOutputSourceLocation, e); evalOnly, xmlOutput, xmlOutputSourceLocation, e);
} }
+44 -40
View File
@@ -238,12 +238,6 @@ static void printTree(const Path & path,
PathSet references; PathSet references;
store->queryReferences(path, references); store->queryReferences(path, references);
#if 0
for (PathSet::iterator i = drv.inputSrcs.begin();
i != drv.inputSrcs.end(); ++i)
cout << format("%1%%2%\n") % (tailPad + treeConn) % *i;
#endif
/* Topologically sort under the relation A < B iff A \in /* Topologically sort under the relation A < B iff A \in
closure(B). That is, if derivation A is an (possibly indirect) closure(B). That is, if derivation A is an (possibly indirect)
input of B, then A is printed first. This has the effect of input of B, then A is printed first. This has the effect of
@@ -251,7 +245,7 @@ static void printTree(const Path & path,
Paths sorted = topoSortPaths(*store, references); Paths sorted = topoSortPaths(*store, references);
reverse(sorted.begin(), sorted.end()); reverse(sorted.begin(), sorted.end());
for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i) { foreach (Paths::iterator, i, sorted) {
Paths::iterator j = i; ++j; Paths::iterator j = i; ++j;
printTree(*i, tailPad + treeConn, printTree(*i, tailPad + treeConn,
j == sorted.end() ? tailPad + treeNull : tailPad + treeLine, j == sorted.end() ? tailPad + treeNull : tailPad + treeLine,
@@ -293,7 +287,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
else if (*i == "--resolve") query = qResolve; else if (*i == "--resolve") query = qResolve;
else if (*i == "--roots") query = qRoots; else if (*i == "--roots") query = qRoots;
else if (*i == "--use-output" || *i == "-u") useOutput = true; else if (*i == "--use-output" || *i == "-u") useOutput = true;
else if (*i == "--force-realise" || *i == "-f") forceRealise = true; else if (*i == "--force-realise" || *i == "--force-realize" || *i == "-f") forceRealise = true;
else if (*i == "--include-outputs") includeOutputs = true; else if (*i == "--include-outputs") includeOutputs = true;
else throw UsageError(format("unknown flag `%1%'") % *i); else throw UsageError(format("unknown flag `%1%'") % *i);
@@ -404,7 +398,8 @@ static void opQuery(Strings opFlags, Strings opArgs)
foreach (Strings::iterator, i, opArgs) { foreach (Strings::iterator, i, opArgs) {
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise); PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
foreach (PathSet::iterator, j, paths) foreach (PathSet::iterator, j, paths)
computeFSClosure(*store, *j, referrers, true); computeFSClosure(*store, *j, referrers, true,
settings.gcKeepOutputs, settings.gcKeepDerivations);
} }
Roots roots = store->findRoots(); Roots roots = store->findRoots();
foreach (Roots::iterator, i, roots) foreach (Roots::iterator, i, roots)
@@ -459,35 +454,42 @@ static void opReadLog(Strings opFlags, Strings opArgs)
foreach (Strings::iterator, i, opArgs) { foreach (Strings::iterator, i, opArgs) {
Path path = useDeriver(followLinksToStorePath(*i)); Path path = useDeriver(followLinksToStorePath(*i));
Path logPath = (format("%1%/%2%/%3%") % for (int j = 0; j <= 2; j++) {
settings.nixLogDir % drvsLogDir % baseNameOf(path)).str(); if (j == 2) throw Error(format("build log of derivation `%1%' is not available") % path);
Path logBz2Path = logPath + ".bz2";
if (pathExists(logPath)) { string baseName = baseNameOf(path);
/* !!! Make this run in O(1) memory. */ Path logPath =
string log = readFile(logPath); j == 0
writeFull(STDOUT_FILENO, (const unsigned char *) log.data(), log.size()); ? (format("%1%/%2%/%3%/%4%") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2) % string(baseName, 2)).str()
: (format("%1%/%2%/%3%") % settings.nixLogDir % drvsLogDir % baseName).str();
Path logBz2Path = logPath + ".bz2";
if (pathExists(logPath)) {
/* !!! Make this run in O(1) memory. */
string log = readFile(logPath);
writeFull(STDOUT_FILENO, (const unsigned char *) log.data(), log.size());
break;
}
else if (pathExists(logBz2Path)) {
AutoCloseFD fd = open(logBz2Path.c_str(), O_RDONLY);
FILE * f = 0;
if (fd == -1 || (f = fdopen(fd.borrow(), "r")) == 0)
throw SysError(format("opening file `%1%'") % logBz2Path);
int err;
BZFILE * bz = BZ2_bzReadOpen(&err, f, 0, 0, 0, 0);
if (!bz) throw Error(format("cannot open bzip2 file `%1%'") % logBz2Path);
unsigned char buf[128 * 1024];
do {
int n = BZ2_bzRead(&err, bz, buf, sizeof(buf));
if (err != BZ_OK && err != BZ_STREAM_END)
throw Error(format("error reading bzip2 file `%1%'") % logBz2Path);
writeFull(STDOUT_FILENO, buf, n);
} while (err != BZ_STREAM_END);
BZ2_bzReadClose(&err, bz);
break;
}
} }
else if (pathExists(logBz2Path)) {
AutoCloseFD fd = open(logBz2Path.c_str(), O_RDONLY);
FILE * f = 0;
if (fd == -1 || (f = fdopen(fd.borrow(), "r")) == 0)
throw SysError(format("opening file `%1%'") % logBz2Path);
int err;
BZFILE * bz = BZ2_bzReadOpen(&err, f, 0, 0, 0, 0);
if (!bz) throw Error(format("cannot open bzip2 file `%1%'") % logBz2Path);
unsigned char buf[128 * 1024];
do {
int n = BZ2_bzRead(&err, bz, buf, sizeof(buf));
if (err != BZ_OK && err != BZ_STREAM_END)
throw Error(format("error reading bzip2 file `%1%'") % logBz2Path);
writeFull(STDOUT_FILENO, buf, n);
} while (err != BZ_STREAM_END);
BZ2_bzReadClose(&err, bz);
}
else throw Error(format("build log of derivation `%1%' is not available") % path);
} }
} }
@@ -513,7 +515,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
if (!store->isValidPath(info.path) || reregister) { if (!store->isValidPath(info.path) || reregister) {
/* !!! races */ /* !!! races */
if (canonicalise) if (canonicalise)
canonicalisePathMetaData(info.path); canonicalisePathMetaData(info.path, -1);
if (!hashGiven) { if (!hashGiven) {
HashResult hash = hashPath(htSHA256, info.path); HashResult hash = hashPath(htSHA256, info.path);
info.hash = hash.first; info.hash = hash.first;
@@ -692,7 +694,9 @@ static void opExport(Strings opFlags, Strings opArgs)
else throw UsageError(format("unknown flag `%1%'") % *i); else throw UsageError(format("unknown flag `%1%'") % *i);
FdSink sink(STDOUT_FILENO); FdSink sink(STDOUT_FILENO);
exportPaths(*store, opArgs, sign, sink); Paths sorted = topoSortPaths(*store, PathSet(opArgs.begin(), opArgs.end()));
reverse(sorted.begin(), sorted.end());
exportPaths(*store, sorted, sign, sink);
} }
@@ -841,7 +845,7 @@ void run(Strings args)
Operation oldOp = op; Operation oldOp = op;
if (arg == "--realise" || arg == "-r") if (arg == "--realise" || arg == "--realize" || arg == "-r")
op = opRealise; op = opRealise;
else if (arg == "--add" || arg == "-A") else if (arg == "--add" || arg == "-A")
op = opAdd; op = opAdd;
@@ -883,7 +887,7 @@ void run(Strings args)
op = opVerifyPath; op = opVerifyPath;
else if (arg == "--repair-path") else if (arg == "--repair-path")
op = opRepairPath; op = opRepairPath;
else if (arg == "--optimise") else if (arg == "--optimise" || arg == "--optimize")
op = opOptimise; op = opOptimise;
else if (arg == "--query-failed-paths") else if (arg == "--query-failed-paths")
op = opQueryFailedPaths; op = opQueryFailedPaths;
+4
View File
@@ -7,6 +7,7 @@
-e "s^@bindir\@^$(bindir)^g" \ -e "s^@bindir\@^$(bindir)^g" \
-e "s^@datadir\@^$(datadir)^g" \ -e "s^@datadir\@^$(datadir)^g" \
-e "s^@sysconfdir\@^$(sysconfdir)^g" \ -e "s^@sysconfdir\@^$(sysconfdir)^g" \
-e "s^@profiledir\@^$(profiledir)^g" \
-e "s^@localstatedir\@^$(localstatedir)^g" \ -e "s^@localstatedir\@^$(localstatedir)^g" \
-e "s^@datadir\@^$(datadir)^g" \ -e "s^@datadir\@^$(datadir)^g" \
-e "s^@libdir\@^$(libdir)^g" \ -e "s^@libdir\@^$(libdir)^g" \
@@ -36,3 +37,6 @@
-e "s^@testPath\@^$(coreutils):$$(dirname $$(type -p expr))^g" \ -e "s^@testPath\@^$(coreutils):$$(dirname $$(type -p expr))^g" \
< $< > $@ || rm $@ < $< > $@ || rm $@
if test -x $<; then chmod +x $@; fi if test -x $<; then chmod +x $@; fi
$(CONFIG_HEADER):
true
+2
View File
@@ -14,6 +14,8 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
XFAIL_TESTS = XFAIL_TESTS =
profiledir = $(sysconfdir)/profile.d
include ../substitute.mk include ../substitute.mk
$(TESTS): common.sh config.nix $(TESTS): common.sh config.nix
+26 -1
View File
@@ -5,7 +5,7 @@ clearManifests
# Create the binary cache. # Create the binary cache.
cacheDir=$TEST_ROOT/binary-cache cacheDir=$TEST_ROOT/binary-cache
rm -rf $cacheDir rm -rf "$cacheDir"
outPath=$(nix-build dependencies.nix --no-out-link) outPath=$(nix-build dependencies.nix --no-out-link)
@@ -17,6 +17,8 @@ nix-push --dest $cacheDir $outPath
clearStore clearStore
rm -f $NIX_STATE_DIR/binary-cache* rm -f $NIX_STATE_DIR/binary-cache*
export _NIX_CACHE_FILE_URLS=1
nix-env --option binary-caches "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---" nix-env --option binary-caches "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---"
nix-store --option binary-caches "file://$cacheDir" -r $outPath nix-store --option binary-caches "file://$cacheDir" -r $outPath
@@ -37,3 +39,26 @@ nix-store --option binary-caches "file://$cacheDir" -r $outPath
nix-store --check-validity $outPath nix-store --check-validity $outPath
nix-store -qR $outPath | grep input-2 nix-store -qR $outPath | grep input-2
# Test whether fallback works if we have cached info but the
# corresponding NAR has disappeared.
clearStore
nix-build --option binary-caches "file://$cacheDir" dependencies.nix --dry-run # get info
mkdir $cacheDir/tmp
mv $cacheDir/*.nar* $cacheDir/tmp/
NIX_DEBUG_SUBST=1 nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result --fallback
mv $cacheDir/tmp/* $cacheDir/
# Test whether building works if the binary cache contains an
# incomplete closure.
clearStore
rm $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo)
nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
grep -q "Downloading" $TEST_ROOT/log
+2 -1
View File
@@ -17,5 +17,6 @@ in
mkDerivation { mkDerivation {
name = "build-hook"; name = "build-hook";
builder = ./dependencies.builder0.sh; builder = ./dependencies.builder0.sh;
inherit input1 input2; input1 = " " + input1 + "/.";
input2 = " ${input2}/.";
} }
+1 -1
View File
@@ -1,7 +1,7 @@
set -e set -e
datadir="@datadir@" datadir="@datadir@"
sysconfdir="@sysconfdir@" profiledir="@profiledir@"
export TEST_ROOT=$(pwd)/test-tmp export TEST_ROOT=$(pwd)/test-tmp
export NIX_STORE_DIR export NIX_STORE_DIR
+3
View File
@@ -1,3 +1,6 @@
[ "${input1: -2}" = /. ]
[ "${input2: -2}" = /. ]
mkdir $out mkdir $out
echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
+3 -3
View File
@@ -9,14 +9,14 @@ let {
input2 = mkDerivation { input2 = mkDerivation {
name = "dependencies-input-2"; name = "dependencies-input-2";
builder = ./dependencies.builder2.sh; builder = "${./dependencies.builder2.sh}";
}; };
body = mkDerivation { body = mkDerivation {
name = "dependencies"; name = "dependencies";
builder = ./dependencies.builder0.sh + "/FOOBAR/../."; builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
input1 = input1 + "/."; input1 = input1 + "/.";
inherit input2; input2 = "${input2}/.";
meta.description = "Random test package"; meta.description = "Random test package";
}; };
+1 -1
View File
@@ -40,7 +40,7 @@ for i in lang/eval-okay-*.nix; do
if test -e lang/$i.flags; then if test -e lang/$i.flags; then
flags=$(cat lang/$i.flags) flags=$(cat lang/$i.flags)
fi fi
if ! NIX_PATH=lang/dir3:lang/dir4:$NIX_PATH nix-instantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then if ! NIX_PATH=lang/dir3:lang/dir4_PATH nix-instantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
echo "FAIL: $i should evaluate" echo "FAIL: $i should evaluate"
fail=1 fail=1
elif ! diff lang/$i.out lang/$i.exp; then elif ! diff lang/$i.out lang/$i.exp; then
+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}"

Some files were not shown because too many files have changed in this diff Show More