Compare commits

..

146 Commits

Author SHA1 Message Date
Nicolas Pierron
6468f63994 Fix typo. 2009-07-08 09:44:22 +00:00
Nicolas Pierron
9416202465 Add a way to cache result for future evaluations. 2009-07-08 09:27:35 +00:00
Nicolas Pierron
4941ba3413 Add a session independent cache. 2009-07-08 09:26:15 +00:00
Nicolas Pierron
7244b9b861 Create a cache of reduce operations which are session independent. 2009-07-08 09:18:26 +00:00
Eelco Dolstra
c6890d6b5c * Replace newlines in table output with spaces. 2009-07-02 08:52:12 +00:00
Eelco Dolstra
749dd97a54 * Support integers and lists of strings in meta fields. This is
useful for fields like meta.maintainers, meta.priority (which can be
  a proper integer now) and even meta.license (if there are multiple
  licenses).
2009-06-30 15:53:39 +00:00
Eelco Dolstra
f2c3fc5191 * Don't show trace information by default (`--show-trace' to enable).
NixOS evaluation errors in particular look intimidating and
  generally aren't very useful.  Ideally the builtins.throw messages
  should be self-contained.
2009-06-30 13:28:29 +00:00
Eelco Dolstra
a2fc3a53ba * Highlight URLs containing "=" properly. 2009-06-18 10:04:14 +00:00
Eelco Dolstra
d53603c928 * Attributes names in attrsets can contain dots now. 2009-06-18 09:56:50 +00:00
Eelco Dolstra
14bc3ce3d6 * Canonicalise timestamps in the Nix store to 1 (1970-01-01 00:00:01
UTC) rather than 0 (00:00:00).  1 is a better choice because some
  programs use 0 as a special value.  For instance, the Template
  Toolkit uses a timestamp of 0 to denote the non-existence of a file,
  so it barfs on files in the Nix store (see
  template-toolkit-nix-store.patch in Nixpkgs).  Similarly, Maya 2008
  fails to load script directories with a timestamp of 0 and can't be
  patched because it's closed source.

  This will also shut up those "implausibly old time stamp" GNU tar
  warnings.
2009-06-13 16:30:58 +00:00
Eelco Dolstra
f24cf5d303 * nix-build: if nix-instantiate or nix-store fails due to a signal
(e.g. out of memory or a segfault), print an error message.
  Otherwise the user doesn't see anything.
2009-06-10 11:30:34 +00:00
Eelco Dolstra
c6cb792d48 2009-05-26 16:32:02 +00:00
Eelco Dolstra
a64bbe049e * Change the scoping of "inherit (e) ..." in recs so that the
attributes of the rec are in scope of `e'.  This is useful in
  expressions such as

    rec {
      lib = import ./lib;
      inherit (lib) concatStrings;
    }

  It does change the semantics of expressions such as

    let x = {y = 1;}; in rec { x = {y = 2;}; inherit (x) y; }.y

  This now returns 2 instead of 1.  However, no code in Nixpkgs or
  NixOS seems to rely on the old behaviour.
2009-05-15 13:46:13 +00:00
Eelco Dolstra
d407d572fd * Some syntactic sugar for attribute sets: allow {x.y.z = ...;} as a
shorthand for {x = {y = {z = ...;};};}.  This is especially useful
  for NixOS configuration files, e.g.

    {
      services = {
        sshd = {
          enable = true;
          port = 2022;
        };
      };
    }

  can now be written as

    {
      services.sshd.enable = true;
      services.sshd.port = 2022;
    }

  However, it is currently not permitted to write
  
    {
      services.sshd = {enable = true;};
      services.sshd.port = 2022;
    }

  as this is considered a duplicate definition of `services.sshd'.
2009-05-15 12:35:23 +00:00
Eelco Dolstra
e42975490f * Check for duplicate attributes in fixAttrs, rather than doing a
separate traversal after parsing.  Likewise, check for duplicate
  pattern variables right away.
2009-05-14 14:29:45 +00:00
Eelco Dolstra
50d11b90ca * Allow unsafe (unspecified) comparisons between attrsets unless
NIX_NO_UNSAFE_EQ is set, for now.
2009-05-12 11:06:24 +00:00
Eelco Dolstra
c34e6d71bc * Disallow equality tests between attribute sets. This was always
broken, but now the evaluator checks for it to prevent Nix
  expressions from relying on undefined behaviour.  Equality tests are
  implemented using a shallow pointer equality test between ATerms.
  However, because attribute sets are lazy and contain position
  information, this can give false positives.  For instance,
  previously

    let y = {x = 1;}; in y == y

  evaluated to true, while the equivalent expression

    {x = 1;} == {x = 1;}

  evaluated to false.  So disallow these tests for now.  (Eventually
  we may want to implement deep equality tests for attribute sets,
  like lib.eqStrict.)
  
* Idem: disallow comparisons between functions.

* Implemented deep comparisons of lists.  This had the same problem as
  attribute sets - the elements in the list weren't evaluated.  For
  instance,

    ["xy"] == [("x" + "y")]

  evaluated to false.  Now it works properly.
2009-05-11 15:50:14 +00:00
Eelco Dolstra
9536ba19d4 2009-05-07 13:22:26 +00:00
Eelco Dolstra
83bd320b39 * Build a PDF version of the manual in the tarball job (it's not
included in the tarball though).
2009-05-07 13:12:46 +00:00
Eelco Dolstra
f751c2966b * Don't have a bugs section in the manual. Bugs should be kept in the
bug tracker.
2009-05-07 13:11:58 +00:00
Eelco Dolstra
404996ca1f * Produce the manual in the tarball job. 2009-05-07 12:57:04 +00:00
Eelco Dolstra
7660e2a068 * Remove a right recursion that causes the parser to barf on very long
lists.  The comment about ATreverse requiring unbounded stack space
  was unfounded anyway.
2009-05-07 11:35:52 +00:00
Eelco Dolstra
52a9ba96f5 * Include the version file. 2009-05-07 11:34:08 +00:00
Eelco Dolstra
6a5599fd01 * Typo. 2009-05-07 11:33:57 +00:00
Michael Raskin
098cb9d233 Add an ftruncate call paired with fallocate to play safe with some FSes (namely, BtrFS fallocate sets file size to allocated size, i.e. multiple of block size) 2009-05-04 08:10:24 +00:00
Eelco Dolstra
c710fe540e * Machines of type x86_64-linux can do i686-linux builds. 2009-04-21 12:42:47 +00:00
Eelco Dolstra
d5eab2fc82 * Use foreach in a lot of places. 2009-04-21 11:52:16 +00:00
Eelco Dolstra
8f1bf28505 * nix-store --verify: don't bail out if a referenced path is missing.
(It can't fix it though.)
2009-04-21 11:06:27 +00:00
Eelco Dolstra
4e646b0ddb * Fix a few "comparison is always false/true due to limited range of
data type" warnings on 64-bit platforms.  The one in parser.y is
  likely to be a real bug.
2009-04-16 12:03:17 +00:00
Eelco Dolstra
0460ea4c39 * Cleanup. 2009-04-16 09:27:33 +00:00
Eelco Dolstra
dfb863f333 * Don't cache transient build hook problems. 2009-04-15 06:25:02 +00:00
Eelco Dolstra
435a93b5d8 * Remove references to __XXX primops. 2009-04-14 13:03:27 +00:00
Eelco Dolstra
8b2a01a8c2 * Release notes. 2009-04-14 12:02:22 +00:00
Eelco Dolstra
50cb6f9782 * Debian packages: use /etc, not /usr/etc. 2009-04-02 08:16:15 +00:00
Eelco Dolstra
351bf658f9 * Do a substitution even if --max-jobs == 0. 2009-03-31 21:14:07 +00:00
Eelco Dolstra
cff2b2a13a * Doh! 2009-03-31 15:50:03 +00:00
Eelco Dolstra
160a60d663 2009-03-30 19:35:55 +00:00
Eelco Dolstra
805144b705 * Make the poll interval configurable. 2009-03-30 11:34:03 +00:00
Eelco Dolstra
096affb55b * Update the mtime on the lock file to make it easy to see when a slot
was last used.
2009-03-29 18:40:44 +00:00
Eelco Dolstra
cbc1f57b48 * Undocument the "system" option. No sane person would use it :-) 2009-03-29 18:08:32 +00:00
Eelco Dolstra
7377195297 * With `--max-jobs 0', print a nicer error message than "Assertion
`!awake.empty()' failed."
2009-03-29 18:06:00 +00:00
Eelco Dolstra
737423a89c * Use polling to wait for a remote build slot when using a build hook
(that is, call the build hook with a certain interval until it
  accepts the build).
* build-remote.pl was totally broken: for all system types other than
  the local system type, it would send all builds to the *first*
  machine of the appropriate type.
2009-03-29 18:00:45 +00:00
Eelco Dolstra
47706e3924 * nix-copy-closure: compute the closure in one call to nix-store,
which is much faster.
2009-03-28 21:10:29 +00:00
Eelco Dolstra
6e946c8e72 * Scan for references and compute the SHA-256 hash of the output in
one pass.  This halves the amount of I/O.
2009-03-28 20:51:33 +00:00
Eelco Dolstra
c7152c8f97 * Don't use the non-standard __gnu_cxx::stdio_filebuf class. 2009-03-28 19:41:53 +00:00
Eelco Dolstra
3a2bbe7f8a * Simplify communication with the hook a bit (don't use file
descriptors 3/4, just use stdin/stderr).
2009-03-28 19:29:55 +00:00
Eelco Dolstra
7fb548aa26 * Clean up the output a bit. 2009-03-28 16:12:46 +00:00
Eelco Dolstra
f54e800366 * The `fixedOutput' variable didn't get initialised when using a build
hook, causing negative caching to fail randomly.
2009-03-28 16:12:27 +00:00
Eelco Dolstra
38f98b3282 * Argh, stupid timing sensitive tests... 2009-03-27 22:40:22 +00:00
Eelco Dolstra
e23a7a8b7b * Debian 5.0, Ubuntu 8.10 builds. 2009-03-27 14:20:03 +00:00
Eelco Dolstra
cb85bc396e * Create /nix/store if it doesn't exist. 2009-03-27 14:19:04 +00:00
Eelco Dolstra
61686926d1 * A job for creating a fully statically linked binary tarball. 2009-03-26 14:12:42 +00:00
Eelco Dolstra
9856efc7b0 * Support doing a fully static build. Statically linked Nix binaries
will be useful as a fallback on platforms for which there are no
  RPMs/Debs available.
2009-03-26 12:22:08 +00:00
Eelco Dolstra
5dd8fb2069 * Don't use ULLONG_MAX in maxFreed - use 0 to mean "no limit".
18446744073709551615ULL breaks on GCC 3.3.6 (`integer constant is
  too large for "long" type').
2009-03-26 11:02:07 +00:00
Eelco Dolstra
92f525ecf4 * Negative caching, i.e. caching of build failures. Disabled by
default.  This is mostly useful for Hydra.
2009-03-25 21:05:42 +00:00
Eelco Dolstra
7024a1ef07 * Removed the locking.sh test; it's redundant because of the extended
parallel.sh test.  Also, don't call multiple nix-builds in parallel,
  since they can race creating .nix-build-tmp-derivation.
2009-03-25 16:43:38 +00:00
Eelco Dolstra
8146a0c731 * Use bash in the tests. 2009-03-25 16:11:04 +00:00
Eelco Dolstra
a046858a22 * Doh. 2009-03-24 23:40:24 +00:00
Eelco Dolstra
12c8c64efa 2009-03-24 14:07:37 +00:00
Eelco Dolstra
7aedcf9460 * Make this test a bit more robust. It's still timing dependent
though.
2009-03-23 15:16:36 +00:00
Eelco Dolstra
3a87163b24 * Shut up a GCC warning. 2009-03-23 13:52:52 +00:00
Eelco Dolstra
cacff1be88 * No longer block while waiting for a lock on a store path. Instead
poll for it (i.e. if we can't acquire the lock, then let the main
  select() loop wait for at most a few seconds and then try again).
  This improves parallelism: if two nix-store processes are both
  trying to build a path at the same time, the second one shouldn't
  block; it should first see if it can build other goals.  Also, it
  prevents the deadlocks that have been occuring in Hydra lately,
  where a process waits for a lock held by another process that's
  waiting for a lock held by the first.

  The downside is that polling isn't really elegant, but POSIX doesn't
  provide a way to wait for locks in a select() loop.  The only
  solution would be to spawn a thread for each lock to do a blocking
  fcntl() and then signal the main thread, but that would require
  pthreads.
2009-03-23 01:05:54 +00:00
Eelco Dolstra
58969fa2bf * Refactoring. 2009-03-22 23:53:05 +00:00
Eelco Dolstra
d7b2d11255 * Test case (currently fails): multiple Nix builds shouldn't block
waiting on the same lock when there are other builds that can be
  done.
2009-03-22 23:16:18 +00:00
Eelco Dolstra
7a57b2920b * Better error message when nix-store --import is applied to garbage
(previously it would likely say "implementation cannot deal with >
  32-bit integers").
2009-03-22 17:51:45 +00:00
Eelco Dolstra
77d272623f * NAR archives: handle files larger than 2^32 bytes. Previously it
would just silently store only (fileSize % 2^32) bytes.
* Use posix_fallocate if available when unpacking archives.
* Provide a better error message when trying to unpack something that
  isn't a NAR archive.
2009-03-22 17:36:43 +00:00
Eelco Dolstra
7e05b8b75e * Future proofing: assume we can read manifests up to version 10
(which should therefore be backwards compatible).
2009-03-19 10:02:02 +00:00
Eelco Dolstra
b88460bcbc * Disregard the Hash field in manifests. 2009-03-19 09:47:34 +00:00
Eelco Dolstra
2897286487 * Unify exportReferencesGraph and exportBuildReferencesGraph, and make
sure that it works as expected when you pass it a derivation.  That
  is, we have to make sure that all build-time dependencies are built,
  and that they are all in the input closure (otherwise remote builds
  might fail, for example).  This is ensured at instantiation time by
  adding all derivations and their sources to inputDrvs and inputSrcs.
2009-03-18 17:36:42 +00:00
Eelco Dolstra
e530e0a350 * Improve the test. 2009-03-18 16:36:13 +00:00
Eelco Dolstra
9485ec31ea * Better cleanup after tests. 2009-03-18 16:35:35 +00:00
Eelco Dolstra
13df3915ef * Missing file. 2009-03-18 16:23:29 +00:00
Eelco Dolstra
c183ee5c79 * Acquire the locks on the output paths before trying to run the build
hook.  This fixes a problem with log files being partially or
  completely filled with 0's because another nix-store process
  truncates the log file.  It should also be more efficient.
2009-03-18 14:48:42 +00:00
Eelco Dolstra
1dcf208f56 * Clean up some tests (use nix-build where appropriate). 2009-03-18 13:15:55 +00:00
Eelco Dolstra
93b6926054 * Regression test for exportBuildReferencesGraph. It currently fails. 2009-03-17 17:38:32 +00:00
Eelco Dolstra
51e7e32c3b * Refactoring: renamed *.nix.in to *.nix. 2009-03-17 17:11:55 +00:00
Eelco Dolstra
2d5114452d * Regression test for the `exportReferencesGraph'
derivation attribute.
2009-03-17 16:33:48 +00:00
Eelco Dolstra
33ecb42991 * Cleanup. 2009-03-17 11:42:55 +00:00
Eelco Dolstra
be88248add * Make the version available to release.nix. 2009-03-09 15:05:08 +00:00
Eelco Dolstra
a96cac0d18 2009-03-06 17:00:58 +00:00
Eelco Dolstra
d4753c944f 2009-03-06 11:01:45 +00:00
Eelco Dolstra
0e6f604178 * Install some headers in the right location. 2009-03-05 14:57:50 +00:00
Eelco Dolstra
7f254706b0 * Allow the channel to declare a name for itself. 2009-03-03 14:47:39 +00:00
Eelco Dolstra
1273d355ac * nix-install-package: don't pollute /nix/var/nix/manifests. 2009-02-27 14:06:38 +00:00
Eelco Dolstra
d4ca5c3952 * Use the regular progress bar; the hash bar isn't very useful when
the size of the download isn't known in advance.
2009-02-27 13:55:11 +00:00
Eelco Dolstra
fd2e14b3c8 * nix-build: support --option. 2009-02-27 12:09:30 +00:00
Eelco Dolstra
60cb7de336 * Allow options from the Nix config file to be overriden from the
command line (e.g. "--option build-use-chroot true").
2009-02-27 11:04:41 +00:00
Eelco Dolstra
8ab6bc5a49 * nix-channel: use nix-build. 2009-02-27 11:01:03 +00:00
Eelco Dolstra
f052c10eed * Check the manifest version. 2009-02-27 09:53:58 +00:00
Eelco Dolstra
a7cee528c5 * Handle base-16 hashes in manifests. 2009-02-26 21:12:35 +00:00
Eelco Dolstra
041717eda3 * download-using-manifests: don't check the cryptographic hash of
downloaded files; rather, check the hash of the unpacked store
  path.

  When the server produces bzipped NAR archives on demand (like Hydra
  does), the hash of the file is not known in advance; it's streamed
  from the server.  Thus the manifest doesn't contain a hash for the
  bzipped NAR archive.  However, the server does know the hash of the
  *uncompressed* NAR archive (the "NarHash" field), since it's stored
  in the Nix database (nix-store -q --hash /nix/store/bla).  So we use
  that instead for checking the integrity of the download.
2009-02-19 23:46:37 +00:00
Michael Raskin
621093cb1c Replace wrong (w.r.t. PATH) sed call with in-shell substitution 2009-02-19 20:46:45 +00:00
Eelco Dolstra
824b154ce8 * Release output locks as soon as possible, not when the destructor of
the DerivationGoal runs.  Otherwise, if a goal is a top-level goal,
  then the lock won't be released until nix-store finishes.  With
  --keep-going and lots of top-level goals, it's possible to run out
  of file descriptors (this happened sometimes in the build farm for
  Nixpkgs).  Also, for failed derivation, it won't be possible to
  build it again  until the lock is released.
  
* Idem for locks on build users: these weren't released in a timely
  manner for failed top-level derivation goals.  So if there were more
  than (say) 10 such failed builds, you would get an error about
  having run out of build users.
2009-02-16 09:24:20 +00:00
Marc Weber
2ef579d1aa documentation for previous commit 2009-02-05 19:35:44 +00:00
Marc Weber
1407a1ec99 added primop functions __isBool, __isString, __isInt 2009-02-05 19:35:40 +00:00
Eelco Dolstra
1bb0f1e84b * Build hook: compress the transferred data. 2009-02-03 10:34:15 +00:00
Eelco Dolstra
b682fae9d9 * Build hooks: use nix-store --import. This prevents a redundant
scan for runtime dependencies (i.e. the local machine shouldn't do a
  scan that the remote machine has already done).  Also pipe directly
  into `nix-store --import': don't use a temporary file.
2009-02-02 17:24:10 +00:00
Marc Weber
6f8c96d123 vim syntax: support for indented strings ('' .. '') 2009-01-28 12:14:53 +00:00
Nicolas Pierron
110606d470 Add the "addErrorContext" builtin to add more information in the stack trace. 2009-01-27 14:36:44 +00:00
Eelco Dolstra
061141e632 * Make it compile on Debian 4.0 (which doesn't define PER_LINUX32_3GB
in sys/personality.h).
2009-01-27 13:36:59 +00:00
Eelco Dolstra
019176137f * When using a build hook, distinguish between transient failures
(e.g. an SSH connection problem) and permanent failures (i.e. the
  builder failed).  This matters to Hydra (it wants to know whether it
  makes sense to retry a build).
2009-01-13 11:39:09 +00:00
Eelco Dolstra
4ce692df88 2009-01-13 10:42:13 +00:00
Eelco Dolstra
c504d90c11 * Support i686-linux builds directly on x86_64-linux Nix
installations.  This is implemented using the personality() syscall,
  which causes uname to return "i686" in child processes.
2009-01-12 16:30:32 +00:00
Eelco Dolstra
8e39d9bdb3 * Make Nix build with Bison 2.4. 2009-01-12 12:51:28 +00:00
Eelco Dolstra
28355dafcf * Removed reference to losser.st-lab.cs.uu.nl, which is RIP after 9
years of loyal service :-)
2009-01-05 12:14:43 +00:00
Eelco Dolstra
8fce03e0ad * nix-store --verify: repair bad hash fields in the metadata file. 2008-12-16 13:28:18 +00:00
Eelco Dolstra
60ec75048a * Pass --use-atime / --max-atime to the daemon. 2008-12-16 12:23:35 +00:00
Eelco Dolstra
6f6bb1fdea * Delete the chroot if it already exists (e.g. left over from an
interrupted build).
2008-12-15 23:55:11 +00:00
Eelco Dolstra
92cb7c4dfe * Put chroots in the Nix store. This ensures that we can create hard
links to the inputs.
2008-12-12 17:14:57 +00:00
Eelco Dolstra
0008b0006d * Simplify deleting .lock files in /nix/store: just don't delete them
if they belong a path that's currently being built.  This gets rid
  of some Cygwin-specific code.
2008-12-12 17:03:18 +00:00
Eelco Dolstra
ac36c6cd44 * Some hackery to make "make check" succeed on Cygwin. 2008-12-12 15:36:18 +00:00
Eelco Dolstra
2b70a8e7c9 * Detect whether unshare() is available. 2008-12-12 13:41:36 +00:00
Eelco Dolstra
9122dcecbb * We can't use string objects in signal handlers because they might
allocate memory, which is verboten in signal handlers.  This caused
  random failures in the test suite on Mac OS X (triggered by the spurious
  SIGPOLL signals on Mac OS X, which should also be fixed).
2008-12-12 12:59:27 +00:00
Eelco Dolstra
6776a52bb3 * Use a PathSet for the chroot directories so that we don't
accidentally bind-mount a directory twice.
2008-12-12 11:49:42 +00:00
Eelco Dolstra
d86bd22d24 * Define _GNU_SOURCE. Hopefully this fixes the build on Debian 4.0
(http://hydra.nixos.org/nixlog/384/1).
2008-12-12 10:20:19 +00:00
Eelco Dolstra
ac5478eb52 * Don't provide the whole Nix store in the chroot, but only the
closure of the inputs.  This really enforces that there can't be any
  undeclared dependencies on paths in the store.  This is done by
  creating a fake Nix store and creating bind-mounts or hard-links in
  the fake store for all paths in the closure.  After the build, the
  build output is moved from the fake store to the real store.  TODO:
  the chroot has to be on the same filesystem as the Nix store for
  this to work, but this isn't enforced yet.  (I.e. it only works
  currently if /tmp is on the same FS as /nix/store.)
2008-12-11 18:57:10 +00:00
Eelco Dolstra
652817046b * Revert r13150: now that we use private namespaces for the chroot, we
don't have to put the chroot in /nix/var/nix/chroots anymore.
  They're back in /tmp now.
2008-12-11 17:52:34 +00:00
Eelco Dolstra
5a569509b4 * Provide a minimal /etc/passwd in the chroot to keep some builders
happy.
2008-12-11 17:44:02 +00:00
Eelco Dolstra
7c54f1603f * Do chroot builds in a private namespace. This means that all the
bind-mounts we do are only visible to the builder process and its
  children.  So accidentally doing "rm -rf" on the chroot directory
  won't wipe out /nix/store and other bind-mounted directories
  anymore.  Also, the bind-mounts in the private namespace disappear
  automatically when the builder exits.
2008-12-11 17:00:12 +00:00
Eelco Dolstra
07cdfb09fb * Open the connection to the daemon lazily (on demand) so that
read-only operations (like nix-env -qa) work properly when the
  daemon isn't running.
2008-12-11 14:30:25 +00:00
Eelco Dolstra
a0766eca27 * Build on Debian 4.0 (GCC 4.1.1). 2008-12-04 21:07:29 +00:00
Eelco Dolstra
cd16d5dc3d * Doh. 2008-12-04 17:56:12 +00:00
Eelco Dolstra
5b949241a5 * Build some 64-bit RPMs/Debs. 2008-12-04 17:54:14 +00:00
Eelco Dolstra
67958f21df * Be sure to clean up the daemon if the test fails. 2008-12-04 16:55:22 +00:00
Eelco Dolstra
d91dc086bb 2008-12-04 16:51:36 +00:00
Eelco Dolstra
9ac3f5df9c * Propagate --max-silent-time to remote machines. 2008-12-04 16:51:16 +00:00
Eelco Dolstra
bcfe98acff * Prefer building on a remote machine over a local machine. This
makes more sense for the build farm, otherwise every nix-store
  invocation will lead to at least one local build.  Will come up with
  a better solution later...
2008-12-04 16:35:47 +00:00
Eelco Dolstra
9850262a72 * Build RPMs, Debs, coverage analysis. 2008-12-04 15:25:28 +00:00
Eelco Dolstra
909fbb9de1 * When using build hooks, for any nix-store -r build operation, it is
necessary that at least one build hook doesn't return "postpone",
  otherwise nix-store will barf ("waiting for a build slot, yet there
  are no running children").  So inform the build hook when this is
  the case, so that it can start a build even when that would exceed
  the maximum load on a machine.
2008-12-04 14:29:41 +00:00
Eelco Dolstra
5dfba0b4db * Force allocation of a pseudo-terminal to clean up the remote
nix-store process when the connection is interrupted.
2008-12-04 13:36:52 +00:00
Eelco Dolstra
9ccdb80de3 * Don't ignore errors from writing to stderr. That way, when
nix-store -r (or some other operation) is started via ssh, it will
  at least have a chance of terminating quickly when the connection is
  killed.  Right now it just runs to completion, because it never
  notices that stderr is no longer connected to anything.  Of course
  it would be better if sshd would just send a SIGHUP, but it doesn't
  (https://bugzilla.mindrot.org/show_bug.cgi?id=396).
2008-12-04 13:13:31 +00:00
Eelco Dolstra
9fd9c4c635 * Support multiple system types per remote machine, e.g. a machine
list like

    root@example.org x86_64-linux /root/.ssh/id_buildfarm 1
    root@example.org i686-darwin  /root/.ssh/id_buildfarm 1

  This is possible when the Nix installation on example.org itself has
  remote builds enabled.
2008-12-04 12:20:06 +00:00
Eelco Dolstra
63b8f09d8d 2008-12-04 10:45:47 +00:00
Eelco Dolstra
f8713e1287 * Dirty hack to make nix-push work properly on derivations: the
derivation should be a source rather than a derivation dependency of
  the call to the NAR derivation.  Otherwise the derivation (and all
  its dependencies) will be built as a side-effect, which may not even
  succeed.
2008-12-04 10:40:41 +00:00
Eelco Dolstra
82ae85de27 * addToStore() in nix-worker: don't write the NAR dump received from
the client to a temporary directory, as that is highly inefficient.
2008-12-03 18:05:14 +00:00
Eelco Dolstra
5eaf644c99 * A simple API for parsing NAR archives. 2008-12-03 17:30:32 +00:00
Eelco Dolstra
cdee317419 * Backwards compatibility. 2008-12-03 17:02:29 +00:00
Eelco Dolstra
d95b68fde3 2008-12-03 16:15:38 +00:00
Eelco Dolstra
ff762fb499 * Pass HashType values instead of strings. 2008-12-03 16:10:17 +00:00
Eelco Dolstra
1307b22223 * Made addToStore() a lot more efficient: it no longer reads the path
being copied 3 times in the worst case.  It doesn't run in constant space,
  but it didn't do that anyway.
2008-12-03 15:51:17 +00:00
Eelco Dolstra
64519cfd65 * Unify the treatment of sources copied to the store, and recursive
SHA-256 outputs of fixed-output derivations.  I.e. they now produce
  the same store path:

  $ nix-store --add x
  /nix/store/j2fq9qxvvxgqymvpszhs773ncci45xsj-x

  $ nix-store --add-fixed --recursive sha256 x
  /nix/store/j2fq9qxvvxgqymvpszhs773ncci45xsj-x

  the latter being the same as the path that a derivation

    derivation {
      name = "x";
      outputHashAlgo = "sha256";
      outputHashMode = "recursive";
      outputHash = "...";
      ...
    };

  produces.

  This does change the output path for such fixed-output derivations.
  Fortunately they are quite rare.  The most common use is fetchsvn
  calls with SHA-256 hashes.  (There are a handful of those is
  Nixpkgs, mostly unstable development packages.)
  
* Documented the computation of store paths (in store-api.cc).
2008-12-03 15:06:30 +00:00
Eelco Dolstra
09bc0c502c * Install the release notes. 2008-11-29 00:31:39 +00:00
Eelco Dolstra
5d4eb9dd07 * Moved the build farm job for building Nix to the Nix tree. 2008-11-26 01:13:29 +00:00
Eelco Dolstra
0c478d2f4d * Ignore carriage returns. 2008-11-25 02:30:35 +00:00
Eelco Dolstra
5024bde8f4 * Handle prematurely ended logfiles, i.e. make sure we emit enough
close tags.
2008-11-25 01:06:15 +00:00
Eelco Dolstra
2ab09a55cf * Bump the version number. 2008-11-20 21:51:58 +00:00
127 changed files with 3018 additions and 1935 deletions

View File

@@ -1,17 +1,11 @@
SUBDIRS = externals src scripts corepkgs doc misc tests
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
svn-revision nix.conf.example NEWS
nix.conf.example NEWS version
include ./substitute.mk
nix.spec: nix.spec.in
rpm: nix.spec dist
rpm $(EXTRA_RPM_FLAGS) -ta $(distdir).tar.gz
relname:
echo -n $(distdir) > relname
install-data-local: init-state
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
$(INSTALL_DATA) $(srcdir)/nix.conf.example $(DESTDIR)$(sysconfdir)/nix
@@ -44,13 +38,11 @@ init-state:
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
else
init-state:
endif
svn-revision:
svnversion . > svn-revision
all-local: NEWS
NEWS: doc/manual/NEWS.txt
NEWS:
$(MAKE) -C doc/manual NEWS.txt
cp $(srcdir)/doc/manual/NEWS.txt NEWS

View File

@@ -1,21 +1,8 @@
AC_INIT(nix, 0.12)
AC_INIT(nix, m4_esyscmd([echo -n $(cat ./version)$VERSION_SUFFIX]))
AC_CONFIG_SRCDIR(README)
AC_CONFIG_AUX_DIR(config)
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
# Change to `1' to produce a `stable' release (i.e., the `preREVISION'
# suffix is not added).
STABLE=1
# Put the revision number in the version.
if test "$STABLE" != "1"; then
if REVISION=`test -d $srcdir/.svn && svnversion -n $srcdir 2> /dev/null`; then
VERSION=${VERSION}pre${REVISION}
elif REVISION=`cat $srcdir/svn-revision 2> /dev/null`; then
VERSION=${VERSION}pre${REVISION}
fi
fi
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [Nix version.])
AC_CANONICAL_HOST
@@ -63,14 +50,30 @@ AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
# Whether to produce a statically linked binary. On Cygwin, this is
# the default: dynamically linking against the ATerm DLL does work,
# except that it requires the ATerm "lib" directory to be in $PATH, as
# Windows doesn't have anything like an RPATH embedded in executable.
# Since this is kind of annoying, we use static libraries for now.
AC_ARG_ENABLE(static-nix, AC_HELP_STRING([--enable-static-nix],
[produce statically linked binaries]),
static_nix=$enableval, static_nix=no)
if test "$sys_name" = cygwin; then
static_nix=yes
fi
if test "$static_nix" = yes; then
AC_DISABLE_SHARED
AC_ENABLE_STATIC
fi
# Windows-specific stuff.
if test "$sys_name" = "cygwin"; then
# We cannot delete open files.
AC_DEFINE(CANNOT_DELETE_OPEN_FILES, 1, [Whether it is impossible to delete open files.])
# Shared libraries don't work, currently.
AC_DISABLE_SHARED
AC_ENABLE_STATIC
fi
@@ -103,6 +106,8 @@ AC_LANG_POP(C++)
# Check for chroot support (requires chroot() and bind mounts).
AC_CHECK_FUNCS([chroot])
AC_CHECK_FUNCS([unshare])
AC_CHECK_HEADERS([sched.h], [], [], [])
AC_CHECK_HEADERS([sys/param.h], [], [], [])
AC_CHECK_HEADERS([sys/mount.h], [], [],
[#ifdef HAVE_SYS_PARAM_H
@@ -111,12 +116,17 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
])
# Check for <locale>
# Check for <locale>.
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([locale], [], [], [])
AC_LANG_POP(C++)
# Check whether we have the personality() syscall, which allows us to
# do i686-linux builds on x86_64-linux machines.
AC_CHECK_HEADERS([sys/personality.h])
AC_DEFUN([NEED_PROG],
[
AC_PATH_PROG($1, $2)
@@ -272,6 +282,7 @@ AC_CHECK_FUNCS([setresuid setreuid lchown])
# Nice to have, but not essential.
AC_CHECK_FUNCS([strsignal])
AC_CHECK_FUNCS([posix_fallocate])
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
@@ -281,6 +292,14 @@ if test "$(uname)" = "Darwin"; then
fi
if test "$static_nix" = yes; then
# `-all-static' has to be added at the end of configure, because
# the C compiler doesn't know about -all-static (it's filtered out
# by libtool, but configure doesn't use libtool).
LDFLAGS="-all-static $LDFLAGS"
fi
AM_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile
externals/Makefile

View File

@@ -8,9 +8,15 @@ inputs=($inputs)
for ((n = 0; n < ${#inputs[*]}; n += 2)); do
channelName=${inputs[n]}
channelTarball=${inputs[n+1]}
echo "unpacking channel $channelName"
@bunzip2@ < $channelTarball | @tar@ xf -
if test -e */channel-name; then
channelName="$(@coreutils@/cat */channel-name)"
fi
nr=1
attrName=$(echo $channelName | @tr@ -- '- ' '__')
dirName=$attrName

View File

@@ -79,10 +79,14 @@ all-local: manual.html NEWS.html NEWS.txt
install-data-local: manual.html
$(INSTALL) -d $(DESTDIR)$(docdir)/manual
$(INSTALL_DATA) manual.html $(DESTDIR)$(docdir)/manual
ln -sf manual.html $(DESTDIR)$(docdir)/manual/index.html
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/manual
cp -r images $(DESTDIR)$(docdir)/manual/images
$(INSTALL) -d $(DESTDIR)$(docdir)/manual/figures
$(INSTALL_DATA) $(FIGURES) $(DESTDIR)$(docdir)/manual/figures
$(INSTALL) -d $(DESTDIR)$(docdir)/release-notes
$(INSTALL_DATA) NEWS.html $(DESTDIR)$(docdir)/release-notes/index.html
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/release-notes/
images:
mkdir images

View File

@@ -77,18 +77,8 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
This allows a Nix expression to fall back gracefully on older Nix
installations that dont have the desired built-in function.
However, in that case you should not write
<programlisting>
if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
This Nix expression will trigger an “undefined variable” error on
older Nix versions since <function>__getEnv</function> doesnt
exist. <literal>builtins.getEnv</literal>, on the other hand, is
safe since <literal>builtins</literal> always exists and attribute
selection is lazy, so its only performed if the test
succeeds.</para></listitem>
installations that dont have the desired built-in
function.</para></listitem>
</varlistentry>
@@ -364,6 +354,36 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isString</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a string, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isInt</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a int, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isBool</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a bool, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>isNull</function>
<replaceable>e</replaceable></term>

View File

@@ -151,12 +151,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<para>On the basis of this information, and whatever persistent
state the build hook keeps about other machines and their current
load, it has to decide what to do with the build. It should print
out on file descriptor 3 one of the following responses (terminated
by a newline, <literal>"\n"</literal>):
out on standard error one of the following responses (terminated by
a newline, <literal>"\n"</literal>):
<variablelist>
<varlistentry><term><literal>decline</literal></term>
<varlistentry><term><literal># decline</literal></term>
<listitem><para>The build hook is not willing or able to perform
the build; the calling Nix process should do the build itself,
@@ -164,7 +164,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry><term><literal>postpone</literal></term>
<varlistentry><term><literal># postpone</literal></term>
<listitem><para>The build hook cannot perform the build now, but
can do so in the future (e.g., because all available build slots
@@ -174,7 +174,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry><term><literal>accept</literal></term>
<varlistentry><term><literal># accept</literal></term>
<listitem><para>The build hook has accepted the
build.</para></listitem>
@@ -185,37 +185,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</para>
<para>If the build hook accepts the build, it is possible that it is
no longer necessary to do the build because some other process has
performed the build in the meantime. To prevent races, the hook
must read from file descriptor 4 a single line that tells it whether
to continue:
<variablelist>
<varlistentry><term><literal>cancel</literal></term>
<listitem><para>The build has already been done, so the hook
should exit.</para></listitem>
</varlistentry>
<varlistentry><term><literal>okay</literal></term>
<listitem><para>The hook should proceed with the build. At this
point, the calling Nix process has acquired locks on the output
path, so no other Nix process will perform the
build.</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>If the hook has been told to proceed, Nix will store in the
hooks current directory a number of text files that contain
information about the derivation:
<para>After sending <literal># accept</literal>, the hook should
read one line from standard input, which will be the string
<literal>okay</literal>. It can then proceed with the build.
Before sending <literal>okay</literal>, Nix will store in the hooks
current directory a number of text files that contain information
about the derivation:
<variablelist>
@@ -255,7 +230,9 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<para>The hook should copy the inputs to the remote machine,
register the validity of the inputs, perform the remote build, and
copy the outputs back to the local machine. An exit code other than
<literal>0</literal> indicates that the hook has failed.</para>
<literal>0</literal> indicates that the hook has failed. An exit
code equal to 100 means that the remote build failed (as opposed to,
e.g., a network error).</para>
</listitem>

View File

@@ -110,7 +110,7 @@
</appendix>
<xi:include href="troubleshooting.xml" />
<xi:include href="bugs.xml" />
<!-- <xi:include href="bugs.xml" /> -->
<xi:include href="glossary.xml" />
<appendix>

View File

@@ -24,6 +24,7 @@
<arg><option>--fallback</option></arg>
<arg><option>--readonly-mode</option></arg>
<arg><option>--log-type</option> <replaceable>type</replaceable></arg>
<arg><option>--show-trace</option></arg>
<sbr />
</nop>

View File

@@ -251,14 +251,14 @@
<programlisting>
{ # The system (e.g., `i686-linux') for which to build the packages.
system ? __currentSystem
system ? builtins.currentSystem
<replaceable>...</replaceable>
}: <replaceable>...</replaceable></programlisting>
So if you call this Nix expression (e.g., when you do
<literal>nix-env -i <replaceable>pkgname</replaceable></literal>),
the function will be called automatically using the value <link
linkend='builtin-currentSystem'><literal>__currentSystem</literal></link>
linkend='builtin-currentSystem'><literal>builtins.currentSystem</literal></link>
for the <literal>system</literal> argument. You can override this
using <option>--arg</option>, e.g., <literal>nix-env -i
<replaceable>pkgname</replaceable> --arg system
@@ -305,6 +305,14 @@
</varlistentry>
<varlistentry><term><option>--show-trace</option></term>
<listitem><para>Causes Nix to print out a stack trace in case of Nix
expression evaluation errors.</para></listitem>
</varlistentry>
</variablelist>

View File

@@ -6,6 +6,93 @@
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.13"><title>Release 0.13 (April NN,
2009)</title>
<para>This is primarily a bug fix release. It has some new
features:</para>
<itemizedlist>
<listitem>
<para>Syntactic sugar for writing nested attribute sets. Instead of
<programlisting>
{
foo = {
bar = 123;
xyzzy = true;
};
a = { b = { c = "d"; }; };
}
</programlisting>
you can write
<programlisting>
{
foo.bar = 123;
foo.xyzzy = true;
a.b.c = "d";
}
</programlisting>
This is useful, for instance, in NixOS configuration files.</para>
</listitem>
<listitem>
<para>Support for Nix channels generated by Hydra, the Nix-based
continuous build system. (Hydra generates NAR archives on the
fly, so the size and hash of these archives isnt known in
advance.)</para>
</listitem>
<listitem>
<para>Support <literal>i686-linux</literal> builds directly on
<literal>x86_64-linux</literal> Nix installations. This is
implemented using the <function>personality()</function> syscall,
which causes <command>uname</command> to return
<literal>i686</literal> in child processes.</para>
</listitem>
<listitem>
<para>Various improvements to the <literal>chroot</literal>
support. Building in a <literal>chroot</literal> works quite well
now.</para>
</listitem>
<listitem>
<para>Nix no longer blocks if it tries to build a path and another
process is already building the same path. Instead it tries to
build another buildable path first. This improves
parallelism.</para>
</listitem>
<listitem>
<para>Support for large (> 4 GiB) files in NAR archives.</para>
</listitem>
<listitem>
<para>Various (performance) improvements to the remote build
mechanism.</para>
</listitem>
<listitem>
<para>New primops: <varname>builtins.addErrorContext</varname> (to
add a string to stack traces — useful for debugging),
<varname>builtins.isBool</varname>,
<varname>builtins.isString</varname>,
<varname>builtins.isInt</varname>.</para>
</listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (November 20,

View File

@@ -1432,7 +1432,7 @@ command-line argument. See <xref linkend='sec-standard-environment'
inputs.</para></listitem>
<listitem><para>After the build, Nix sets the last-modified
timestamp on all files in the build result to 0 (00:00:00 1/1/1970
timestamp on all files in the build result to 1 (00:00:01 1/1/1970
UTC), sets the group to the default group, and sets the mode of the
file to 0444 or 0555 (i.e., read-only, with execute permission
enabled if the file was originally executable). Note that possible

View File

@@ -48,7 +48,7 @@ ATERM = aterm-2.4.2-fixes-r2
$(ATERM).tar.bz2:
@echo "Nix requires the CWI ATerm library to build."
@echo "Please download version 2.4.2-fixes-r2 from"
@echo " http://losser.st-lab.cs.uu.nl/~eelco/dist/aterm-2.4.2-fixes-r2.tar.bz2"
@echo " http://nixos.org/tarballs/aterm-2.4.2-fixes-r2.tar.bz2"
@echo "and place it in the externals/ directory."
false

View File

@@ -76,10 +76,10 @@ The hook `nix-mode-hook' is run when Nix mode is started.
("\\<baseNameOf\\>" . font-lock-builtin-face)
("\\<toString\\>" . font-lock-builtin-face)
("\\<isNull\\>" . font-lock-builtin-face)
("\\<\\([a-zA-Z_][a-zA-Z0-9_']*\\)[ \t]*="
(1 font-lock-variable-name-face nil nil))
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
. font-lock-constant-face)
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\.]*\\)[ \t]*="
(1 font-lock-variable-name-face nil nil))
("[a-zA-Z0-9._\\+-]*\\(/[a-zA-Z0-9._\\+-]+\\)+"
. font-lock-constant-face)
))

View File

@@ -21,12 +21,14 @@ syn match nixFuncArg "\zs\w\+\ze\s*:"
syn region nixStringParam start=+\${+ end=+}+
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
syn match nixEndOfLineComment "#.*$"
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
syn region nixStringIndented start=+''+ skip=+'''\|''${\|"+ end=+''+ contains=nixStringParam
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
hi def link nixKeyword Keyword
hi def link nixConditional Conditional
hi def link nixBrace Special
hi def link nixString String
hi def link nixStringIndented String
hi def link nixBuiltin Special
hi def link nixStringParam Macro
hi def link nixMultiLineComment Comment

View File

@@ -154,21 +154,17 @@
#build-chroot-dirs = /dev /dev/pts /proc
### Option `system'
### Option `build-cache-failure'
#
# This option specifies the canonical Nix system name of the current
# installation, such as `i686-linux' or `powerpc-darwin'. Nix can
# only build derivations whose `system' attribute equals the value
# specified here. In general, it never makes sense to modify this
# value from its default, since you can use it to `lie' about the
# platform you are building on (e.g., perform a Mac OS build on a
# Linux machine; the result would obviously be wrong). It only makes
# sense if the Nix binaries can run on multiple platforms, e.g.,
# `universal binaries' that run on `powerpc-darwin' and `i686-darwin'.
#
# It defaults to the canonical Nix system name detected by `configure'
# at build time.
# If this option is enabled, Nix will do negative caching; that is, it
# will remember failed builds, and won't attempt to try to build them
# again if you ask for it. Negative caching is disabled by default
# because Nix cannot distinguish between permanent build errors (e.g.,
# a syntax error in a source file) and transient build errors (e.g., a
# full disk), as they both cause the builder to return a non-zero exit
# code. You can clear the cache by doing `rm -f
# /nix/var/nix/db/failed/*'.
#
# Example:
# system = i686-darwin
#system =
# build-cache-failure = true
#build-cache-failure = false

196
release.nix Normal file
View File

@@ -0,0 +1,196 @@
{ nixpkgs ? ../nixpkgs }:
let
jobs = rec {
tarball =
{ nix ? {outPath = ./.; rev = 1234;}
, officialRelease ? false
}:
with import nixpkgs {};
releaseTools.sourceTarball {
name = "nix-tarball";
version = builtins.readFile ./version;
src = nix;
inherit officialRelease;
buildInputs = [curl bison flex2533 perl libxml2 libxslt w3m bzip2 jing_tools tetex dblatex];
configureFlags = ''
--with-docbook-rng=${docbook5}/xml/rng/docbook
--with-docbook-xsl=${docbook5_xsl}/xml/xsl/docbook
--with-xml-flags=--nonet
'';
# Include the BDB, ATerm and Bzip2 tarballs in the distribution.
preConfigure = ''
stripHash ${db45.src}
# Remove unnecessary stuff from the Berkeley DB tarball.
( mkdir bdb-temp
cd bdb-temp
tar xfz ${db45.src}
cd *
rm -rf docs test tcl perl libdb_java java rpc_server build_vxworks \
examples_java examples_c examples_cxx dist/tags
mkdir test
touch test/include.tcl
cd ..
tar cvfz ../externals/$strippedName *
)
stripHash ${aterm242fixes.src}
cp -pv ${aterm242fixes.src} externals/$strippedName
stripHash ${bzip2.src}
cp -pv ${bzip2.src} externals/$strippedName
'';
preDist = ''
make -C doc/manual install prefix=$out
make -C doc/manual manual.pdf prefix=$out
cp doc/manual/manual.pdf $out/manual.pdf
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
echo "doc-pdf manual $out/manual.pdf" >> $out/nix-support/hydra-build-products
echo "doc release-notes $out/share/doc/nix/release-notes" >> $out/nix-support/hydra-build-products
'';
};
build =
{ tarball ? jobs.tarball {}
, system ? "i686-linux"
}:
with import nixpkgs {inherit system;};
releaseTools.nixBuild {
name = "nix";
src = tarball;
buildInputs = [curl perl bzip2 openssl];
configureFlags = ''
--disable-init-state
--with-bdb=${db45} --with-aterm=${aterm242fixes} --with-bzip2=${bzip2}
'';
};
static =
{ tarball ? jobs.tarball {}
, system ? "i686-linux"
}:
with import nixpkgs {inherit system;};
releaseTools.binaryTarball {
name = "nix-static-tarball";
src = tarball;
buildInputs = [curl perl bzip2];
configureFlags = ''
--disable-init-state
--disable-old-db-compat --with-aterm=${aterm242fixes} --with-bzip2=${bzip2}
--enable-static-nix
'';
};
coverage =
{ tarball ? jobs.tarball {}
}:
with import nixpkgs {};
releaseTools.coverageAnalysis {
name = "nix-build";
src = tarball;
buildInputs = [
curl perl bzip2 openssl
# These are for "make check" only:
graphviz libxml2 libxslt
];
configureFlags = ''
--disable-init-state --disable-shared
--with-bdb=${db45} --with-aterm=${aterm242fixes} --with-bzip2=${bzip2}
'';
lcovFilter = ["*/boost/*" "*-tab.*"];
# We call `dot', and even though we just use it to
# syntax-check generated dot files, it still requires some
# fonts. So provide those.
FONTCONFIG_FILE = texFunctions.fontsConf;
};
rpm_fedora5i386 = makeRPM_i686 (diskImages: diskImages.fedora5i386) 20;
rpm_fedora9i386 = makeRPM_i686 (diskImages: diskImages.fedora9i386) 50;
rpm_fedora9x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora9x86_64) 50;
rpm_fedora10i386 = makeRPM_i686 (diskImages: diskImages.fedora10i386) 40;
rpm_fedora10x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora10x86_64) 40;
rpm_opensuse103i386 = makeRPM_i686 (diskImages: diskImages.opensuse103i386) 40;
deb_debian40i386 = makeDeb_i686 (diskImages: diskImages.debian40i386) 40;
deb_debian40x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian40x86_64) 40;
deb_debian50i386 = makeDeb_i686 (diskImages: diskImages.debian50i386) 30;
deb_debian50x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian50x86_64) 30;
deb_ubuntu804i386 = makeDeb_i686 (diskImages: diskImages.ubuntu804i386) 50;
deb_ubuntu804x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu804x86_64) 50;
deb_ubuntu810i386 = makeDeb_i686 (diskImages: diskImages.ubuntu810i386) 40;
deb_ubuntu810x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu810x86_64) 40;
};
makeRPM_i686 = makeRPM "i686-linux";
makeRPM_x86_64 = makeRPM "x86_64-linux";
makeRPM =
system: diskImageFun: prio:
{ tarball ? jobs.tarball {}
}:
with import nixpkgs {inherit system;};
releaseTools.rpmBuild rec {
name = "nix-rpm-${diskImage.name}";
src = tarball;
diskImage = diskImageFun vmTools.diskImages;
memSize = 1024;
meta = { schedulingPriority = toString prio; };
};
makeDeb_i686 = makeDeb "i686-linux";
makeDeb_x86_64 = makeDeb "x86_64-linux";
makeDeb =
system: diskImageFun: prio:
{ tarball ? jobs.tarball {}
}:
with import nixpkgs {inherit system;};
releaseTools.debBuild {
name = "nix-deb";
src = tarball;
diskImage = diskImageFun vmTools.diskImages;
memSize = 1024;
meta = { schedulingPriority = toString prio; };
configureFlags = "--sysconfdir=/etc";
};
in jobs

View File

@@ -3,6 +3,7 @@
use strict;
use Fcntl ':flock';
use English '-no_match_vars';
use IO::Handle;
# General operation:
#
@@ -23,16 +24,12 @@ use English '-no_match_vars';
my $loadIncreased = 0;
my $amWilling = shift @ARGV;
my $localSystem = shift @ARGV;
my $neededSystem = shift @ARGV;
my $drvPath = shift @ARGV;
my ($amWilling, $localSystem, $neededSystem, $drvPath, $maxSilentTime) = @ARGV;
$maxSilentTime = 0 unless defined $maxSilentTime;
sub sendReply {
my $reply = shift;
open OUT, ">&3" or die;
print OUT "$reply\n";
close OUT;
print STDERR "# $reply\n";
}
sub decline {
@@ -47,15 +44,11 @@ mkdir $currentLoad, 0777 or die unless -d $currentLoad;
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
decline if !defined $conf || ! -e $conf;
# Decline if the local system can do the build.
decline if $amWilling && ($localSystem eq $neededSystem);
my $canBuildLocally = $amWilling && ($localSystem eq $neededSystem);
# Otherwise find a willing remote machine.
my %machines;
my %systemTypes;
my %sshKeys;
my %maxJobs;
my @machines;
my %curJobs;
@@ -67,10 +60,12 @@ while (<CONF>) {
s/\#.*$//g;
next if /^\s*$/;
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s*$/ or die;
$machines{$1} = "";
$systemTypes{$1} = $2;
$sshKeys{$1} = $3;
$maxJobs{$1} = $4;
push @machines,
{ hostName => $1
, systemType => $2
, sshKeys => $3
, maxJobs => $4
};
}
close CONF;
@@ -85,21 +80,26 @@ flock(MAINLOCK, LOCK_EX) or die;
# Find a suitable system.
my $rightType = 0;
my $machine;
LOOP: foreach my $cur (keys %machines) {
if ($neededSystem eq $systemTypes{$cur}) {
my $slotLock;
LOOP: foreach my $cur (@machines) {
if ($neededSystem eq $cur->{systemType}
|| ($neededSystem eq "i686-linux" && $cur->{systemType} eq "x86_64-linux"))
{
$rightType = 1;
# We have a machine of the right type. Try to get a lock on
# one of the machine's lock files.
my $slot = 0;
while ($slot < $maxJobs{$cur}) {
my $slotLock = "$currentLoad/$cur-$slot";
open SLOTLOCK, ">>$slotLock" or die;
if (flock(SLOTLOCK, LOCK_EX | LOCK_NB)) {
while ($slot < $cur->{maxJobs}) {
my $slotLockFn = "$currentLoad/" . $cur->{systemType} . "-" . $cur->{hostName} . "-$slot";
$slotLock = new IO::Handle;
open $slotLock, ">>$slotLockFn" or die;
if (flock($slotLock, LOCK_EX | LOCK_NB)) {
utime undef, undef, $slotLock;
$machine = $cur;
last LOOP;
}
close SLOTLOCK;
}
close $slotLock;
$slot++;
}
}
@@ -108,9 +108,11 @@ LOOP: foreach my $cur (keys %machines) {
close MAINLOCK;
# Didn't find one?
# Didn't find one? Then decline or postpone.
if (!defined $machine) {
if ($rightType) {
# Postpone if we have a machine of the right type, except if the
# local system can and wants to do the build.
if ($rightType && !$canBuildLocally) {
sendReply "postpone";
exit 0;
} else {
@@ -120,11 +122,8 @@ if (!defined $machine) {
# Yes we did, accept.
sendReply "accept";
open IN, "<&4" or die;
my $x = <IN>;
my $x = <STDIN>;
chomp $x;
#print "got $x\n";
close IN;
if ($x ne "okay") {
exit 0;
@@ -132,7 +131,8 @@ if ($x ne "okay") {
# Do the actual job.
print "BUILDING REMOTE: $drvPath on $machine\n";
my $hostName = $machine->{hostName};
print STDERR "building `$drvPath' on `$hostName'\n";
# Make sure that we don't get any SSH passphrase or host key popups -
# if there is any problem it should fail, not do something
@@ -141,10 +141,10 @@ $ENV{"DISPLAY"} = "";
$ENV{"SSH_PASSWORD_FILE="} = "";
$ENV{"SSH_ASKPASS="} = "";
my $sshOpts = "-i $sshKeys{$machine} -x";
my $sshOpts = "-i " . $machine->{sshKeys} . " -x";
# Hack to support Cygwin: if we login without a password, we don't
# have exactly the same right as when we do. This causes the
# have exactly the same rights as when we do. This causes the
# Microsoft C compiler to fail with certain flags:
#
# http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99676
@@ -154,8 +154,8 @@ my $sshOpts = "-i $sshKeys{$machine} -x";
# password. (It only calls this command when there is no controlling
# terminal, but Nix ensures that is is the case. When doing this
# manually, use setsid(1).)
if ($sshKeys{$machine} =~ /^password:/) {
my $passwordFile = $sshKeys{$machine};
if ($machine->{sshKeys} =~ /^password:/) {
my $passwordFile = $machine->{sshKeys};
$passwordFile =~ s/^password://;
$sshOpts = "ssh -x";
$ENV{"SSH_PASSWORD_FILE"} = $passwordFile;
@@ -173,36 +173,40 @@ $inputs =~ s/\n/ /g;
my $outputs = `cat outputs`; die if ($? != 0);
$outputs =~ s/\n/ /g;
print "COPYING INPUTS...\n";
print "copying inputs...\n";
my $maybeSign = "";
$maybeSign = "--sign" if -e "/nix/etc/nix/signing-key.sec";
system("NIX_SSHOPTS=\"$sshOpts\" nix-copy-closure $machine $maybeSign $drvPath $inputs") == 0
or die "cannot copy inputs to $machine: $?";
system("NIX_SSHOPTS=\"$sshOpts\" @bindir@/nix-copy-closure --gzip $hostName $maybeSign $drvPath $inputs") == 0
or die "cannot copy inputs to $hostName: $?";
print "BUILDING...\n";
print "building...\n";
system("ssh $sshOpts $machine 'nix-store -rvvK $drvPath'") == 0
or die "remote build on $machine failed: $?";
my $buildFlags = "--max-silent-time $maxSilentTime";
print "REMOTE BUILD DONE: $drvPath on $machine\n";
# `-tt' forces allocation of a pseudo-terminal. This is required to
# make the remote nix-store process receive a signal when the
# connection dies. Without it, the remote process might continue to
# run indefinitely (that is, until it next tries to write to
# stdout/stderr).
if (system("ssh -tt $sshOpts $hostName 'nix-store --realise -K $buildFlags $drvPath > /dev/null'") != 0) {
# If we couldn't run ssh or there was an ssh problem (indicated by
# exit code 255), then we return exit code 1; otherwise we assume
# that the builder failed, which we indicated to Nix using exit
# code 100. It's important to distinguish between the two because
# the first is a transient failure and the latter is permanent.
my $res = $? == -1 || ($? >> 8) == 255 ? 1 : 100;
print STDERR "build of `$drvPath' on `$hostName' failed with exit code $?\n";
exit $res;
}
print "build of `$drvPath' on `$hostName' succeeded\n";
foreach my $output (split '\n', $outputs) {
my $maybeSignRemote = "";
$maybeSignRemote = "--sign" if $UID != 0;
system("ssh $sshOpts $machine 'nix-store --export $maybeSignRemote $output' > dump") == 0
or die "cannot copy $output from $machine: $?";
# This doesn't work yet, since the caller has a lock on the output
# path. We should move towards lock-free invocation of build
# hooks and substitutes.
#system("nix-store --import < dump") == 0
# or die "cannot import $output: $?";
# Hack: skip the first 8 bytes (the nix-store --export next
# archive marker). The archive follows.
system("(dd bs=1 count=8 of=/dev/null && cat) < dump | nix-store --restore $output") == 0
or die "cannot restore $output: $?";
system("ssh $sshOpts $hostName 'nix-store --export $maybeSignRemote $output | gzip' | gunzip | @bindir@/nix-store --import > /dev/null") == 0
or die "cannot copy $output from $hostName: $?";
}

View File

@@ -9,7 +9,7 @@ my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
STDOUT->autoflush(1);
my $manifestDir = "@localstatedir@/nix/manifests";
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "@localstatedir@/nix/manifests");
my $logFile = "@localstatedir@/log/nix/downloads";
@@ -19,10 +19,15 @@ my %localPaths;
my %patches;
for my $manifest (glob "$manifestDir/*.nixmanifest") {
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
if ($version < 3) {
print STDERR "you have an old-style manifest `$manifest'; please delete it\n";
exit 1;
}
if ($version >= 10) {
print STDERR "manifest `$manifest' is too new; please delete it or upgrade Nix\n";
exit 1;
}
}
@@ -185,13 +190,11 @@ while ($queueFront < scalar @queue) {
$format = "" if $baseHashAlgo eq "md5";
my $hash = `$binDir/nix-hash --type '$baseHashAlgo' $format "$patch->{basePath}"`;
chomp $hash;
# print " MY HASH is $hash\n";
if ($hash ne $baseHash) {
print LOGFILE "$$ rejecting $patch->{basePath}\n";
next;
}
}
# print " PATCH from $patch->{basePath}\n";
addToQueue $patch->{basePath};
addEdge $patch->{basePath}, $u, $patch->{size}, "patch", $patch;
}
@@ -199,10 +202,12 @@ while ($queueFront < scalar @queue) {
# Add NAR file edges to the start node.
my $narFileList = $narFiles{$u};
foreach my $narFile (@{$narFileList}) {
# print " NAR from $narFile->{url}\n";
addEdge "start", $u, $narFile->{size}, "narfile", $narFile;
# !!! how to handle files whose size is not known in advance?
# For now, assume some arbitrary size (1 MB).
addEdge "start", $u, ($narFile->{size} || 1000000), "narfile", $narFile;
if ($u eq $targetPath) {
print LOGFILE "$$ full-download-would-be $narFile->{size}\n";
my $size = $narFile->{size} || -1;
print LOGFILE "$$ full-download-would-be $size\n";
}
}
@@ -228,8 +233,6 @@ while (scalar @todo > 0) {
my $u_ = $graph{$u};
# print "IN $u $u_->{d}\n";
foreach my $edge (@{$u_->{edges}}) {
my $v_ = $graph{$edge->{end}};
if ($v_->{d} > $u_->{d} + $edge->{weight}) {
@@ -237,7 +240,6 @@ while (scalar @todo > 0) {
# Store the edge; to edge->start is actually the
# predecessor.
$v_->{pred} = $edge;
# print " RELAX $edge->{end} $v_->{d}\n";
}
}
}
@@ -259,20 +261,18 @@ while ($cur ne "start") {
my $curStep = 1;
my $maxStep = scalar @path;
sub downloadFile {
my $url = shift;
my ($hashAlgo, $hash) = parseHash(shift);
sub downloadFile {
my $url = shift;
$ENV{"PRINT_PATH"} = 1;
$ENV{"QUIET"} = 1;
$ENV{"NIX_HASH_ALGO"} = $hashAlgo;
my ($hash2, $path) = `$binDir/nix-prefetch-url '$url' '$hash'`;
my ($hash, $path) = `$binDir/nix-prefetch-url '$url'`;
die "download of `$url' failed" unless $? == 0;
chomp $hash2;
chomp $path;
die "hash mismatch, expected $hash, got $hash2" if $hash ne $hash2;
return $path;
}
my $finalNarHash;
while (scalar @path > 0) {
my $edge = pop @path;
my $u = $edge->{start};
@@ -302,7 +302,7 @@ while (scalar @path > 0) {
# Download the patch.
print " downloading patch...\n";
my $patchPath = downloadFile "$patch->{url}", "$patch->{hash}";
my $patchPath = downloadFile "$patch->{url}";
# Apply the patch to the NAR archive produced in step 1 (for
# the already present path) or a later step (for patch sequences).
@@ -320,17 +320,20 @@ while (scalar @path > 0) {
system("$binDir/nix-store --restore $v < $tmpNar2") == 0
or die "cannot unpack $tmpNar2 into `$v'";
}
$finalNarHash = $patch->{narHash};
}
elsif ($edge->{type} eq "narfile") {
my $narFile = $edge->{info};
print "downloading `$narFile->{url}' into `$v'\n";
print LOGFILE "$$ narfile $narFile->{url} $narFile->{size} $v\n";
my $size = $narFile->{size} || -1;
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
# Download the archive.
print " downloading archive...\n";
my $narFilePath = downloadFile "$narFile->{url}", "$narFile->{hash}";
my $narFilePath = downloadFile "$narFile->{url}";
if ($curStep < $maxStep) {
# The archive will be used a base to a patch.
@@ -342,11 +345,36 @@ while (scalar @path > 0) {
system("@bunzip2@ < '$narFilePath' | $binDir/nix-store --restore '$v'") == 0
or die "cannot unpack `$narFilePath' into `$v'";
}
$finalNarHash = $narFile->{narHash};
}
$curStep++;
}
# Make sure that the hash declared in the manifest matches what we
# downloaded and unpacked.
if (defined $finalNarHash) {
my ($hashAlgo, $hash) = parseHash $finalNarHash;
# The hash in the manifest can be either in base-16 or base-32.
# Handle both.
my $extraFlag =
($hashAlgo eq "sha256" && length($hash) != 64)
? "--base32" : "";
my $hash2 = `@bindir@/nix-hash --type $hashAlgo $extraFlag $targetPath`
or die "cannot compute hash of path `$targetPath'";
chomp $hash2;
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2"
if $hash ne $hash2;
} else {
die "cannot check integrity of the downloaded path since its hash is not known";
}
print LOGFILE "$$ success\n";
close LOGFILE;

View File

@@ -77,7 +77,7 @@ EOF
elsif ($arg eq "--attr" or $arg eq "-A") {
$n++;
die "$0: `--attr' requires an argument\n" unless $n < scalar @ARGV;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
push @instArgs, ("--attr", $ARGV[$n]);
}
@@ -87,7 +87,21 @@ EOF
$n += 2;
}
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time") {
elsif ($arg eq "--log-type") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
push @instArgs, ($arg, $ARGV[$n]);
push @buildArgs, ($arg, $ARGV[$n]);
}
elsif ($arg eq "--option") {
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
push @buildArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
$n += 2;
}
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
push @buildArgs, ($arg, $ARGV[$n]);
@@ -128,7 +142,10 @@ foreach my $expr (@exprs) {
# !!! would prefer the perl 5.8.0 pipe open feature here.
my $pid = open(DRVPATHS, "-|") || exec "$binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
close DRVPATHS or exit 1;
if (!close DRVPATHS) {
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
exit 1;
}
foreach my $drvPath (@drvPaths) {
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
@@ -140,7 +157,10 @@ foreach my $expr (@exprs) {
$pid = open(OUTPATHS, "-|") || exec "$binDir/nix-store", "--add-root", $outLink, "--indirect", "-rv",
@buildArgs, @drvPaths;
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
close OUTPATHS or exit 1;
if (!close OUTPATHS) {
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
exit 1;
}
next if $dryRun;

View File

@@ -125,15 +125,13 @@ sub update {
my $rootFile = "$rootsDir/per-user/$userName/channels";
# Instantiate the Nix expression.
# Build the Nix expression.
print "unpacking channel Nix expressions...\n";
my $storeExpr = `@bindir@/nix-instantiate --add-root '$rootFile'.tmp @datadir@/nix/corepkgs/channels/unpack.nix --argstr system @system@ --arg inputs '$inputs'`
or die "cannot instantiate Nix expression";
chomp $storeExpr;
# Build the resulting derivation.
my $outPath = `@bindir@/nix-store --add-root '$rootFile' -r '$storeExpr'`
or die "cannot realise store expression";
my $outPath = `\\
@bindir@/nix-build --out-link '$rootFile' --drv-link '$rootFile'.tmp \\
@datadir@/nix/corepkgs/channels/unpack.nix \\
--argstr system @system@ --arg inputs '$inputs'`
or die "cannot unpack the channels";
chomp $outPath;
unlink "$rootFile.tmp";

View File

@@ -55,30 +55,18 @@ while (@ARGV) {
if ($toMode) { # Copy TO the remote machine.
my @allStorePaths;
my %storePathsSeen;
foreach my $storePath (@storePaths) {
# $arg might be a symlink to the store, so resolve it.
my $storePath2 = (`$binDir/nix-store --query --resolve '$storePath'`
or die "cannot resolve `$storePath'");
chomp $storePath2;
# Get the closure of this path.
my $pid = open(READ,
"$binDir/nix-store --query --requisites '$storePath2'|") or die;
# Get the closure of this path.
my $pid = open(READ, "$binDir/nix-store --query --requisites @storePaths|") or die;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
if (!defined $storePathsSeen{$_}) {
push @allStorePaths, $_;
$storePathsSeen{$_} = 1;
}
}
close READ or die "nix-store failed: $?";
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
push @allStorePaths, $_;
}
close READ or die "nix-store failed: $?";
# Ask the remote host which paths are invalid.
open(READ, "ssh @sshOpts $sshHost nix-store --check-validity --print-invalid @allStorePaths|");
@@ -111,15 +99,11 @@ else { # Copy FROM the remote machine.
"ssh @sshOpts $sshHost nix-store --query --requisites @storePaths|") or die;
my @allStorePaths;
my %storePathsSeen;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
if (!defined $storePathsSeen{$_}) {
push @allStorePaths, $_;
$storePathsSeen{$_} = 1;
}
push @allStorePaths, $_;
}
close READ or die "nix-store on remote machine `$sshHost' failed: $?";

View File

@@ -12,7 +12,8 @@ needed_path="?$QUERY_STRING"
needed_path="${needed_path#*[?&]needed_path=}"
needed_path="${needed_path%%&*}"
#needed_path="$(echo $needed_path | ./unhttp)"
needed_path="$(echo $needed_path | sed -e 's/%2B/+/g; s/%3D/=/g')"
needed_path="${needed_path//%2B/+}"
needed_path="${needed_path//%3D/=}"
echo needed_path: "$needed_path" >&2

View File

@@ -123,6 +123,11 @@ if ($interactive) {
}
# Store the manifest in the temporary directory so that we don't
# pollute /nix/var/nix/manifests.
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
print "\nPulling manifests...\n";
system("$binDir/nix-pull", $manifestURL) == 0
or barf "nix-pull failed: $?";

View File

@@ -60,7 +60,7 @@ removeTempDir() {
doDownload() {
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
@curl@ $cacheFlags --fail --location --max-redirs 20 --disable-epsv \
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
}

View File

@@ -8,15 +8,10 @@ my $tmpDir = tempdir("nix-pull.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
my $libexecDir = $ENV{"NIX_LIBEXEC_DIR"};
$libexecDir = "@libexecdir@" unless defined $libexecDir;
my $stateDir = $ENV{"NIX_STATE_DIR"};
$stateDir = "@localstatedir@/nix" unless defined $stateDir;
my $storeDir = $ENV{"NIX_STORE_DIR"};
$storeDir = "@storedir@" unless defined $storeDir;
my $libexecDir = ($ENV{"NIX_LIBEXEC_DIR"} or "@libexecdir@");
my $storeDir = ($ENV{"NIX_STORE_DIR"} or "@storedir@");
my $stateDir = ($ENV{"NIX_STATE_DIR"} or "@localstatedir@/nix");
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "$stateDir/manifests");
# Prevent access problems in shared-stored installations.
@@ -68,10 +63,11 @@ sub processURL {
print "obtaining list of Nix archives at `$url'...\n";
$manifest = downloadFile $url;
}
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
die "`$url' is not manifest or it is too old (i.e., for Nix <= 0.7)\n";
}
die "`$url' is not a manifest or it is too old (i.e., for Nix <= 0.7)\n" if $version < 3;
die "manifest `$url' is too new\n" if $version >= 5;
if ($skipWrongStore) {
foreach my $path (keys %narFiles) {
@@ -91,7 +87,7 @@ sub processURL {
or die "cannot hash `$manifest'";
chomp $hash;
my $finalPath = "$stateDir/manifests/$baseName-$hash.nixmanifest";
my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
system("@coreutils@/ln", "-sfn", "$manifest", "$finalPath") == 0
or die "cannot link `$finalPath to `$manifest'";

View File

@@ -128,7 +128,7 @@ while (<READ>) {
close READ or die "nix-instantiate failed: $?";
# Realise the store expressions.
# Build the derivations.
print STDERR "creating archives...\n";
my @narPaths;
@@ -140,12 +140,7 @@ while (scalar @tmp > 0) {
my @tmp2 = @tmp[0..$n - 1];
@tmp = @tmp[$n..scalar @tmp - 1];
# Note: we disable build hooks because of the impure path
# reference (see above). Even if that is fixed, using a hook
# probably wouldn't make that much sense; pumping lots of data
# around just to compress them won't gain that much.
$ENV{"NIX_BUILD_HOOK"} = "";
my $pid = open(READ, "$binDir/nix-store --no-build-hook --realise @tmp2|")
my $pid = open(READ, "$binDir/nix-store --realise @tmp2|")
or die "cannot run nix-store";
while (<READ>) {
chomp;

View File

@@ -2,10 +2,7 @@ use strict;
sub addPatch {
my $patches = shift;
my $storePath = shift;
my $patch = shift;
my $allowConflicts = shift;
my ($patches, $storePath, $patch) = @_;
$$patches{$storePath} = []
unless defined $$patches{$storePath};
@@ -14,15 +11,9 @@ sub addPatch {
my $found = 0;
foreach my $patch2 (@{$patchList}) {
if ($patch2->{url} eq $patch->{url}) {
if ($patch2->{hash} eq $patch->{hash}) {
$found = 1 if ($patch2->{basePath} eq $patch->{basePath});
} else {
die "conflicting hashes for URL $patch->{url}, " .
"namely $patch2->{hash} and $patch->{hash}"
unless $allowConflicts;
}
}
$found = 1 if
$patch2->{url} eq $patch->{url} &&
$patch2->{basePath} eq $patch->{basePath};
}
push @{$patchList}, $patch if !$found;
@@ -32,12 +23,7 @@ sub addPatch {
sub readManifest {
my $manifest = shift;
my $narFiles = shift;
my $localPaths = shift;
my $patches = shift;
my $allowConflicts = shift;
$allowConflicts = 0 unless defined $allowConflicts;
my ($manifest, $narFiles, $localPaths, $patches) = @_;
open MANIFEST, "<$manifest"
or die "cannot open `$manifest': $!";
@@ -98,15 +84,7 @@ sub readManifest {
my $found = 0;
foreach my $narFile (@{$narFileList}) {
if ($narFile->{url} eq $url) {
if ($narFile->{hash} eq $hash) {
$found = 1;
} else {
die "conflicting hashes for URL $url, " .
"namely $narFile->{hash} and $hash"
unless $allowConflicts;
}
}
$found = 1 if $narFile->{url} eq $url;
}
if (!$found) {
push @{$narFileList},
@@ -124,7 +102,7 @@ sub readManifest {
, basePath => $basePath, baseHash => $baseHash
, narHash => $narHash, patchType => $patchType
, hashAlgo => $hashAlgo
}, $allowConflicts;
};
}
elsif ($type eq "localPath") {
@@ -171,12 +149,8 @@ sub readManifest {
}
sub writeManifest
{
my $manifest = shift;
my $narFiles = shift;
my $patches = shift;
my $copySources = shift;
sub writeManifest {
my ($manifest, $narFiles, $patches) = @_;
open MANIFEST, ">$manifest.tmp"; # !!! check exclusive
@@ -190,9 +164,9 @@ sub writeManifest
print MANIFEST "{\n";
print MANIFEST " StorePath: $storePath\n";
print MANIFEST " NarURL: $narFile->{url}\n";
print MANIFEST " Hash: $narFile->{hash}\n";
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
print MANIFEST " NarHash: $narFile->{narHash}\n";
print MANIFEST " Size: $narFile->{size}\n";
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
print MANIFEST " References: $narFile->{references}\n"
if defined $narFile->{references} && $narFile->{references} ne "";
print MANIFEST " Deriver: $narFile->{deriver}\n"

View File

@@ -1,6 +1,6 @@
SUBDIRS = format
pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
nobase_pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
shared_ptr.hpp weak_ptr.hpp throw_exception.hpp \
enable_shared_from_this.hpp \
detail/shared_count.hpp detail/workaround.hpp

View File

@@ -3,12 +3,12 @@ pkglib_LTLIBRARIES = libexpr.la
libexpr_la_SOURCES = \
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc \
names.cc
names.cc normal-forms.cc
pkginclude_HEADERS = \
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh \
names.hh
names.hh nixexpr-ast.hh
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
../boost/format/libformat.la

View File

@@ -16,13 +16,23 @@ namespace nix {
EvalState::EvalState()
: normalForms(32768), primOps(128)
: sessionNormalForms(32768), normalForms(32768), primOps(128)
{
nrEvaluated = nrCached = 0;
nrEvaluated = nrCached = nrDepthAfterReset = 0;
initNixExprHelpers();
addPrimOps();
allowUnsafeEquality = getEnv("NIX_NO_UNSAFE_EQ", "") == "";
safeCache = true;
loadNormalForms();
}
EvalState::~EvalState()
{
saveNormalForms();
}
@@ -466,8 +476,10 @@ LocalNoInline(Expr evalVar(EvalState & state, ATerm name))
if (arity == 0)
/* !!! backtrace for primop call */
return ((PrimOp) ATgetBlobData(fun)) (state, ATermVector());
else
else {
state.safeCache = false;
return makePrimOp(arity, fun, ATempty);
}
}
@@ -493,6 +505,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
for (ATermIterator i(args); i; ++i)
args2[--arity] = *i;
/* !!! backtrace for primop call */
state.safeCache = true;
return ((PrimOp) ATgetBlobData(funBlob))
(state, args2);
} else
@@ -644,6 +657,48 @@ LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
}
/* Implementation of the `==' and `!=' operators. */
LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2))
{
e1 = evalExpr(state, e1);
e2 = evalExpr(state, e2);
/* We cannot test functions/primops for equality, and we currently
don't support testing equality between attribute sets or lists
- that would have to be a deep equality test to be sound. */
AFun sym1 = ATgetAFun(e1);
AFun sym2 = ATgetAFun(e2);
if (sym1 != sym2) return false;
/* Functions are incomparable. */
if (sym1 == symFunction || sym1 == symPrimOp) return false;
if (!state.allowUnsafeEquality && sym1 == symAttrs)
throw EvalError("comparison of attribute sets is not implemented");
/* !!! This allows comparisons of infinite data structures to
succeed, such as `let x = [x]; in x == x'. This is
undesirable, since equivalent (?) terms such as `let x = [x]; y
= [y]; in x == y' don't terminate. */
if (e1 == e2) return true;
if (sym1 == symList) {
ATermList es1; matchList(e1, es1);
ATermList es2; matchList(e2, es2);
if (ATgetLength(es1) != ATgetLength(es2)) return false;
ATermIterator i(es1), j(es2);
while (*i) {
if (!areEqual(state, *i, *j)) return false;
++i; ++j;
}
return true;
}
return false;
}
static char * deepestStack = (char *) -1; /* for measuring stack usage */
@@ -707,12 +762,10 @@ Expr evalExpr2(EvalState & state, Expr e)
However, we don't want to make (==) strict, because that would
make operations like `big_derivation == null' very slow (unless
we were to evaluate them side-by-side). */
if (matchOpEq(e, e1, e2))
return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
if (matchOpNEq(e, e1, e2))
return makeBool(evalExpr(state, e1) != evalExpr(state, e2));
if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2));
if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2));
/* Negation. */
if (matchOpNot(e, e1))
return makeBool(!evalBool(state, e1));
@@ -760,11 +813,19 @@ Expr evalExpr(EvalState & state, Expr e)
format("evaluating expression: %1%") % e);
#endif
state.nrEvaluated++;
const unsigned int hugeEvalExpr = 100;
unsigned int nrEvaluated = state.nrEvaluated++;
state.nrDepthAfterReset++;
/* Consult the memo table to quickly get the normal form of
previously evaluated expressions. */
Expr nf = state.normalForms.get(e);
if (nf) {
state.nrCached++;
return nf;
}
nf = state.sessionNormalForms.get(e);
if (nf) {
if (nf == makeBlackHole())
throwEvalError("infinite recursion encountered");
@@ -773,14 +834,25 @@ Expr evalExpr(EvalState & state, Expr e)
}
/* Otherwise, evaluate and memoize. */
state.normalForms.set(e, makeBlackHole());
state.sessionNormalForms.set(e, makeBlackHole());
try {
nf = evalExpr2(state, e);
} catch (Error & err) {
state.normalForms.remove(e);
state.sessionNormalForms.remove(e);
throw;
}
state.normalForms.set(e, nf);
// session independent condition
if (state.nrDepthAfterReset && state.safeCache &&
// heuristic condition
state.nrEvaluated - nrEvaluated > hugeEvalExpr) {
state.sessionNormalForms.remove(e);
state.normalForms.set(e, nf);
state.nrDepthAfterReset--;
} else {
state.sessionNormalForms.set(e, nf);
}
return nf;
}
@@ -788,6 +860,7 @@ Expr evalExpr(EvalState & state, Expr e)
Expr evalFile(EvalState & state, const Path & path)
{
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
state.nrDepthAfterReset = 0;
Expr e = parseExprFromFile(state, path);
try {
return evalExpr(state, e);
@@ -859,11 +932,13 @@ void printEvalStats(EvalState & state)
char x;
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
printMsg(showStats ? lvlInfo : lvlDebug,
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space, %6% cached normal reduction, %7% session dependent reduction.")
% state.nrEvaluated % state.nrCached
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
% AT_calcAllocatedSize()
% (&x - deepestStack));
% (&x - deepestStack)
% state.normalForms.size()
% state.sessionNormalForms.size());
if (showStats)
printATermMapStats();
}

View File

@@ -30,6 +30,7 @@ typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
struct EvalState
{
ATermMap normalForms;
ATermMap sessionNormalForms;
ATermMap primOps;
DrvRoots drvRoots;
DrvHashes drvHashes; /* normalised derivation hashes */
@@ -37,12 +38,19 @@ struct EvalState
unsigned int nrEvaluated;
unsigned int nrCached;
unsigned int nrDepthAfterReset;
bool allowUnsafeEquality;
bool safeCache;
EvalState();
~EvalState();
void addPrimOps();
void addPrimOp(const string & name,
unsigned int arity, PrimOp primOp);
void loadNormalForms();
void saveNormalForms();
};

View File

@@ -50,31 +50,55 @@ MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
Expr e = evalExpr(state, i->value);
string s;
PathSet context;
if (matchStr(e, s, context))
meta[aterm2String(i->key)] = s;
/* For future compatibility, ignore attribute values that are
not strings. */
MetaValue value;
int n;
ATermList es;
if (matchStr(e, s, context)) {
value.type = MetaValue::tpString;
value.stringValue = s;
meta[aterm2String(i->key)] = value;
} else if (matchInt(e, n)) {
value.type = MetaValue::tpInt;
value.intValue = n;
meta[aterm2String(i->key)] = value;
} else if (matchList(e, es)) {
value.type = MetaValue::tpStrings;
for (ATermIterator j(es); j; ++j)
value.stringValues.push_back(evalStringNoCtx(state, *j));
meta[aterm2String(i->key)] = value;
}
}
return meta;
}
string DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
{
/* !!! evaluates all meta attributes => inefficient */
MetaInfo meta = queryMetaInfo(state);
MetaInfo::iterator i = meta.find(name);
return i == meta.end() ? "" : i->second;
return queryMetaInfo(state)[name];
}
void DrvInfo::setMetaInfo(const MetaInfo & meta)
{
ATermMap metaAttrs;
for (MetaInfo::const_iterator i = meta.begin(); i != meta.end(); ++i)
metaAttrs.set(toATerm(i->first),
makeAttrRHS(makeStr(i->second), makeNoPos()));
foreach (MetaInfo::const_iterator, i, meta) {
Expr e;
switch (i->second.type) {
case MetaValue::tpInt: e = makeInt(i->second.intValue); break;
case MetaValue::tpString: e = makeStr(i->second.stringValue); break;
case MetaValue::tpStrings: {
ATermList es = ATempty;
foreach (Strings::const_iterator, j, i->second.stringValues)
es = ATinsert(es, makeStr(*j));
e = makeList(ATreverse(es));
break;
}
default: abort();
}
metaAttrs.set(toATerm(i->first), makeAttrRHS(e, makeNoPos()));
}
attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
}

View File

@@ -12,7 +12,16 @@
namespace nix {
typedef std::map<string, string> MetaInfo;
struct MetaValue
{
enum { tpNone, tpString, tpStrings, tpInt } type;
string stringValue;
Strings stringValues;
int intValue;
};
typedef std::map<string, MetaValue> MetaInfo;
struct DrvInfo
@@ -34,7 +43,7 @@ public:
string queryDrvPath(EvalState & state) const;
string queryOutPath(EvalState & state) const;
MetaInfo queryMetaInfo(EvalState & state) const;
string queryMetaInfo(EvalState & state, const string & name) const;
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
void setDrvPath(const string & s)
{

View File

@@ -70,6 +70,7 @@ Bool | ATermBool | Expr |
Null | | Expr |
Bind | string Expr Pos | ATerm |
BindAttrPath | ATermList Expr Pos | ATerm | # desugared during parsing
Bind | string Expr | ATerm | ObsoleteBind
Inherit | Expr ATermList Pos | ATerm |

View File

@@ -19,7 +19,7 @@ string showPos(ATerm pos)
if (matchNoPos(pos)) return "undefined position";
if (!matchPos(pos, path, line, column))
throw badTerm("position expected", pos);
return (format("`%1%', line %2%") % aterm2String(path) % line).str();
return (format("`%1%:%2%:%3%'") % aterm2String(path) % line % column).str();
}

View File

@@ -11,6 +11,7 @@ namespace nix {
MakeError(EvalError, Error)
MakeError(ParseError, Error)
MakeError(AssertionError, EvalError)
MakeError(ThrownError, AssertionError)
MakeError(Abort, EvalError)

View File

@@ -0,0 +1,64 @@
#include "eval.hh"
#include "util.hh"
#include "globals.hh"
#include "serialise.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nix {
void EvalState::loadNormalForms()
{
// nixCacheFile = getEnv("NIX_CACHE_FILE", nixStateDir + "/reduce-cache");
nixCacheFile = getEnv("NIX_CACHE_FILE");
string loadFlag = getEnv("NIX_CACHE_FILE_LOAD", "1");
if(nixCacheFile == "" || loadFlag == "")
return;
if(!pathExists(nixCacheFile))
return;
printMsg(lvlInfo, format("Load cache: ..."));
try {
int fd = open(nixCacheFile.c_str(), O_RDONLY);
if (fd == -1)
throw SysError(format("opening file `%1%'") % nixCacheFile);
AutoCloseFD auto_fd = fd;
FdSource source = fd;
normalForms = readATermMap(source);
} catch (Error & e) {
e.addPrefix(format("Cannot load cached reduce operations from %1%:\n") % nixCacheFile);
throw;
}
printMsg(lvlInfo, format("Load cache: end"));
}
void EvalState::saveNormalForms()
{
string saveFlag = getEnv("NIX_CACHE_FILE_SAVE", "");
if(nixCacheFile == "" || saveFlag == "")
return;
printMsg(lvlInfo, format("Save cache: ..."));
try {
int fd = open(nixCacheFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (fd == -1)
throw SysError(format("opening file `%1%'") % nixCacheFile);
AutoCloseFD auto_fd = fd;
FdSink sink = fd;
writeATermMap(normalForms, sink);
} catch (Error & e) {
e.addPrefix(format("Cannot save cached reduce operations to %1%:\n") % nixCacheFile);
throw;
}
printMsg(lvlInfo, format("Save cache: end"));
}
}

View File

@@ -25,6 +25,7 @@
#include "parser-tab.hh"
#include "lexer-tab.hh"
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
#include "nixexpr.hh"
#include "nixexpr-ast.hh"
@@ -43,28 +44,128 @@ struct ParseData
Path path;
string error;
};
static Expr fixAttrs(int recursive, ATermList as)
static string showAttrPath(ATermList attrPath)
{
ATermList bs = ATempty, cs = ATempty;
ATermList * is = recursive ? &cs : &bs;
string s;
for (ATermIterator i(attrPath); i; ++i) {
if (!s.empty()) s += '.';
s += aterm2String(*i);
}
return s;
}
struct Tree
{
Expr leaf; ATerm pos; bool recursive;
typedef std::map<ATerm, Tree> Children;
Children children;
Tree() { leaf = 0; recursive = true; }
};
static ATermList buildAttrs(const Tree & t, ATermList & nonrec)
{
ATermList res = ATempty;
for (Tree::Children::const_reverse_iterator i = t.children.rbegin();
i != t.children.rend(); ++i)
if (!i->second.recursive)
nonrec = ATinsert(nonrec, makeBind(i->first, i->second.leaf, i->second.pos));
else
res = ATinsert(res, i->second.leaf
? makeBind(i->first, i->second.leaf, i->second.pos)
: makeBind(i->first, makeAttrs(buildAttrs(i->second, nonrec)), makeNoPos()));
return res;
}
static Expr fixAttrs(bool recursive, ATermList as)
{
Tree attrs;
for (ATermIterator i(as); i; ++i) {
ATermList names;
Expr src;
ATerm pos;
ATermList names, attrPath; Expr src, e; ATerm name, pos;
if (matchInherit(*i, src, names, pos)) {
bool fromScope = matchScope(src);
for (ATermIterator j(names); j; ++j) {
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
*is = ATinsert(*is, makeBind(*j, rhs, pos));
if (attrs.children.find(*j) != attrs.children.end())
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
% showAttrPath(ATmakeList1(*j)) % showPos(pos));
Tree & t(attrs.children[*j]);
t.leaf = fromScope ? makeVar(*j) : makeSelect(src, *j);
t.pos = pos;
if (recursive && fromScope) t.recursive = false;
}
} else bs = ATinsert(bs, *i);
}
else if (matchBindAttrPath(*i, attrPath, e, pos)) {
Tree * t(&attrs);
for (ATermIterator j(attrPath); j; ) {
name = *j; ++j;
if (t->leaf) throw ParseError(format("attribute set containing `%1%' at %2% already defined at %3%")
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
t = &(t->children[name]);
}
if (t->leaf)
throw ParseError(format("duplicate definition of attribute `%1%' at %2% and %3%")
% showAttrPath(attrPath) % showPos(pos) % showPos (t->pos));
if (!t->children.empty())
throw ParseError(format("duplicate definition of attribute `%1%' at %2%")
% showAttrPath(attrPath) % showPos(pos));
t->leaf = e; t->pos = pos;
}
else abort(); /* can't happen */
}
if (recursive)
return makeRec(bs, cs);
else
return makeAttrs(bs);
ATermList nonrec = ATempty;
ATermList rec = buildAttrs(attrs, nonrec);
return recursive ? makeRec(rec, nonrec) : makeAttrs(rec);
}
static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
{
ATerm name;
ATermList formals;
Pattern pat1, pat2;
ATermBool ellipsis;
if (matchVarPat(pat, name)) {
if (map.get(name))
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
map.set(name, name);
}
else if (matchAttrsPat(pat, formals, ellipsis)) {
for (ATermIterator i(formals); i; ++i) {
ATerm d1;
if (!matchFormal(*i, name, d1)) abort();
if (map.get(name))
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
map.set(name, name);
}
}
else if (matchAtPat(pat, pat1, pat2)) {
checkPatternVars(pos, map, pat1);
checkPatternVars(pos, map, pat2);
}
else abort();
}
static void checkPatternVars(ATerm pos, Pattern pat)
{
ATermMap map;
checkPatternVars(pos, map, pat);
}
@@ -146,7 +247,7 @@ static Expr stripIndentation(ATermList es)
/* Remove the last line if it is empty and consists only of
spaces. */
if (n == 1) {
unsigned int p = s2.find_last_of('\n');
string::size_type p = s2.find_last_of('\n');
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
s2 = string(s2, 0, p + 1);
}
@@ -217,7 +318,7 @@ static void freeAndUnprotect(void * p)
%type <t> start expr expr_function expr_if expr_op
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
%type <t> pattern pattern2
%type <ts> binds ids expr_list string_parts ind_string_parts
%type <ts> binds ids attrpath expr_list string_parts ind_string_parts
%type <formals> formals
%token <t> ID INT STR IND_STR PATH URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
@@ -244,13 +345,13 @@ expr: expr_function;
expr_function
: pattern ':' expr_function
{ $$ = makeFunction($1, $3, CUR_POS); }
{ checkPatternVars(CUR_POS, $1); $$ = makeFunction($1, $3, CUR_POS); }
| ASSERT expr ';' expr_function
{ $$ = makeAssert($2, $4, CUR_POS); }
| WITH expr ';' expr_function
{ $$ = makeWith($2, $4, CUR_POS); }
| LET binds IN expr_function
{ $$ = makeSelect(fixAttrs(1, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); }
{ $$ = makeSelect(fixAttrs(true, ATinsert($2, makeBindAttrPath(ATmakeList1(toATerm("<let-body>")), $4, CUR_POS))), toATerm("<let-body>")); }
| expr_if
;
@@ -305,12 +406,12 @@ expr_simple
/* Let expressions `let {..., body = ...}' are just desugared
into `(rec {..., body = ...}).body'. */
| LET '{' binds '}'
{ $$ = makeSelect(fixAttrs(1, $3), toATerm("body")); }
{ $$ = makeSelect(fixAttrs(true, $3), toATerm("body")); }
| REC '{' binds '}'
{ $$ = fixAttrs(1, $3); }
{ $$ = fixAttrs(true, $3); }
| '{' binds '}'
{ $$ = fixAttrs(0, $2); }
| '[' expr_list ']' { $$ = makeList($2); }
{ $$ = fixAttrs(false, $2); }
| '[' expr_list ']' { $$ = makeList(ATreverse($2)); }
;
string_parts
@@ -341,8 +442,8 @@ binds
;
bind
: ID '=' expr ';'
{ $$ = makeBind($1, $3, CUR_POS); }
: attrpath '=' expr ';'
{ $$ = makeBindAttrPath(ATreverse($1), $3, CUR_POS); }
| INHERIT inheritsrc ids ';'
{ $$ = makeInherit($2, $3, CUR_POS); }
;
@@ -354,16 +455,18 @@ inheritsrc
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
attrpath
: attrpath '.' ID { $$ = ATinsert($1, $3); }
| ID { $$ = ATmakeList1($1); }
;
expr_list
: expr_select expr_list { $$ = ATinsert($2, $1); }
/* yes, this is right-recursive, but it doesn't matter since
otherwise we would need ATreverse which requires unbounded
stack space */
: expr_list expr_select { $$ = ATinsert($1, $2); }
| { $$ = ATempty; }
;
formals
: formal ',' formals /* idem - right recursive */
: formal ',' formals /* !!! right recursive */
{ $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; }
| formal
{ $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; }
@@ -392,84 +495,6 @@ formal
namespace nix {
static void checkAttrs(ATermMap & names, ATermList bnds)
{
for (ATermIterator i(bnds); i; ++i) {
ATerm name;
Expr e;
ATerm pos;
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
if (names.get(name))
throw EvalError(format("duplicate attribute `%1%' at %2%")
% aterm2String(name) % showPos(pos));
names.set(name, name);
}
}
static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
{
ATerm name;
ATermList formals;
Pattern pat1, pat2;
ATermBool ellipsis;
if (matchVarPat(pat, name)) {
if (map.get(name))
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
map.set(name, name);
}
else if (matchAttrsPat(pat, formals, ellipsis)) {
for (ATermIterator i(formals); i; ++i) {
ATerm d1;
if (!matchFormal(*i, name, d1)) abort();
if (map.get(name))
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
% aterm2String(name) % showPos(pos));
map.set(name, name);
}
}
else if (matchAtPat(pat, pat1, pat2)) {
checkPatternVars(pos, map, pat1);
checkPatternVars(pos, map, pat2);
}
else abort();
}
static void checkAttrSets(ATerm e)
{
ATerm pat, body, pos;
if (matchFunction(e, pat, body, pos)) {
ATermMap map(16);
checkPatternVars(pos, map, pat);
}
ATermList bnds;
if (matchAttrs(e, bnds)) {
ATermMap names(ATgetLength(bnds));
checkAttrs(names, bnds);
}
ATermList rbnds, nrbnds;
if (matchRec(e, rbnds, nrbnds)) {
ATermMap names(ATgetLength(rbnds) + ATgetLength(nrbnds));
checkAttrs(names, rbnds);
checkAttrs(names, nrbnds);
}
if (ATgetType(e) == AT_APPL) {
int arity = ATgetArity(ATgetAFun(e));
for (int i = 0; i < arity; ++i)
checkAttrSets(ATgetArgument(e, i));
}
else if (ATgetType(e) == AT_LIST)
for (ATermIterator i((ATermList) e); i; ++i)
checkAttrSets(*i);
}
static Expr parse(EvalState & state,
const char * text, const Path & path,
const Path & basePath)
@@ -484,16 +509,14 @@ static Expr parse(EvalState & state,
int res = yyparse(scanner, &data);
yylex_destroy(scanner);
if (res) throw EvalError(data.error);
if (res) throw ParseError(data.error);
try {
checkVarDefs(state.primOps, data.result);
} catch (Error & e) {
throw EvalError(format("%1%, in `%2%'") % e.msg() % path);
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
}
checkAttrSets(data.result);
return data.result;
}

View File

@@ -74,12 +74,14 @@ static Expr prim_null(EvalState & state, const ATermVector & args)
platforms. */
static Expr prim_currentSystem(EvalState & state, const ATermVector & args)
{
state.nrDepthAfterReset = 0;
return makeStr(thisSystem);
}
static Expr prim_currentTime(EvalState & state, const ATermVector & args)
{
state.nrDepthAfterReset = 0;
return ATmake("Int(<int>)", time(0));
}
@@ -125,6 +127,27 @@ static Expr prim_isFunction(EvalState & state, const ATermVector & args)
return makeBool(matchFunction(e, pat, body, pos));
}
/* Determine whether the argument is an Int. */
static Expr prim_isInt(EvalState & state, const ATermVector & args)
{
int i;
return makeBool(matchInt(evalExpr(state, args[0]), i));
}
/* Determine whether the argument is an String. */
static Expr prim_isString(EvalState & state, const ATermVector & args)
{
string s;
PathSet l;
return makeBool(matchStr(evalExpr(state, args[0]), s, l));
}
/* Determine whether the argument is an Bool. */
static Expr prim_isBool(EvalState & state, const ATermVector & args)
{
ATermBool b;
return makeBool(matchBool(evalExpr(state, args[0]), b));
}
static Expr prim_genericClosure(EvalState & state, const ATermVector & args)
{
@@ -185,15 +208,29 @@ static Expr prim_abort(EvalState & state, const ATermVector & args)
static Expr prim_throw(EvalState & state, const ATermVector & args)
{
PathSet context;
throw ThrownError(format("user-thrown exception: `%1%'") %
throw ThrownError(format("user-thrown exception: %1%") %
evalString(state, args[0], context));
}
static Expr prim_addErrorContext(EvalState & state, const ATermVector & args)
{
PathSet context;
try {
return evalExpr(state, args[1]);
} catch (Error & e) {
e.addPrefix(format("%1%\n") %
evalString(state, args[0], context));
throw;
}
}
/* Return an environment variable. Use with care. */
static Expr prim_getEnv(EvalState & state, const ATermVector & args)
{
string name = evalStringNoCtx(state, args[0]);
state.nrDepthAfterReset = 0;
return makeStr(getEnv(name));
}
@@ -215,6 +252,14 @@ static Expr prim_trace(EvalState & state, const ATermVector & args)
*************************************************************/
static bool isFixedOutput(const Derivation & drv)
{
return drv.outputs.size() == 1 &&
drv.outputs.begin()->first == "out" &&
drv.outputs.begin()->second.hash != "";
}
/* Returns the hash of a derivation modulo fixed-output
subderivations. A fixed-output derivation is a derivation with one
output (`out') for which an expected hash and hash algorithm are
@@ -227,35 +272,29 @@ static Expr prim_trace(EvalState & state, const ATermVector & args)
function, we do not want to rebuild everything depending on it
(after all, (the hash of) the file being downloaded is unchanged).
So the *output paths* should not change. On the other hand, the
*derivation store expression paths* should change to reflect the
new dependency graph.
*derivation paths* should change to reflect the new dependency
graph.
That's what this function does: it returns a hash which is just the
of the derivation ATerm, except that any input store expression
hash of the derivation ATerm, except that any input derivation
paths have been replaced by the result of a recursive call to this
function, and that for fixed-output derivations we return
(basically) its outputHash. */
function, and that for fixed-output derivations we return a hash of
its output path. */
static Hash hashDerivationModulo(EvalState & state, Derivation drv)
{
/* Return a fixed hash for fixed-output derivations. */
if (drv.outputs.size() == 1) {
if (isFixedOutput(drv)) {
DerivationOutputs::const_iterator i = drv.outputs.begin();
if (i->first == "out" &&
i->second.hash != "")
{
return hashString(htSHA256, "fixed:out:"
+ i->second.hashAlgo + ":"
+ i->second.hash + ":"
+ i->second.path);
}
return hashString(htSHA256, "fixed:out:"
+ i->second.hashAlgo + ":"
+ i->second.hash + ":"
+ i->second.path);
}
/* For other derivations, replace the inputs paths with recursive
calls to this function.*/
DerivationInputs inputs2;
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i)
{
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
Hash h = state.drvHashes[i->first];
if (h.type == htUnknown) {
Derivation drv2 = derivationFromPath(i->first);
@@ -280,6 +319,7 @@ static Hash hashDerivationModulo(EvalState & state, Derivation drv)
static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
{
startNest(nest, lvlVomit, "evaluating derivation");
state.nrDepthAfterReset = 0;
ATermMap attrs;
queryAllAttrs(evalExpr(state, args[0]), attrs, true);
@@ -304,8 +344,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
PathSet context;
string outputHash;
string outputHashAlgo;
string outputHash, outputHashAlgo;
bool outputHashRecursive = false;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
@@ -364,13 +403,32 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
/* Everything in the context of the strings in the derivation
attributes should be added as dependencies of the resulting
derivation. */
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
debug(format("derivation uses `%1%'") % *i);
assert(isStorePath(*i));
if (isDerivation(*i))
drv.inputDrvs[*i] = singleton<StringSet>("out");
foreach (PathSet::iterator, i, context) {
Path path = *i;
/* Paths marked with `=' denote that the path of a derivation
is explicitly passed to the builder. Since that allows the
builder to gain access to every path in the dependency
graph of the derivation (including all outputs), all paths
in the graph must be added to this derivation's list of
inputs to ensure that they are available when the builder
runs. */
if (path.at(0) == '=') {
path = string(path, 1);
PathSet refs; computeFSClosure(path, refs);
foreach (PathSet::iterator, j, refs) {
drv.inputSrcs.insert(*j);
if (isDerivation(*j))
drv.inputDrvs[*j] = singleton<StringSet>("out");
}
}
debug(format("derivation uses `%1%'") % path);
assert(isStorePath(path));
if (isDerivation(path))
drv.inputDrvs[path] = singleton<StringSet>("out");
else
drv.inputSrcs.insert(*i);
drv.inputSrcs.insert(path);
}
/* Do we have all required attributes? */
@@ -380,6 +438,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
throw EvalError("required attribute `system' missing");
/* If an output hash was given, check it. */
Path outPath;
if (outputHash == "")
outputHashAlgo = "";
else {
@@ -398,6 +457,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
% outputHash % outputHashAlgo);
string s = outputHash;
outputHash = printHash(h);
outPath = makeFixedOutputPath(outputHashRecursive, ht, h, drvName);
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
}
@@ -413,13 +473,12 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
have an empty value. This ensures that changes in the set of
output names do get reflected in the hash. */
drv.env["out"] = "";
drv.outputs["out"] =
DerivationOutput("", outputHashAlgo, outputHash);
drv.outputs["out"] = DerivationOutput("", outputHashAlgo, outputHash);
/* Use the masked derivation expression to compute the output
path. */
Path outPath = makeStorePath("output:out",
hashDerivationModulo(state, drv), drvName);
if (outPath == "")
outPath = makeStorePath("output:out", hashDerivationModulo(state, drv), drvName);
/* Construct the final derivation store expression. */
drv.env["out"] = outPath;
@@ -442,7 +501,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
outAttrs.set(toATerm("outPath"),
makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
outAttrs.set(toATerm("drvPath"),
makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
makeAttrRHS(makeStr(drvPath, singleton<PathSet>("=" + drvPath)), makeNoPos()));
return makeAttrs(outAttrs);
}
@@ -494,11 +553,13 @@ static Expr prim_storePath(EvalState & state, const ATermVector & args)
{
PathSet context;
Path path = canonPath(coerceToPath(state, args[0], context));
state.nrDepthAfterReset = 0;
if (!isInStore(path))
throw EvalError(format("path `%1%' is not in the Nix store") % path);
if (!store->isValidPath(path))
throw EvalError(format("store path `%1%' is not valid") % path);
context.insert(toStorePath(path));
Path path2 = toStorePath(path);
if (!store->isValidPath(path2))
throw EvalError(format("store path `%1%' is not valid") % path2);
context.insert(path2);
return makeStr(path, context);
}
@@ -509,6 +570,7 @@ static Expr prim_pathExists(EvalState & state, const ATermVector & args)
Path path = coerceToPath(state, args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
state.nrDepthAfterReset = 0;
return makeBool(pathExists(path));
}
@@ -542,6 +604,7 @@ static Expr prim_readFile(EvalState & state, const ATermVector & args)
Path path = coerceToPath(state, args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
state.nrDepthAfterReset = 0;
return makeStr(readFile(path));
}
@@ -574,11 +637,14 @@ static Expr prim_toFile(EvalState & state, const ATermVector & args)
PathSet refs;
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
if (isDerivation(*i))
Path path = *i;
if (path.at(0) == '=') path = string(path, 1);
if (isDerivation(path))
throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name);
refs.insert(*i);
refs.insert(path);
}
state.nrDepthAfterReset = 0;
Path storePath = readOnlyMode
? computeStorePathForText(name, contents, refs)
: store->addTextToStore(name, contents, refs);
@@ -631,9 +697,10 @@ static Expr prim_filterSource(EvalState & state, const ATermVector & args)
FilterFromExpr filter(state, args[0]);
state.nrDepthAfterReset = 0;
Path dstPath = readOnlyMode
? computeStorePathForPath(path, false, false, "", filter).first
: store->addToStore(path, false, false, "", filter);
? computeStorePathForPath(path, true, htSHA256, filter).first
: store->addToStore(path, true, htSHA256, filter);
return makeStr(dstPath, singleton<PathSet>(dstPath));
}
@@ -955,11 +1022,16 @@ void EvalState::addPrimOps()
addPrimOp("import", 1, prim_import);
addPrimOp("isNull", 1, prim_isNull);
addPrimOp("__isFunction", 1, prim_isFunction);
addPrimOp("__isString", 1, prim_isString);
addPrimOp("__isInt", 1, prim_isInt);
addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort);
addPrimOp("throw", 1, prim_throw);
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
addPrimOp("__getEnv", 1, prim_getEnv);
addPrimOp("__trace", 2, prim_trace);
// Expr <-> String
addPrimOp("__exprToString", 1, prim_exprToString);

View File

@@ -1,6 +1,10 @@
pkglib_LTLIBRARIES = libmain.la
libmain_la_SOURCES = shared.cc shared.hh
libmain_la_SOURCES = shared.cc
libmain_la_LIBADD = ../libstore/libstore.la
pkginclude_HEADERS = shared.hh
AM_CXXFLAGS = \
-DNIX_STORE_DIR=\"$(storedir)\" \

View File

@@ -125,6 +125,9 @@ RemoveTempRoots::~RemoveTempRoots()
}
static bool showTrace = false;
/* Initialize and reorder arguments, then call the actual argument
processor. */
static void initAndRun(int argc, char * * argv)
@@ -138,7 +141,6 @@ static void initAndRun(int argc, char * * argv)
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
nixChrootsDir = canonPath(getEnv("NIX_CHROOTS_DIR", nixStateDir + "/chroots"));
string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") {
@@ -244,6 +246,15 @@ static void initAndRun(int argc, char * * argv)
maxSilentTime = getIntArg(arg, i, args.end());
else if (arg == "--no-build-hook")
useBuildHook = false;
else if (arg == "--show-trace")
showTrace = true;
else if (arg == "--option") {
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
string name = *i;
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
string value = *i;
overrideSetting(name, tokenizeString(value));
}
else remaining.push_back(arg);
}
@@ -359,7 +370,9 @@ int main(int argc, char * * argv)
% e.what() % programId);
return 1;
} catch (BaseError & e) {
printMsg(lvlError, format("error: %1%") % e.msg());
printMsg(lvlError, format("error: %1%%2%") % (showTrace ? e.prefix() : "") % e.msg());
if (e.prefix() != "" && !showTrace)
printMsg(lvlError, "(use `--show-trace' to show detailed location information)");
return 1;
} catch (std::exception & e) {
printMsg(lvlError, format("error: %1%") % e.what());

File diff suppressed because it is too large Load Diff

View File

@@ -21,8 +21,7 @@ Path writeDerivation(const Derivation & drv, const string & name)
{
PathSet references;
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
for (DerivationInputs::const_iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i)
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
references.insert(i->first);
/* Note that the outputs of a derivation are *not* references
(that can be missing (of course) and should not necessarily be

View File

@@ -247,9 +247,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
Strings tempRootFiles = readDirectory(
(format("%1%/%2%") % nixStateDir % tempRootsDir).str());
for (Strings::iterator i = tempRootFiles.begin();
i != tempRootFiles.end(); ++i)
{
foreach (Strings::iterator, i, tempRootFiles) {
Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
debug(format("reading temporary root file `%1%'") % path);
@@ -328,7 +326,7 @@ static void findRoots(const Path & path, bool recurseSymlinks,
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
foreach (Strings::iterator, i, names)
findRoots(path + "/" + *i, recurseSymlinks, deleteStale, roots);
}
@@ -399,7 +397,7 @@ static void addAdditionalRoots(PathSet & roots)
Strings paths = tokenizeString(result, "\n");
for (Strings::iterator i = paths.begin(); i != paths.end(); ++i) {
foreach (Strings::iterator, i, paths) {
if (isInStore(*i)) {
Path path = toStorePath(*i);
if (roots.find(path) == roots.end() && store->isValidPath(path)) {
@@ -421,8 +419,7 @@ static void dfsVisit(const PathSet & paths, const Path & path,
if (store->isValidPath(path))
store->queryReferences(path, references);
for (PathSet::iterator i = references.begin();
i != references.end(); ++i)
foreach (PathSet::iterator, i, references)
/* Don't traverse into paths that don't exist. That can
happen due to substitutes for non-existent paths. */
if (*i != path && paths.find(*i) != paths.end())
@@ -436,7 +433,7 @@ Paths topoSortPaths(const PathSet & paths)
{
Paths sorted;
PathSet visited;
for (PathSet::const_iterator i = paths.begin(); i != paths.end(); ++i)
foreach (PathSet::const_iterator, i, paths)
dfsVisit(paths, *i, visited, sorted);
return sorted;
}
@@ -453,7 +450,7 @@ static time_t lastFileAccessTime(const Path & path)
if (S_ISDIR(st.st_mode)) {
time_t last = 0;
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i) {
foreach (Strings::iterator, i, names) {
time_t t = lastFileAccessTime(path + "/" + *i);
if (t > last) last = t;
}
@@ -476,30 +473,13 @@ void LocalStore::gcPath(const GCOptions & options, GCResults & results,
if (!pathExists(path)) return;
#ifndef __CYGWIN__
AutoCloseFD fdLock;
/* Only delete a lock file if we can acquire a write lock on it.
That means that it's either stale, or the process that created
it hasn't locked it yet. In the latter case the other process
will detect that we deleted the lock, and retry (see
pathlocks.cc). */
if (path.size() >= 5 && string(path, path.size() - 5) == ".lock") {
fdLock = openLockFile(path, false);
if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
debug(format("skipping active lock `%1%'") % path);
return;
}
}
#endif
/* Okay, it's safe to delete. */
unsigned long long bytesFreed, blocksFreed;
deleteFromStore(path, bytesFreed, blocksFreed);
results.bytesFreed += bytesFreed;
results.blocksFreed += blocksFreed;
if (results.bytesFreed > options.maxFreed) {
if (options.maxFreed && results.bytesFreed > options.maxFreed) {
printMsg(lvlInfo, format("deleted more than %1% bytes; stopping") % options.maxFreed);
throw GCLimitReached();
}
@@ -513,12 +493,6 @@ void LocalStore::gcPath(const GCOptions & options, GCResults & results,
throw GCLimitReached();
}
}
#ifndef __CYGWIN__
if (fdLock != -1)
/* Write token to stale (deleted) lock file. */
writeFull(fdLock, (const unsigned char *) "d", 1);
#endif
}
@@ -569,7 +543,7 @@ struct CachingAtimeComparator : public std::binary_function<Path, Path, bool>
};
string showTime(const string & format, time_t t)
static string showTime(const string & format, time_t t)
{
char s[128];
strftime(s, sizeof s, format.c_str(), localtime(&t));
@@ -577,6 +551,26 @@ string showTime(const string & format, time_t t)
}
static bool isLive(const Path & path, const PathSet & livePaths,
const PathSet & tempRoots, const PathSet & tempRootsClosed)
{
if (livePaths.find(path) != livePaths.end() ||
tempRootsClosed.find(path) != tempRootsClosed.end()) return true;
/* A lock file belonging to a path that we're building right
now isn't garbage. */
if (hasSuffix(path, ".lock") && tempRoots.find(string(path, 0, path.size() - 5)) != tempRoots.end())
return true;
/* Don't delete .chroot directories for derivations that are
currently being built. */
if (hasSuffix(path, ".chroot") && tempRoots.find(string(path, 0, path.size() - 7)) != tempRoots.end())
return true;
return false;
}
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
{
bool gcKeepOutputs =
@@ -597,8 +591,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(true);
PathSet roots;
for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i)
roots.insert(i->second);
foreach (Roots::iterator, i, rootMap) roots.insert(i->second);
/* Add additional roots returned by the program specified by the
NIX_ROOT_FINDER environment variable. This is typically used
@@ -616,13 +609,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
roots under the `references' relation. */
printMsg(lvlError, format("computing live paths..."));
PathSet livePaths;
for (PathSet::const_iterator i = roots.begin(); i != roots.end(); ++i)
foreach (PathSet::const_iterator, i, roots)
computeFSClosure(canonPath(*i), livePaths);
if (gcKeepDerivations) {
for (PathSet::iterator i = livePaths.begin();
i != livePaths.end(); ++i)
{
foreach (PathSet::iterator, i, livePaths) {
/* Note that the deriver need not be valid (e.g., if we
previously ran the collector with `gcKeepDerivations'
turned off). */
@@ -634,8 +625,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (gcKeepOutputs) {
/* Hmz, identical to storePathRequisites in nix-store. */
for (PathSet::iterator i = livePaths.begin();
i != livePaths.end(); ++i)
foreach (PathSet::iterator, i, livePaths)
if (isDerivation(*i)) {
Derivation drv = derivationFromPath(*i);
@@ -645,8 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
gcLevel = defaultGcLevel;
if (gcLevel >= gcKeepOutputsThreshold)
for (DerivationOutputs::iterator j = drv.outputs.begin();
j != drv.outputs.end(); ++j)
foreach (DerivationOutputs::iterator, j, drv.outputs)
if (isValidPath(j->second.path))
computeFSClosure(j->second.path, livePaths);
}
@@ -671,7 +660,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
(and computeFSClosure() assumes that the presence of a path
means that it has already been closed). */
PathSet tempRootsClosed;
for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i)
foreach (PathSet::iterator, i, tempRoots)
if (isValidPath(*i))
computeFSClosure(*i, tempRootsClosed);
else
@@ -691,9 +680,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
Paths entries = readDirectory(nixStore);
foreach (Paths::iterator, i, entries) {
Path path = canonPath(nixStore + "/" + *i);
if (livePaths.find(path) == livePaths.end() &&
tempRootsClosed.find(path) == tempRootsClosed.end())
storePaths.insert(path);
if (!isLive(path, livePaths, tempRoots, tempRootsClosed)) storePaths.insert(path);
}
}
@@ -701,10 +688,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
foreach (PathSet::iterator, i, options.pathsToDelete) {
assertStorePath(*i);
storePaths.insert(*i);
if (livePaths.find(*i) != livePaths.end())
if (isLive(*i, livePaths, tempRoots, tempRootsClosed))
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
if (tempRootsClosed.find(*i) != tempRootsClosed.end())
throw Error(format("cannot delete path `%1%' since it is temporarily in use") % *i);
}
}

View File

@@ -13,10 +13,10 @@ string nixDataDir = "/UNINIT";
string nixLogDir = "/UNINIT";
string nixStateDir = "/UNINIT";
string nixDBPath = "/UNINIT";
string nixCacheFile = "/UNINIT";
string nixConfDir = "/UNINIT";
string nixLibexecDir = "/UNINIT";
string nixBinDir = "/UNINIT";
string nixChrootsDir = "/UNINIT";
bool keepFailed = false;
bool keepGoing = false;
@@ -25,7 +25,7 @@ Verbosity buildVerbosity = lvlInfo;
unsigned int maxBuildJobs = 1;
bool readOnlyMode = false;
string thisSystem = "unset";
unsigned int maxSilentTime = 0;
time_t maxSilentTime = 0;
Paths substituters;
bool useBuildHook = true;
bool printBuildTrace = false;
@@ -35,6 +35,9 @@ static bool settingsRead = false;
static std::map<string, Strings> settings;
/* Overriden settings. */
std::map<string, Strings> settingsCmdline;
string & at(Strings & ss, unsigned int n)
{
@@ -74,6 +77,8 @@ static void readSettings()
advance(i, 2);
settings[name] = Strings(i, tokens.end());
};
settings.insert(settingsCmdline.begin(), settingsCmdline.end());
settingsRead = true;
}
@@ -119,6 +124,13 @@ unsigned int queryIntSetting(const string & name, unsigned int def)
}
void overrideSetting(const string & name, const Strings & value)
{
if (settingsRead) settings[name] = value;
settingsCmdline[name] = value;
}
void reloadSettings()
{
settingsRead = false;

View File

@@ -24,6 +24,9 @@ extern string nixStateDir;
/* nixDBPath is the path name of our Berkeley DB environment. */
extern string nixDBPath;
/* nixCacheFile is the path name of the normal form cache. */
extern string nixCacheFile;
/* nixConfDir is the directory where configuration files are
stored. */
extern string nixConfDir;
@@ -35,12 +38,6 @@ extern string nixLibexecDir;
/* nixBinDir is the directory where the main programs are stored. */
extern string nixBinDir;
/* nixChrootsDir is the directory where we create chroot environments
(when chroot builds are enabled). We don't put these under /tmp to
prevent "rm -rf /tmp" from recursing into /nix/store via the bind
mounts in the chroots. */
extern string nixChrootsDir;
/* Misc. global flags. */
@@ -71,7 +68,7 @@ extern string thisSystem;
/* The maximum time in seconds that a builer can go without producing
any output on stdout/stderr before it is killed. 0 means
infinity. */
extern unsigned int maxSilentTime;
extern time_t maxSilentTime;
/* The substituters. There are programs that can somehow realise a
store path without building, e.g., by downloading it or copying it
@@ -107,6 +104,8 @@ bool queryBoolSetting(const string & name, bool def);
unsigned int queryIntSetting(const string & name, unsigned int def);
void overrideSetting(const string & name, const Strings & value);
void reloadSettings();

View File

@@ -47,6 +47,8 @@ LocalStore::LocalStore()
if (readOnlyMode) return;
createDirs(nixStore);
checkStoreNotSymlink();
try {
@@ -65,6 +67,7 @@ LocalStore::LocalStore()
createDirs(nixDBPath + "/info");
createDirs(nixDBPath + "/referrer");
createDirs(nixDBPath + "/failed");
int curSchema = getSchema();
if (curSchema > nixSchemaVersion)
@@ -85,8 +88,8 @@ LocalStore::~LocalStore()
flushDelayedUpdates();
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
i->second.toBuf.reset();
i->second.to.reset();
i->second.to.close();
i->second.from.close();
i->second.pid.wait(true);
}
@@ -108,23 +111,6 @@ int LocalStore::getSchema()
}
void copyPath(const Path & src, const Path & dst, PathFilter & filter)
{
debug(format("copying `%1%' to `%2%'") % src % dst);
/* Dump an archive of the path `src' into a string buffer, then
restore the archive to `dst'. This is not a very good method
for very large paths, but `copyPath' is mainly used for small
files. */
StringSink sink;
dumpPath(src, sink, filter);
StringSource source(sink.s);
restorePath(dst, source);
}
void canonicalisePathMetaData(const Path & path, bool recurse)
{
checkInterrupt();
@@ -167,7 +153,7 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
if (st.st_mtime != 0) {
struct utimbuf utimbuf;
utimbuf.actime = st.st_atime;
utimbuf.modtime = 0;
utimbuf.modtime = 1; /* 1 second into the epoch */
if (utime(path.c_str(), &utimbuf) == -1)
throw SysError(format("changing modification time of `%1%'") % path);
}
@@ -176,7 +162,7 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
if (recurse && S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
foreach (Strings::iterator, i, names)
canonicalisePathMetaData(path + "/" + *i, true);
}
}
@@ -213,6 +199,13 @@ static Path referrersFileFor(const Path & path)
}
static Path failedFileFor(const Path & path)
{
string baseName = baseNameOf(path);
return (format("%1%/failed/%2%") % nixDBPath % baseName).str();
}
static Path tmpFileForAtomicUpdate(const Path & path)
{
return (format("%1%/.%2%.%3%") % dirOf(path) % getpid() % baseNameOf(path)).str();
@@ -332,6 +325,8 @@ void LocalStore::registerValidPath(const ValidPathInfo & info, bool ignoreValidi
appendReferrer(*i, info.path, false);
}
assert(info.hash.type == htSHA256);
string s = (format(
"Hash: sha256:%1%\n"
"References: %2%\n"
@@ -350,6 +345,20 @@ void LocalStore::registerValidPath(const ValidPathInfo & info, bool ignoreValidi
}
void LocalStore::registerFailedPath(const Path & path)
{
/* Write an empty file in the .../failed directory to denote the
failure of the builder for `path'. */
writeFile(failedFileFor(path), "");
}
bool LocalStore::hasPathFailed(const Path & path)
{
return pathExists(failedFileFor(path));
}
Hash parseHashField(const Path & path, const string & s)
{
string::size_type colon = s.find(':');
@@ -364,7 +373,7 @@ Hash parseHashField(const Path & path, const string & s)
}
ValidPathInfo LocalStore::queryPathInfo(const Path & path)
ValidPathInfo LocalStore::queryPathInfo(const Path & path, bool ignoreErrors)
{
ValidPathInfo res;
res.path = path;
@@ -383,8 +392,8 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
/* Parse it. */
Strings lines = tokenizeString(info, "\n");
for (Strings::iterator i = lines.begin(); i != lines.end(); ++i) {
unsigned int p = i->find(':');
foreach (Strings::iterator, i, lines) {
string::size_type p = i->find(':');
if (p == string::npos) continue; /* bad line */
string name(*i, 0, p);
string value(*i, p + 2);
@@ -394,7 +403,12 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
} else if (name == "Deriver") {
res.deriver = value;
} else if (name == "Hash") {
res.hash = parseHashField(path, value);
try {
res.hash = parseHashField(path, value);
} catch (Error & e) {
if (!ignoreErrors) throw;
printMsg(lvlError, format("cannot parse hash field in `%1%': %2%") % infoFile % e.msg());
}
} else if (name == "Registered-At") {
int n = 0;
string2Int(value, n);
@@ -419,7 +433,7 @@ PathSet LocalStore::queryValidPaths()
{
PathSet paths;
Strings entries = readDirectory(nixDBPath + "/info");
for (Strings::iterator i = entries.begin(); i != entries.end(); ++i)
foreach (Strings::iterator, i, entries)
if (i->at(0) != '.') paths.insert(nixStore + "/" + *i);
return paths;
}
@@ -454,7 +468,7 @@ bool LocalStore::queryReferrersInternal(const Path & path, PathSet & referrers)
Paths refs = tokenizeString(readFile(fd), " ");
for (Paths::iterator i = refs.begin(); i != refs.end(); ++i)
foreach (Paths::iterator, i, refs)
/* Referrers can be invalid (see registerValidPath() for the
invariant), so we only return one if it is valid. */
if (isStorePath(*i) && isValidPath(*i)) referrers.insert(*i); else allValid = false;
@@ -512,23 +526,16 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
/* Parent. */
toPipe.readSide.close();
fromPipe.writeSide.close();
run.toBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(toPipe.writeSide.borrow(), std::ios_base::out));
run.to = boost::shared_ptr<std::ostream>(new std::ostream(&*run.toBuf));
run.fromBuf = boost::shared_ptr<stdio_filebuf>(new stdio_filebuf(fromPipe.readSide.borrow(), std::ios_base::in));
run.from = boost::shared_ptr<std::istream>(new std::istream(&*run.fromBuf));
run.to = toPipe.writeSide.borrow();
run.from = fromPipe.readSide.borrow();
}
template<class T> T getIntLine(std::istream & str)
template<class T> T getIntLine(int fd)
{
string s;
string s = readLine(fd);
T res;
getline(str, s);
if (!str || !string2Int(s, res)) throw Error("integer expected from stream");
if (!string2Int(s, res)) throw Error("integer expected from stream");
return res;
}
@@ -538,10 +545,8 @@ bool LocalStore::hasSubstitutes(const Path & path)
foreach (Paths::iterator, i, substituters) {
RunningSubstituter & run(runningSubstituters[*i]);
startSubstituter(*i, run);
*run.to << "have\n" << path << "\n" << std::flush;
if (getIntLine<int>(*run.from)) return true;
writeLine(run.to, "have\n" + path);
if (getIntLine<int>(run.from)) return true;
}
return false;
@@ -554,19 +559,19 @@ bool LocalStore::querySubstitutablePathInfo(const Path & substituter,
RunningSubstituter & run(runningSubstituters[substituter]);
startSubstituter(substituter, run);
*run.to << "info\n" << path << "\n" << std::flush;
if (!getIntLine<int>(*run.from)) return false;
writeLine(run.to, "info\n" + path);
if (!getIntLine<int>(run.from)) return false;
getline(*run.from, info.deriver);
info.deriver = readLine(run.from);
if (info.deriver != "") assertStorePath(info.deriver);
int nrRefs = getIntLine<int>(*run.from);
int nrRefs = getIntLine<int>(run.from);
while (nrRefs--) {
Path p; getline(*run.from, p);
Path p = readLine(run.from);
assertStorePath(p);
info.references.insert(p);
}
info.downloadSize = getIntLine<long long>(*run.from);
info.downloadSize = getIntLine<long long>(run.from);
return true;
}
@@ -595,8 +600,7 @@ static void dfsVisit(std::map<Path, ValidPathInfo> & infos,
ValidPathInfo & info(infos[path]);
for (PathSet::iterator i = info.references.begin();
i != info.references.end(); ++i)
foreach (PathSet::iterator, i, info.references)
if (infos.find(*i) != infos.end())
dfsVisit(infos, *i, visited, sorted);
@@ -611,15 +615,15 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
/* Sort the paths topologically under the references relation, so
that if path A is referenced by B, then A is registered before
B. */
for (ValidPathInfos::const_iterator i = infos.begin(); i != infos.end(); ++i)
foreach (ValidPathInfos::const_iterator, i, infos)
infosMap[i->path] = *i;
PathSet visited;
Paths sorted;
for (ValidPathInfos::const_iterator i = infos.begin(); i != infos.end(); ++i)
foreach (ValidPathInfos::const_iterator, i, infos)
dfsVisit(infosMap, i->path, visited, sorted);
for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i)
foreach (Paths::iterator, i, sorted)
registerValidPath(infosMap[*i]);
}
@@ -670,16 +674,12 @@ void LocalStore::invalidatePath(const Path & path)
}
Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
bool recursive, string hashAlgo, PathFilter & filter)
Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
bool recursive, HashType hashAlgo)
{
Path srcPath(absPath(_srcPath));
debug(format("adding `%1%' to the store") % srcPath);
Hash h = hashString(hashAlgo, dump);
std::pair<Path, Hash> pr =
computeStorePathForPath(srcPath, fixed, recursive, hashAlgo, filter);
Path & dstPath(pr.first);
Hash & h(pr.second);
Path dstPath = makeFixedOutputPath(recursive, hashAlgo, h, name);
addTempRoot(dstPath);
@@ -694,16 +694,22 @@ Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
if (pathExists(dstPath)) deletePathWrapped(dstPath);
copyPath(srcPath, dstPath, filter);
Hash h2 = hashPath(htSHA256, dstPath, filter);
if (h != h2)
throw Error(format("contents of `%1%' changed while copying it to `%2%' (%3% -> %4%)")
% srcPath % dstPath % printHash(h) % printHash(h2));
if (recursive) {
StringSource source(dump);
restorePath(dstPath, source);
} else
writeStringToFile(dstPath, dump);
canonicalisePathMetaData(dstPath);
registerValidPath(dstPath, h, PathSet(), "");
/* Register the SHA-256 hash of the NAR serialisation of
the path in the database. We may just have computed it
above (if called with recursive == true and hashAlgo ==
sha256); otherwise, compute it here. */
registerValidPath(dstPath,
(recursive && hashAlgo == htSHA256) ? h :
(recursive ? hashString(htSHA256, dump) : hashPath(htSHA256, dstPath)),
PathSet(), "");
}
outputLock.setDeletion(true);
@@ -713,10 +719,29 @@ Path LocalStore::addToStore(const Path & _srcPath, bool fixed,
}
Path LocalStore::addTextToStore(const string & suffix, const string & s,
Path LocalStore::addToStore(const Path & _srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter)
{
Path srcPath(absPath(_srcPath));
debug(format("adding `%1%' to the store") % srcPath);
/* Read the whole path into memory. This is not a very scalable
method for very large paths, but `copyPath' is mainly used for
small files. */
StringSink sink;
if (recursive)
dumpPath(srcPath, sink, filter);
else
sink.s = readFile(srcPath);
return addToStoreFromDump(sink.s, baseNameOf(srcPath), recursive, hashAlgo);
}
Path LocalStore::addTextToStore(const string & name, const string & s,
const PathSet & references)
{
Path dstPath = computeStorePathForText(suffix, s, references);
Path dstPath = computeStorePathForText(name, s, references);
addTempRoot(dstPath);
@@ -854,7 +879,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
store path follows the archive data proper), and besides, we
don't know yet whether the signature is valid. */
Path tmpDir = createTempDir(nixStore);
AutoDelete delTmp(tmpDir);
AutoDelete delTmp(tmpDir); /* !!! could be GC'ed! */
Path unpacked = tmpDir + "/unpacked";
restorePath(unpacked, hashAndReadSource);
@@ -913,7 +938,14 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
if (!isValidPath(dstPath)) {
PathLocks outputLock(singleton<PathSet, Path>(dstPath));
PathLocks outputLock;
/* Lock the output path. But don't lock if we're being called
from a build hook (whose parent process already acquired a
lock on this path). */
Strings locksHeld = tokenizeString(getEnv("NIX_HELD_LOCKS"));
if (find(locksHeld.begin(), locksHeld.end(), dstPath) == locksHeld.end())
outputLock.lockPaths(singleton<PathSet, Path>(dstPath));
if (!isValidPath(dstPath)) {
@@ -971,7 +1003,7 @@ void LocalStore::verifyStore(bool checkContents)
PathSet validPaths2 = queryValidPaths(), validPaths;
for (PathSet::iterator i = validPaths2.begin(); i != validPaths2.end(); ++i) {
foreach (PathSet::iterator, i, validPaths2) {
checkInterrupt();
if (!isStorePath(*i)) {
printMsg(lvlError, format("path `%1%' is not in the Nix store") % *i);
@@ -989,26 +1021,25 @@ void LocalStore::verifyStore(bool checkContents)
std::map<Path, PathSet> referrersCache;
for (PathSet::iterator i = validPaths.begin(); i != validPaths.end(); ++i) {
foreach (PathSet::iterator, i, validPaths) {
bool update = false;
ValidPathInfo info = queryPathInfo(*i);
ValidPathInfo info = queryPathInfo(*i, true);
/* Check the references: each reference should be valid, and
it should have a matching referrer. */
for (PathSet::iterator j = info.references.begin();
j != info.references.end(); ++j)
{
if (referrersCache.find(*j) == referrersCache.end())
queryReferrers(*j, referrersCache[*j]);
if (referrersCache[*j].find(*i) == referrersCache[*j].end()) {
printMsg(lvlError, format("adding missing referrer mapping from `%1%' to `%2%'")
% *j % *i);
appendReferrer(*j, *i, true);
}
foreach (PathSet::iterator, j, info.references) {
if (validPaths.find(*j) == validPaths.end()) {
printMsg(lvlError, format("incomplete closure: `%1%' needs missing `%2%'")
% *i % *j);
/* nothing we can do about it... */
} else {
if (referrersCache.find(*j) == referrersCache.end())
queryReferrers(*j, referrersCache[*j]);
if (referrersCache[*j].find(*i) == referrersCache[*j].end()) {
printMsg(lvlError, format("adding missing referrer mapping from `%1%' to `%2%'")
% *j % *i);
appendReferrer(*j, *i, true);
}
}
}
@@ -1020,7 +1051,11 @@ void LocalStore::verifyStore(bool checkContents)
}
/* Check the content hash (optionally - slow). */
if (checkContents) {
if (info.hash.hashSize == 0) {
printMsg(lvlError, format("re-hashing `%1%'") % *i);
info.hash = hashPath(htSHA256, *i);
update = true;
} else if (checkContents) {
debug(format("checking contents of `%1%'") % *i);
Hash current = hashPath(info.hash.type, *i);
if (current != info.hash) {
@@ -1042,10 +1077,12 @@ void LocalStore::verifyStore(bool checkContents)
std::map<Path, PathSet> referencesCache;
Strings entries = readDirectory(nixDBPath + "/referrer");
for (Strings::iterator i = entries.begin(); i != entries.end(); ++i) {
foreach (Strings::iterator, i, entries) {
Path from = nixStore + "/" + *i;
if (validPaths.find(from) == validPaths.end()) {
/* !!! This removes lock files as well. Need to check
whether that's okay. */
printMsg(lvlError, format("removing referrers file for invalid `%1%'") % from);
Path p = referrersFileFor(from);
if (unlink(p.c_str()) == -1)
@@ -1064,7 +1101,7 @@ void LocalStore::verifyStore(bool checkContents)
/* Each referrer should have a matching reference. */
PathSet referrersNew;
for (PathSet::iterator j = referrers.begin(); j != referrers.end(); ++j) {
foreach (PathSet::iterator, j, referrers) {
if (referencesCache.find(*j) == referencesCache.end())
queryReferences(*j, referencesCache[*j]);
if (referencesCache[*j].find(from) == referencesCache[*j].end()) {

View File

@@ -3,8 +3,6 @@
#include <string>
#include <ext/stdio_filebuf.h>
#include "store-api.hh"
#include "util.hh"
@@ -36,15 +34,10 @@ struct OptimiseStats
};
typedef __gnu_cxx::stdio_filebuf<char> stdio_filebuf;
struct RunningSubstituter
{
Pid pid;
boost::shared_ptr<stdio_filebuf> toBuf, fromBuf;
boost::shared_ptr<std::ostream> to;
boost::shared_ptr<std::istream> from;
AutoCloseFD to, from;
};
@@ -89,11 +82,18 @@ public:
bool querySubstitutablePathInfo(const Path & substituter,
const Path & path, SubstitutablePathInfo & info);
Path addToStore(const Path & srcPath, bool fixed = false,
bool recursive = false, string hashAlgo = "",
Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter);
Path addTextToStore(const string & suffix, const string & s,
/* Like addToStore(), but the contents of the path are contained
in `dump', which is either a NAR serialisation (if recursive ==
true) or simply the contents of a regular file (if recursive ==
false). */
Path addToStoreFromDump(const string & dump, const string & name,
bool recursive = true, HashType hashAlgo = htSHA256);
Path addTextToStore(const string & name, const string & s,
const PathSet & references);
void exportPath(const Path & path, bool sign,
@@ -137,6 +137,13 @@ public:
void registerValidPaths(const ValidPathInfos & infos);
/* Register that the build of a derivation with output `path' has
failed. */
void registerFailedPath(const Path & path);
/* Query whether `path' previously failed to build. */
bool hasPathFailed(const Path & path);
private:
Path schemaPath;
@@ -155,7 +162,7 @@ private:
void registerValidPath(const ValidPathInfo & info, bool ignoreValidity = false);
ValidPathInfo queryPathInfo(const Path & path);
ValidPathInfo queryPathInfo(const Path & path, bool ignoreErrors = false);
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
@@ -183,8 +190,8 @@ void copyPath(const Path & src, const Path & dst);
/* "Fix", or canonicalise, the meta-data of the files in a store path
after it has been built. In particular:
- the last modification date on each file is set to 0 (i.e.,
00:00:00 1/1/1970 UTC)
- the last modification date on each file is set to 1 (i.e.,
00:00:01 1/1/1970 UTC)
- the permissions are set of 444 or 555 (i.e., read-only with or
without execute permission; setuid bits etc. are cleared)
- the owner and group are set to the Nix user and group, if we're

View File

@@ -30,16 +30,14 @@ void computeFSClosure(const Path & storePath,
else
store->queryReferences(storePath, references);
for (PathSet::iterator i = references.begin();
i != references.end(); ++i)
foreach (PathSet::iterator, i, references)
computeFSClosure(*i, paths, flipDirection);
}
Path findOutput(const Derivation & drv, string id)
{
for (DerivationOutputs::const_iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
foreach (DerivationOutputs::const_iterator, i, drv.outputs)
if (i->first == id) return i->second.path;
throw Error(format("derivation has no output `%1%'") % id);
}
@@ -67,20 +65,17 @@ void queryMissing(const PathSet & targets,
Derivation drv = derivationFromPath(p);
bool mustBuild = false;
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
foreach (DerivationOutputs::iterator, i, drv.outputs)
if (!store->isValidPath(i->second.path) && !store->hasSubstitutes(i->second.path))
mustBuild = true;
if (mustBuild) {
willBuild.insert(p);
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i)
foreach (DerivationInputs::iterator, i, drv.inputDrvs)
todo.insert(i->first);
} else
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
foreach (DerivationOutputs::iterator, i, drv.outputs)
todo.insert(i->second.path);
}

View File

@@ -131,7 +131,7 @@ static void hashAndLink(bool dryRun, HashToPath & hashToPath,
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
foreach (Strings::iterator, i, names)
hashAndLink(dryRun, hashToPath, stats, path + "/" + *i);
}
}
@@ -143,7 +143,7 @@ void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats)
PathSet paths = queryValidPaths();
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
foreach (PathSet::iterator, i, paths) {
addTempRoot(*i);
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);

View File

@@ -141,9 +141,9 @@ PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
}
void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
bool PathLocks::lockPaths(const PathSet & _paths,
const string & waitMsg, bool wait)
{
/* May be called only once! */
assert(fds.empty());
/* Note that `fds' is built incrementally so that the destructor
@@ -155,7 +155,7 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
paths.sort();
/* Acquire the lock for each path. */
for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
foreach (Paths::iterator, i, paths) {
checkInterrupt();
Path path = *i;
Path lockPath = path + ".lock";
@@ -174,8 +174,15 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
/* Acquire an exclusive lock. */
if (!lockFile(fd, ltWrite, false)) {
if (waitMsg != "") printMsg(lvlError, waitMsg);
lockFile(fd, ltWrite, true);
if (wait) {
if (waitMsg != "") printMsg(lvlError, waitMsg);
lockFile(fd, ltWrite, true);
} else {
/* Failed to lock this path; release all other
locks. */
unlock();
return false;
}
}
debug(format("lock acquired on `%1%'") % lockPath);
@@ -199,12 +206,20 @@ void PathLocks::lockPaths(const PathSet & _paths, const string & waitMsg)
fds.push_back(FDPair(fd.borrow(), lockPath));
lockedPaths.insert(lockPath);
}
return true;
}
PathLocks::~PathLocks()
{
for (list<FDPair>::iterator i = fds.begin(); i != fds.end(); i++) {
unlock();
}
void PathLocks::unlock()
{
foreach (list<FDPair>::iterator, i, fds) {
if (deletePaths) deleteLockFilePreClose(i->second, i->first);
lockedPaths.erase(i->second);
@@ -216,6 +231,8 @@ PathLocks::~PathLocks()
debug(format("lock released on `%1%'") % i->second);
}
fds.clear();
}

View File

@@ -33,9 +33,11 @@ public:
PathLocks();
PathLocks(const PathSet & paths,
const string & waitMsg = "");
void lockPaths(const PathSet & _paths,
const string & waitMsg = "");
bool lockPaths(const PathSet & _paths,
const string & waitMsg = "",
bool wait = true);
~PathLocks();
void unlock();
void setDeletion(bool deletePaths);
};

View File

@@ -1,17 +1,10 @@
#include "references.hh"
#include "hash.hh"
#include "util.hh"
#include "archive.hh"
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <map>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <cstdlib>
namespace nix {
@@ -20,8 +13,8 @@ namespace nix {
static unsigned int refLength = 32; /* characters */
static void search(size_t len, const unsigned char * s,
StringSet & ids, StringSet & seen)
static void search(const unsigned char * s, unsigned int len,
StringSet & hashes, StringSet & seen)
{
static bool initialised = false;
static bool isBase32[256];
@@ -43,93 +36,60 @@ static void search(size_t len, const unsigned char * s,
}
if (!match) continue;
string ref((const char *) s + i, refLength);
if (ids.find(ref) != ids.end()) {
if (hashes.find(ref) != hashes.end()) {
debug(format("found reference to `%1%' at offset `%2%'")
% ref % i);
seen.insert(ref);
ids.erase(ref);
hashes.erase(ref);
}
++i;
}
}
void checkPath(const string & path,
StringSet & ids, StringSet & seen)
struct RefScanSink : Sink
{
checkInterrupt();
HashSink hashSink;
StringSet hashes;
StringSet seen;
string tail;
RefScanSink() : hashSink(htSHA256) { }
debug(format("checking `%1%'") % path);
void operator () (const unsigned char * data, unsigned int len);
};
struct stat st;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path);
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); i++) {
search(i->size(), (const unsigned char *) i->c_str(), ids, seen);
checkPath(path + "/" + *i, ids, seen);
}
}
void RefScanSink::operator () (const unsigned char * data, unsigned int len)
{
hashSink(data, len);
else if (S_ISREG(st.st_mode)) {
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
/* It's possible that a reference spans the previous and current
fragment, so search in the concatenation of the tail of the
previous fragment and the start of the current fragment. */
string s = tail + string((const char *) data, len > refLength ? refLength : len);
search((const unsigned char *) s.c_str(), s.size(), hashes, seen);
size_t bufSize = 1024 * 1024;
assert(refLength <= bufSize);
unsigned char * buf = new unsigned char[bufSize];
search(data, len, hashes, seen);
size_t left = st.st_size;
bool firstBlock = true;
while (left > 0) {
checkInterrupt();
size_t read = left > bufSize ? bufSize : left;
size_t copiedBytes = 0;
if (!firstBlock) {
/* Move the last (refLength - 1) bytes from the last
block to the start of the buffer to deal with
references that cross block boundaries. */
copiedBytes = refLength - 1;
if (read + copiedBytes > bufSize)
read -= copiedBytes;
memcpy(buf, buf + (bufSize - copiedBytes), copiedBytes);
}
firstBlock = false;
readFull(fd, buf + copiedBytes, read);
left -= read;
search(copiedBytes + read, buf, ids, seen);
}
delete[] buf; /* !!! autodelete */
}
else if (S_ISLNK(st.st_mode)) {
string target = readLink(path);
search(target.size(), (const unsigned char *) target.c_str(), ids, seen);
}
else throw Error(format("unknown file type: %1%") % path);
unsigned int tailLen = len <= refLength ? len : refLength;
tail =
string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen)) +
string((const char *) data + len - tailLen, tailLen);
}
PathSet scanForReferences(const string & path, const PathSet & paths)
PathSet scanForReferences(const string & path,
const PathSet & refs, Hash & hash)
{
RefScanSink sink;
std::map<string, Path> backMap;
StringSet ids;
StringSet seen;
/* For efficiency (and a higher hit rate), just search for the
hash part of the file name. (This assumes that all references
have the form `HASH-bla'). */
for (PathSet::const_iterator i = paths.begin(); i != paths.end(); i++) {
foreach (PathSet::const_iterator, i, refs) {
string baseName = baseNameOf(*i);
string::size_type pos = baseName.find('-');
if (pos == string::npos)
@@ -138,21 +98,25 @@ PathSet scanForReferences(const string & path, const PathSet & paths)
assert(s.size() == refLength);
assert(backMap.find(s) == backMap.end());
// parseHash(htSHA256, s);
ids.insert(s);
sink.hashes.insert(s);
backMap[s] = *i;
}
checkPath(path, ids, seen);
/* Look for the hashes in the NAR dump of the path. */
dumpPath(path, sink);
/* Map the hashes found back to their store paths. */
PathSet found;
for (StringSet::iterator i = seen.begin(); i != seen.end(); i++) {
foreach (StringSet::iterator, i, sink.seen) {
std::map<string, Path>::iterator j;
if ((j = backMap.find(*i)) == backMap.end()) abort();
found.insert(j->second);
}
hash = sink.hashSink.finish();
return found;
}
}

View File

@@ -2,10 +2,12 @@
#define __REFERENCES_H
#include "types.hh"
#include "hash.hh"
namespace nix {
PathSet scanForReferences(const Path & path, const PathSet & refs);
PathSet scanForReferences(const Path & path, const PathSet & refs,
Hash & hash);
}

View File

@@ -29,14 +29,22 @@ Path readStorePath(Source & from)
PathSet readStorePaths(Source & from)
{
PathSet paths = readStringSet(from);
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
assertStorePath(*i);
foreach (PathSet::iterator, i, paths) assertStorePath(*i);
return paths;
}
RemoteStore::RemoteStore()
{
initialised = false;
}
void RemoteStore::openConnection()
{
if (initialised) return;
initialised = true;
string remoteMode = getEnv("NIX_REMOTE");
if (remoteMode == "slave")
@@ -64,8 +72,8 @@ RemoteStore::RemoteStore()
throw Error("Nix daemon protocol version not supported");
writeInt(PROTOCOL_VERSION, to);
processStderr();
} catch (Error & e) {
}
catch (Error & e) {
throw Error(format("cannot start worker (%1%)")
% e.msg());
}
@@ -194,6 +202,7 @@ void RemoteStore::setOptions()
bool RemoteStore::isValidPath(const Path & path)
{
openConnection();
writeInt(wopIsValidPath, to);
writeString(path, to);
processStderr();
@@ -204,12 +213,14 @@ bool RemoteStore::isValidPath(const Path & path)
PathSet RemoteStore::queryValidPaths()
{
openConnection();
throw Error("not implemented");
}
bool RemoteStore::hasSubstitutes(const Path & path)
{
openConnection();
writeInt(wopHasSubstitutes, to);
writeString(path, to);
processStderr();
@@ -221,6 +232,7 @@ bool RemoteStore::hasSubstitutes(const Path & path)
bool RemoteStore::querySubstitutablePathInfo(const Path & path,
SubstitutablePathInfo & info)
{
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return false;
writeInt(wopQuerySubstitutablePathInfo, to);
writeString(path, to);
@@ -237,6 +249,7 @@ bool RemoteStore::querySubstitutablePathInfo(const Path & path,
Hash RemoteStore::queryPathHash(const Path & path)
{
openConnection();
writeInt(wopQueryPathHash, to);
writeString(path, to);
processStderr();
@@ -248,6 +261,7 @@ Hash RemoteStore::queryPathHash(const Path & path)
void RemoteStore::queryReferences(const Path & path,
PathSet & references)
{
openConnection();
writeInt(wopQueryReferences, to);
writeString(path, to);
processStderr();
@@ -259,6 +273,7 @@ void RemoteStore::queryReferences(const Path & path,
void RemoteStore::queryReferrers(const Path & path,
PathSet & referrers)
{
openConnection();
writeInt(wopQueryReferrers, to);
writeString(path, to);
processStderr();
@@ -269,6 +284,7 @@ void RemoteStore::queryReferrers(const Path & path,
Path RemoteStore::queryDeriver(const Path & path)
{
openConnection();
writeInt(wopQueryDeriver, to);
writeString(path, to);
processStderr();
@@ -278,27 +294,31 @@ Path RemoteStore::queryDeriver(const Path & path)
}
Path RemoteStore::addToStore(const Path & _srcPath, bool fixed,
bool recursive, string hashAlgo, PathFilter & filter)
Path RemoteStore::addToStore(const Path & _srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter)
{
openConnection();
Path srcPath(absPath(_srcPath));
writeInt(wopAddToStore, to);
writeString(baseNameOf(srcPath), to);
writeInt(fixed ? 1 : 0, to);
/* backwards compatibility hack */
writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
writeInt(recursive ? 1 : 0, to);
writeString(hashAlgo, to);
writeString(printHashType(hashAlgo), to);
dumpPath(srcPath, to, filter);
processStderr();
return readStorePath(from);
}
Path RemoteStore::addTextToStore(const string & suffix, const string & s,
Path RemoteStore::addTextToStore(const string & name, const string & s,
const PathSet & references)
{
openConnection();
writeInt(wopAddTextToStore, to);
writeString(suffix, to);
writeString(name, to);
writeString(s, to);
writeStringSet(references, to);
@@ -310,6 +330,7 @@ Path RemoteStore::addTextToStore(const string & suffix, const string & s,
void RemoteStore::exportPath(const Path & path, bool sign,
Sink & sink)
{
openConnection();
writeInt(wopExportPath, to);
writeString(path, to);
writeInt(sign ? 1 : 0, to);
@@ -320,10 +341,10 @@ void RemoteStore::exportPath(const Path & path, bool sign,
Path RemoteStore::importPath(bool requireSignature, Source & source)
{
openConnection();
writeInt(wopImportPath, to);
/* We ignore requireSignature, since the worker forces it to true
anyway. */
anyway. */
processStderr(0, &source);
return readStorePath(from);
}
@@ -331,6 +352,7 @@ Path RemoteStore::importPath(bool requireSignature, Source & source)
void RemoteStore::buildDerivations(const PathSet & drvPaths)
{
openConnection();
writeInt(wopBuildDerivations, to);
writeStringSet(drvPaths, to);
processStderr();
@@ -340,6 +362,7 @@ void RemoteStore::buildDerivations(const PathSet & drvPaths)
void RemoteStore::ensurePath(const Path & path)
{
openConnection();
writeInt(wopEnsurePath, to);
writeString(path, to);
processStderr();
@@ -349,6 +372,7 @@ void RemoteStore::ensurePath(const Path & path)
void RemoteStore::addTempRoot(const Path & path)
{
openConnection();
writeInt(wopAddTempRoot, to);
writeString(path, to);
processStderr();
@@ -358,6 +382,7 @@ void RemoteStore::addTempRoot(const Path & path)
void RemoteStore::addIndirectRoot(const Path & path)
{
openConnection();
writeInt(wopAddIndirectRoot, to);
writeString(path, to);
processStderr();
@@ -367,6 +392,7 @@ void RemoteStore::addIndirectRoot(const Path & path)
void RemoteStore::syncWithGC()
{
openConnection();
writeInt(wopSyncWithGC, to);
processStderr();
readInt(from);
@@ -375,6 +401,7 @@ void RemoteStore::syncWithGC()
Roots RemoteStore::findRoots()
{
openConnection();
writeInt(wopFindRoots, to);
processStderr();
unsigned int count = readInt(from);
@@ -390,12 +417,18 @@ Roots RemoteStore::findRoots()
void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
{
openConnection();
writeInt(wopCollectGarbage, to);
writeInt(options.action, to);
writeStringSet(options.pathsToDelete, to);
writeInt(options.ignoreLiveness, to);
writeLongLong(options.maxFreed, to);
writeInt(options.maxLinks, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) {
writeInt(options.useAtime, to);
writeInt(options.maxAtime, to);
}
processStderr();

View File

@@ -42,11 +42,11 @@ public:
bool querySubstitutablePathInfo(const Path & path,
SubstitutablePathInfo & info);
Path addToStore(const Path & srcPath, bool fixed = false,
bool recursive = false, string hashAlgo = "",
Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter);
Path addTextToStore(const string & suffix, const string & s,
Path addTextToStore(const string & name, const string & s,
const PathSet & references);
void exportPath(const Path & path, bool sign,
@@ -74,6 +74,9 @@ private:
FdSource from;
Pid child;
unsigned int daemonVersion;
bool initialised;
void openConnection();
void processStderr(Sink * sink = 0, Source * source = 0);

View File

@@ -5,12 +5,6 @@
#include <limits.h>
/* Needed for some ancient environments. */
#ifndef ULLONG_MAX
#define ULLONG_MAX 18446744073709551615
#endif
namespace nix {
@@ -18,7 +12,7 @@ GCOptions::GCOptions()
{
action = gcDeleteDead;
ignoreLiveness = false;
maxFreed = ULLONG_MAX;
maxFreed = 0;
maxLinks = 0;
useAtime = false;
maxAtime = (time_t) -1;
@@ -87,7 +81,7 @@ void checkStoreName(const string & name)
reasons (e.g., "." and ".."). */
if (string(name, 0, 1) == ".")
throw Error(format("illegal name: `%1%'") % name);
for (string::const_iterator i = name.begin(); i != name.end(); ++i)
foreach (string::const_iterator, i, name)
if (!((*i >= 'A' && *i <= 'Z') ||
(*i >= 'a' && *i <= 'z') ||
(*i >= '0' && *i <= '9') ||
@@ -99,55 +93,113 @@ void checkStoreName(const string & name)
}
/* Store paths have the following form:
<store>/<h>-<name>
where
<store> = the location of the Nix store, usually /nix/store
<name> = a human readable name for the path, typically obtained
from the name attribute of the derivation, or the name of the
source file from which the store path is created
<h> = base-32 representation of the first 160 bits of a SHA-256
hash of <s>; the hash part of the store name
<s> = the string "<type>:sha256:<h2>:<store>:<name>";
note that it includes the location of the store as well as the
name to make sure that changes to either of those are reflected
in the hash (e.g. you won't get /nix/store/<h>-name1 and
/nix/store/<h>-name2 with equal hash parts).
<type> = one of:
"text:<r1>:<r2>:...<rN>"
for plain text files written to the store using
addTextToStore(); <r1> ... <rN> are the references of the
path.
"source"
for paths copied to the store using addToStore() when recursive
= true and hashAlgo = "sha256"
"output:out"
for either the outputs created by derivations, OR paths copied
to the store using addToStore() with recursive != true or
hashAlgo != "sha256" (in that case "source" is used; it's
silly, but it's done that way for compatibility).
<h2> = base-16 representation of a SHA-256 hash of:
if <type> = "text:...":
the string written to the resulting store path
if <type> = "source":
the serialisation of the path from which this store path is
copied, as returned by hashPath()
if <type> = "output:out":
for non-fixed derivation outputs:
the derivation (see hashDerivationModulo() in
primops.cc)
for paths copied by addToStore() or produced by fixed-output
derivations:
the string "fixed:out:<rec><algo>:<hash>:", where
<rec> = "r:" for recursive (path) hashes, or "" or flat
(file) hashes
<algo> = "md5", "sha1" or "sha256"
<hash> = base-16 representation of the path or flat hash of
the contents of the path (or expected contents of the
path for fixed-output derivations)
It would have been nicer to handle fixed-output derivations under
"source", e.g. have something like "source:<rec><algo>", but we're
stuck with this for now...
The main reason for this way of computing names is to prevent name
collisions (for security). For instance, it shouldn't be feasible
to come up with a derivation whose output path collides with the
path for a copied source. The former would have a <s> starting with
"output:out:", while the latter would have a <2> starting with
"source:".
*/
Path makeStorePath(const string & type,
const Hash & hash, const string & suffix)
const Hash & hash, const string & name)
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":sha256:" + printHash(hash) + ":"
+ nixStore + ":" + suffix;
+ nixStore + ":" + name;
checkStoreName(suffix);
checkStoreName(name);
return nixStore + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + suffix;
+ "-" + name;
}
Path makeFixedOutputPath(bool recursive,
string hashAlgo, Hash hash, string name)
HashType hashAlgo, Hash hash, string name)
{
/* !!! copy/paste from primops.cc */
Hash h = hashString(htSHA256, "fixed:out:"
+ (recursive ? (string) "r:" : "") + hashAlgo + ":"
+ printHash(hash) + ":"
+ "");
return makeStorePath("output:out", h, name);
return hashAlgo == htSHA256 && recursive
? makeStorePath("source", hash, name)
: makeStorePath("output:out", hashString(htSHA256,
"fixed:out:" + (recursive ? (string) "r:" : "") +
printHashType(hashAlgo) + ":" + printHash(hash) + ":"),
name);
}
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
bool fixed, bool recursive, string hashAlgo, PathFilter & filter)
bool recursive, HashType hashAlgo, PathFilter & filter)
{
Hash h = hashPath(htSHA256, srcPath, filter);
string baseName = baseNameOf(srcPath);
Path dstPath;
if (fixed) {
HashType ht(parseHashType(hashAlgo));
Hash h2 = recursive ? hashPath(ht, srcPath, filter) : hashFile(ht, srcPath);
dstPath = makeFixedOutputPath(recursive, hashAlgo, h2, baseName);
}
else dstPath = makeStorePath("source", h, baseName);
HashType ht(hashAlgo);
Hash h = recursive ? hashPath(ht, srcPath, filter) : hashFile(ht, srcPath);
string name = baseNameOf(srcPath);
Path dstPath = makeFixedOutputPath(recursive, hashAlgo, h, name);
return std::pair<Path, Hash>(dstPath, h);
}
Path computeStorePathForText(const string & suffix, const string & s,
Path computeStorePathForText(const string & name, const string & s,
const PathSet & references)
{
Hash hash = hashString(htSHA256, s);
@@ -155,11 +207,11 @@ Path computeStorePathForText(const string & suffix, const string & s,
hacky, but we can't put them in `s' since that would be
ambiguous. */
string type = "text";
for (PathSet::const_iterator i = references.begin(); i != references.end(); ++i) {
foreach (PathSet::const_iterator, i, references) {
type += ":";
type += *i;
}
return makeStorePath(type, hash, suffix);
return makeStorePath(type, hash, name);
}
@@ -171,7 +223,7 @@ string makeValidityRegistration(const PathSet & paths,
{
string s = "";
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
foreach (PathSet::iterator, i, paths) {
s += *i + "\n";
if (showHash)
@@ -185,8 +237,7 @@ string makeValidityRegistration(const PathSet & paths,
s += (format("%1%\n") % references.size()).str();
for (PathSet::iterator j = references.begin();
j != references.end(); ++j)
foreach (PathSet::iterator, j, references)
s += *j + "\n";
}
@@ -220,9 +271,7 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
string showPaths(const PathSet & paths)
{
string s;
for (PathSet::const_iterator i = paths.begin();
i != paths.end(); ++i)
{
foreach (PathSet::const_iterator, i, paths) {
if (s.size() != 0) s += ", ";
s += "`" + *i + "'";
}

View File

@@ -56,7 +56,8 @@ struct GCOptions
/* For `gcDeleteSpecific', the paths to delete. */
PathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. */
/* Stop after at least `maxFreed' bytes have been freed. 0 means
no limit. */
unsigned long long maxFreed;
/* Stop after the number of hard links to the Nix store directory
@@ -173,13 +174,13 @@ public:
derivation is pre-loaded into the Nix store. The function
object `filter' can be used to exclude files (see
libutil/archive.hh). */
virtual Path addToStore(const Path & srcPath, bool fixed = false,
bool recursive = false, string hashAlgo = "",
virtual Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter) = 0;
/* Like addToStore, but the contents written to the output path is
a regular file containing the given string. */
virtual Path addTextToStore(const string & suffix, const string & s,
virtual Path addTextToStore(const string & name, const string & s,
const PathSet & references) = 0;
/* Export a store path, that is, create a NAR dump of the store
@@ -274,10 +275,10 @@ Path followLinksToStorePath(const Path & path);
/* Constructs a unique store path name. */
Path makeStorePath(const string & type,
const Hash & hash, const string & suffix);
const Hash & hash, const string & name);
Path makeFixedOutputPath(bool recursive,
string hashAlgo, Hash hash, string name);
HashType hashAlgo, Hash hash, string name);
/* This is the preparatory part of addToStore() and addToStoreFixed();
@@ -285,7 +286,7 @@ Path makeFixedOutputPath(bool recursive,
Returns the store path and the cryptographic hash of the
contents of srcPath. */
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
bool fixed = false, bool recursive = false, string hashAlgo = "",
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter);
/* Preparatory part of addTextToStore().
@@ -302,7 +303,7 @@ std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
simply yield a different store path, so other users wouldn't be
affected), but it has some backwards compatibility issues (the
hashing scheme changes), so I'm not doing that for now. */
Path computeStorePathForText(const string & suffix, const string & s,
Path computeStorePathForText(const string & name, const string & s,
const PathSet & references);

View File

@@ -73,7 +73,7 @@ void LocalStore::upgradeStore12()
Paths paths;
nixDB.enumTable(noTxn, dbValidPaths, paths);
for (Paths::iterator i = paths.begin(); i != paths.end(); ++i) {
foreach (Paths::iterator, i, paths) {
ValidPathInfo info;
info.path = *i;

View File

@@ -8,7 +8,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION 0x104
#define PROTOCOL_VERSION 0x105
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)

View File

@@ -2,6 +2,7 @@
#include <algorithm>
#include <vector>
#define _XOPEN_SOURCE 600
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -11,6 +12,8 @@
#include "archive.hh"
#include "util.hh"
#include "config.h"
namespace nix {
@@ -47,17 +50,17 @@ static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
}
static void dumpContents(const Path & path, unsigned int size,
static void dumpContents(const Path & path, size_t size,
Sink & sink)
{
writeString("contents", sink);
writeInt(size, sink);
writeLongLong(size, sink);
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
unsigned char buf[65536];
unsigned int left = size;
size_t left = size;
while (left > 0) {
size_t n = left > sizeof(buf) ? sizeof(buf) : left;
@@ -85,7 +88,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
writeString("executable", sink);
writeString("", sink);
}
dumpContents(path, st.st_size, sink);
dumpContents(path, (size_t) st.st_size, sink);
}
else if (S_ISDIR(st.st_mode)) {
@@ -101,7 +104,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
writeString(readLink(path), sink);
}
else throw Error("unknown file type: " + path);
else throw Error(format("file `%1%' has an unknown type") % path);
writeString(")", sink);
}
@@ -114,9 +117,9 @@ void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
}
static Error badArchive(string s)
static SerialisationError badArchive(string s)
{
return Error("bad archive: " + s);
return SerialisationError("bad archive: " + s);
}
@@ -129,10 +132,11 @@ static void skipGeneric(Source & source)
}
static void restore(const Path & path, Source & source);
static void parse(ParseSink & sink, Source & source, const Path & path);
static void restoreEntry(const Path & path, Source & source)
static void parseEntry(ParseSink & sink, Source & source, const Path & path)
{
string s, name;
@@ -150,7 +154,7 @@ static void restoreEntry(const Path & path, Source & source)
name = readString(source);
} else if (s == "node") {
if (s == "") throw badArchive("entry name missing");
restore(path + "/" + name, source);
parse(sink, source, path + "/" + name);
} else {
throw badArchive("unknown field " + s);
skipGeneric(source);
@@ -159,26 +163,31 @@ static void restoreEntry(const Path & path, Source & source)
}
static void restoreContents(int fd, const Path & path, Source & source)
static void parseContents(ParseSink & sink, Source & source, const Path & path)
{
unsigned int size = readInt(source);
unsigned int left = size;
unsigned long long size = readLongLong(source);
sink.preallocateContents(size);
unsigned long long left = size;
unsigned char buf[65536];
while (left) {
checkInterrupt();
unsigned int n = sizeof(buf);
if (n > left) n = left;
if ((unsigned long long) n > left) n = left;
source(buf, n);
writeFull(fd, buf, n);
sink.receiveContents(buf, n);
left -= n;
}
sink.finalizeContents(size);
readPadding(size, source);
}
static void restore(const Path & path, Source & source)
static void parse(ParseSink & sink, Source & source, const Path & path)
{
string s;
@@ -186,7 +195,6 @@ static void restore(const Path & path, Source & source)
if (s != "(") throw badArchive("expected open tag");
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
AutoCloseFD fd;
while (1) {
checkInterrupt();
@@ -204,15 +212,12 @@ static void restore(const Path & path, Source & source)
if (t == "regular") {
type = tpRegular;
fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd == -1)
throw SysError("creating file " + path);
sink.createRegularFile(path);
}
else if (t == "directory") {
sink.createDirectory(path);
type = tpDirectory;
if (mkdir(path.c_str(), 0777) == -1)
throw SysError("creating directory " + path);
}
else if (t == "symlink") {
@@ -224,42 +229,109 @@ static void restore(const Path & path, Source & source)
}
else if (s == "contents" && type == tpRegular) {
restoreContents(fd, path, source);
parseContents(sink, source, path);
}
else if (s == "executable" && type == tpRegular) {
readString(source);
struct stat st;
if (fstat(fd, &st) == -1)
throw SysError("fstat");
if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
throw SysError("fchmod");
sink.isExecutable();
}
else if (s == "entry" && type == tpDirectory) {
restoreEntry(path, source);
parseEntry(sink, source, path);
}
else if (s == "target" && type == tpSymlink) {
string target = readString(source);
if (symlink(target.c_str(), path.c_str()) == -1)
throw SysError("creating symlink " + path);
sink.createSymlink(path, target);
}
else {
throw badArchive("unknown field " + s);
skipGeneric(source);
}
}
}
void parseDump(ParseSink & sink, Source & source)
{
string version;
try {
version = readString(source);
} catch (SerialisationError & e) {
/* This generally means the integer at the start couldn't be
decoded. Ignore and throw the exception below. */
}
if (version != archiveVersion1)
throw badArchive("input doesn't look like a Nix archive");
parse(sink, source, "");
}
struct RestoreSink : ParseSink
{
Path dstPath;
AutoCloseFD fd;
void createDirectory(const Path & path)
{
Path p = dstPath + path;
if (mkdir(p.c_str(), 0777) == -1)
throw SysError(format("creating directory `%1%'") % p);
};
void createRegularFile(const Path & path)
{
Path p = dstPath + path;
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd == -1) throw SysError(format("creating file `%1%'") % p);
}
void isExecutable()
{
struct stat st;
if (fstat(fd, &st) == -1)
throw SysError("fstat");
if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
throw SysError("fchmod");
}
void preallocateContents(unsigned long long len)
{
#if HAVE_POSIX_FALLOCATE
if (len) {
errno = posix_fallocate(fd, 0, len);
if (errno) throw SysError(format("preallocating file of %1% bytes") % len);
}
#endif
}
void receiveContents(unsigned char * data, unsigned int len)
{
writeFull(fd, data, len);
}
void finalizeContents(unsigned long long size)
{
errno = ftruncate(fd, size);
if (errno) throw SysError(format("truncating file to its allocated length of %1% bytes") % size);
}
void createSymlink(const Path & path, const string & target)
{
Path p = dstPath + path;
if (symlink(target.c_str(), p.c_str()) == -1)
throw SysError(format("creating symlink `%1%'") % p);
}
};
void restorePath(const Path & path, Source & source)
{
if (readString(source) != archiveVersion1)
throw badArchive("expected Nix archive");
restore(path, source);
RestoreSink sink;
sink.dstPath = path;
parseDump(sink, source);
}

View File

@@ -56,6 +56,21 @@ extern PathFilter defaultPathFilter;
void dumpPath(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
struct ParseSink
{
virtual void createDirectory(const Path & path) { };
virtual void createRegularFile(const Path & path) { };
virtual void isExecutable() { };
virtual void preallocateContents(unsigned long long size) { };
virtual void receiveContents(unsigned char * data, unsigned int len) { };
virtual void finalizeContents(unsigned long long size) { };
virtual void createSymlink(const Path & path, const string & target) { };
};
void parseDump(ParseSink & sink, Source & source);
void restorePath(const Path & path, Source & source);

View File

@@ -215,7 +215,7 @@ void ATermMap::remove(ATerm key)
}
unsigned int ATermMap::size()
unsigned int ATermMap::size() const
{
return count; /* STL nomenclature */
}

View File

@@ -59,7 +59,7 @@ public:
void remove(ATerm key);
unsigned int size();
unsigned int size() const;
struct const_iterator
{

View File

@@ -335,4 +335,13 @@ HashType parseHashType(const string & s)
}
string printHashType(HashType ht)
{
if (ht == htMD5) return "md5";
else if (ht == htSHA1) return "sha1";
else if (ht == htSHA256) return "sha256";
else throw Error("cannot print unknown hash type");
}
}

View File

@@ -82,6 +82,9 @@ Hash compressHash(const Hash & hash, unsigned int newSize);
/* Parse a string representing a hash type. */
HashType parseHashType(const string & s);
/* And the reverse. */
string printHashType(HashType ht);
union Ctx;

View File

@@ -1,9 +1,9 @@
#include "serialise.hh"
#include "util.hh"
#include "aterm.hh"
#include <cstring>
namespace nix {
@@ -73,6 +73,67 @@ void writeStringSet(const StringSet & ss, Sink & sink)
}
void writeATerm(ATerm t, Sink & sink)
{
int len;
unsigned char *buf = (unsigned char *) ATwriteToBinaryString(t, &len);
AutoDeleteArray<unsigned char> d(buf);
writeInt(len, sink);
sink(buf, len);
}
/* convert the ATermMap to a list of couple because many terms are shared
between the keys and between the values. Thus the BAF stored by
writeATerm consume less memory space. The list of couples is saved
inside a tree structure of /treedepth/ height because the serialiasation
of ATerm cause a tail recurssion on list tails. */
void writeATermMap(const ATermMap & tm, Sink & sink)
{
const unsigned int treedepth = 5;
const unsigned int maxarity = 128; // 2 < maxarity < MAX_ARITY (= 255)
const unsigned int bufsize = treedepth * maxarity;
AFun map = ATmakeAFun("map", 2, ATfalse);
AFun node = ATmakeAFun("node", maxarity, ATfalse);
ATerm empty = (ATerm) ATmakeAppl0(ATmakeAFun("empty", 0, ATfalse));
unsigned int c[treedepth];
ATerm *buf = new ATerm[bufsize];
AutoDeleteArray<ATerm> d(buf);
memset(buf, 0, bufsize * sizeof(ATerm));
ATprotectArray(buf, bufsize);
for (unsigned int j = 0; j < treedepth; j++)
c[j] = 0;
for (ATermMap::const_iterator i = tm.begin(); i != tm.end(); ++i) {
unsigned int depth = treedepth - 1;
ATerm term = (ATerm) ATmakeAppl2(map, i->key, i->value);
buf[depth * maxarity + c[depth]++] = term;
while (c[depth] % maxarity == 0) {
c[depth] = 0;
term = (ATerm) ATmakeApplArray(node, &buf[depth * maxarity]);
depth--;
buf[depth * maxarity + c[depth]++] = term;
}
}
unsigned int depth = treedepth;
ATerm last_node = empty;
while (depth--) {
buf[depth * maxarity + c[depth]++] = last_node;
while (c[depth] % maxarity)
buf[depth * maxarity + c[depth]++] = empty;
last_node = (ATerm) ATmakeApplArray(node, &buf[depth * maxarity]);
}
writeATerm(last_node, sink);
ATunprotectArray(buf);
}
void readPadding(unsigned int len, Source & source)
{
if (len % 8) {
@@ -80,7 +141,7 @@ void readPadding(unsigned int len, Source & source)
unsigned int n = 8 - (len % 8);
source(zero, n);
for (unsigned int i = 0; i < n; i++)
if (zero[i]) throw Error("non-zero padding");
if (zero[i]) throw SerialisationError("non-zero padding");
}
}
@@ -90,7 +151,7 @@ unsigned int readInt(Source & source)
unsigned char buf[8];
source(buf, sizeof(buf));
if (buf[4] || buf[5] || buf[6] || buf[7])
throw Error("implementation cannot deal with > 32-bit integers");
throw SerialisationError("implementation cannot deal with > 32-bit integers");
return
buf[0] |
(buf[1] << 8) |
@@ -136,4 +197,48 @@ StringSet readStringSet(Source & source)
}
ATerm readATerm(Source & source)
{
unsigned int len = readInt(source);
unsigned char * buf = new unsigned char[len];
AutoDeleteArray<unsigned char> d(buf);
source(buf, len);
ATerm t = ATreadFromBinaryString((char *) buf, len);
if (t == 0)
throw SerialisationError("cannot read a valid ATerm.");
return t;
}
static void recursiveInitATermMap(ATermMap &tm, bool &stop, ATermAppl node)
{
const unsigned int arity = ATgetArity(ATgetAFun(node));
ATerm key, value;
switch (arity) {
case 0:
stop = true;
return;
case 2:
key = ATgetArgument(node, 0);
value = ATgetArgument(node, 1);
tm.set(key, value);
return;
default:
for (unsigned int i = 0; i < arity && !stop; i++)
recursiveInitATermMap(tm, stop, (ATermAppl) ATgetArgument(node, i));
return;
}
}
ATermMap readATermMap(Source & source)
{
ATermMap tm;
bool stop = false;
recursiveInitATermMap(tm, stop, (ATermAppl) readATerm(source));
return tm;
}
}

View File

@@ -2,7 +2,7 @@
#define __SERIALISE_H
#include "types.hh"
#include "aterm-map.hh"
namespace nix {
@@ -35,7 +35,7 @@ struct FdSink : Sink
FdSink()
{
fd = 0;
fd = -1;
}
FdSink(int fd)
@@ -54,7 +54,7 @@ struct FdSource : Source
FdSource()
{
fd = 0;
fd = -1;
}
FdSource(int fd)
@@ -80,9 +80,9 @@ struct StringSink : Sink
/* A source that reads data from a string. */
struct StringSource : Source
{
string & s;
const string & s;
unsigned int pos;
StringSource(string & _s) : s(_s), pos(0) { }
StringSource(const string & _s) : s(_s), pos(0) { }
virtual void operator () (unsigned char * data, unsigned int len)
{
s.copy((char *) data, len, pos);
@@ -98,12 +98,19 @@ void writeInt(unsigned int n, Sink & sink);
void writeLongLong(unsigned long long n, Sink & sink);
void writeString(const string & s, Sink & sink);
void writeStringSet(const StringSet & ss, Sink & sink);
void writeATerm(ATerm t, Sink & sink);
void writeATermMap(const ATermMap & tm, Sink & sink);
void readPadding(unsigned int len, Source & source);
unsigned int readInt(Source & source);
unsigned long long readLongLong(Source & source);
string readString(Source & source);
StringSet readStringSet(Source & source);
ATerm readATerm(Source & source);
ATermMap readATermMap(Source & source);
MakeError(SerialisationError, Error)
}

View File

@@ -24,12 +24,14 @@ using boost::format;
class BaseError : public std::exception
{
protected:
string prefix_; // used for location traces etc.
string err;
public:
BaseError(const format & f);
~BaseError() throw () { };
const char * what() const throw () { return err.c_str(); }
const string & msg() const throw () { return err; }
const string & prefix() const throw () { return prefix_; }
BaseError & addPrefix(const format & f);
};

View File

@@ -33,7 +33,7 @@ BaseError::BaseError(const format & f)
BaseError & BaseError::addPrefix(const format & f)
{
err = f.str() + err;
prefix_ = f.str() + prefix_;
return *this;
}
@@ -229,6 +229,33 @@ void writeFile(const Path & path, const string & s)
}
string readLine(int fd)
{
string s;
while (1) {
checkInterrupt();
char ch;
ssize_t rd = read(fd, &ch, 1);
if (rd == -1) {
if (errno != EINTR)
throw SysError("reading a line");
} else if (rd == 0)
throw Error("unexpected EOF reading a line");
else {
if (ch == '\n') return s;
s += ch;
}
}
}
void writeLine(int fd, string s)
{
s += '\n';
writeFull(fd, (const unsigned char *) s.c_str(), s.size());
}
static void _computePathSize(const Path & path,
unsigned long long & bytes, unsigned long long & blocks)
{
@@ -469,11 +496,7 @@ void warnOnce(bool & haveWarned, const format & f)
static void defaultWriteToStderr(const unsigned char * buf, size_t count)
{
try {
writeFull(STDERR_FILENO, buf, count);
} catch (SysError & e) {
/* ignore EPIPE etc. */
}
writeFull(STDERR_FILENO, buf, count);
}

View File

@@ -60,6 +60,12 @@ string readFile(const Path & path);
/* Write a string to a file. */
void writeFile(const Path & path, const string & s);
/* Read a line from a file descriptor. */
string readLine(int fd);
/* Write a line to a file descriptor. */
void writeLine(int fd, string s);
/* Compute the sum of the sizes of all files in `path'. */
void computePathSize(const Path & path,
unsigned long long & bytes, unsigned long long & blocks);

View File

@@ -251,15 +251,14 @@ static string optimisticLockProfile(const Path & profile)
}
static bool createUserEnv(EvalState & state, const DrvInfos & elems,
static bool createUserEnv(EvalState & state, DrvInfos & elems,
const Path & profile, bool keepDerivations,
const string & lockToken)
{
/* Build the components in the user environment, if they don't
exist already. */
PathSet drvsToBuild;
for (DrvInfos::const_iterator i = elems.begin();
i != elems.end(); ++i)
foreach (DrvInfos::const_iterator, i, elems)
/* Call to `isDerivation' is for compatibility with Nix <= 0.7
user environments. */
if (i->queryDrvPath(state) != "" &&
@@ -277,19 +276,16 @@ static bool createUserEnv(EvalState & state, const DrvInfos & elems,
PathSet references;
ATermList manifest = ATempty;
ATermList inputs = ATempty;
for (DrvInfos::const_iterator i = elems.begin();
i != elems.end(); ++i)
{
foreach (DrvInfos::iterator, i, elems) {
/* Create a pseudo-derivation containing the name, system,
output path, and optionally the derivation path, as well as
the meta attributes. */
Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
/* Round trip to get rid of "bad" meta values (like
functions). */
MetaInfo meta = i->queryMetaInfo(state);
ATermList metaList = ATempty;
foreach (MetaInfo::iterator, j, meta)
metaList = ATinsert(metaList,
makeBind(toATerm(j->first), makeStr(j->second), makeNoPos()));
i->setMetaInfo(meta);
ATermList as = ATmakeList5(
makeBind(toATerm("type"),
@@ -300,7 +296,8 @@ static bool createUserEnv(EvalState & state, const DrvInfos & elems,
makeStr(i->system), makeNoPos()),
makeBind(toATerm("outPath"),
makeStr(i->queryOutPath(state)), makeNoPos()),
makeBind(toATerm("meta"), makeAttrs(metaList), makeNoPos()));
makeBind(toATerm("meta"),
i->attrs->get(toATerm("meta")), makeNoPos()));
if (drvPath != "") as = ATinsert(as,
makeBind(toATerm("drvPath"),
@@ -361,13 +358,23 @@ static bool createUserEnv(EvalState & state, const DrvInfos & elems,
}
static int getPriority(EvalState & state, const DrvInfo & drv)
{
MetaValue value = drv.queryMetaInfo(state, "priority");
int prio = 0;
if (value.type == MetaValue::tpInt) prio = value.intValue;
else if (value.type == MetaValue::tpString)
/* Backwards compatibility. Priorities used to be strings
before we had support for integer meta field. */
string2Int(value.stringValue, prio);
return prio;
}
static int comparePriorities(EvalState & state,
const DrvInfo & drv1, const DrvInfo & drv2)
{
int prio1, prio2;
if (!string2Int(drv1.queryMetaInfo(state, "priority"), prio1)) prio1 = 0;
if (!string2Int(drv2.queryMetaInfo(state, "priority"), prio2)) prio2 = 0;
return prio2 - prio1; /* higher number = lower priority, so negate */
return getPriority(state, drv2) - getPriority(state, drv1);
}
@@ -594,6 +601,13 @@ static void printMissing(EvalState & state, const DrvInfos & elems)
}
static bool keep(MetaInfo & meta)
{
MetaValue value = meta["keep"];
return value.type == MetaValue::tpString && value.stringValue == "true";
}
static void installDerivations(Globals & globals,
const Strings & args, const Path & profile)
{
@@ -628,7 +642,7 @@ static void installDerivations(Globals & globals,
MetaInfo meta = i->queryMetaInfo(globals.state);
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
meta["keep"] != "true")
!keep(meta))
printMsg(lvlInfo,
format("replacing old `%1%'") % i->name);
else
@@ -691,7 +705,7 @@ static void upgradeDerivations(Globals & globals,
DrvName drvName(i->name);
MetaInfo meta = i->queryMetaInfo(globals.state);
if (meta["keep"] == "true") {
if (keep(meta)) {
newElems.push_back(*i);
continue;
}
@@ -709,9 +723,9 @@ static void upgradeDerivations(Globals & globals,
if (newName.name == drvName.name) {
int d = comparePriorities(globals.state, *i, *j);
if (d == 0) d = compareVersions(drvName.version, newName.version);
if (upgradeType == utLt && d < 0 ||
upgradeType == utLeq && d <= 0 ||
upgradeType == utEq && d == 0 ||
if ((upgradeType == utLt && d < 0) ||
(upgradeType == utLeq && d <= 0) ||
(upgradeType == utEq && d == 0) ||
upgradeType == utAlways)
{
int d2 = -1;
@@ -770,7 +784,10 @@ static void setMetaFlag(EvalState & state, DrvInfo & drv,
const string & name, const string & value)
{
MetaInfo meta = drv.queryMetaInfo(state);
meta[name] = value;
MetaValue v;
v.type = MetaValue::tpString;
v.stringValue = value;
meta[name] = v;
drv.setMetaInfo(meta);
}
@@ -913,7 +930,7 @@ void printTable(Table & table)
vector<unsigned int> widths;
widths.resize(nrColumns);
for (Table::iterator i = table.begin(); i != table.end(); ++i) {
foreach (Table::iterator, i, table) {
assert(i->size() == nrColumns);
Strings::iterator j;
unsigned int column;
@@ -921,14 +938,15 @@ void printTable(Table & table)
if (j->size() > widths[column]) widths[column] = j->size();
}
for (Table::iterator i = table.begin(); i != table.end(); ++i) {
foreach (Table::iterator, i, table) {
Strings::iterator j;
unsigned int column;
for (j = i->begin(), column = 0; j != i->end(); ++j, ++column)
{
cout << *j;
for (j = i->begin(), column = 0; j != i->end(); ++j, ++column) {
string s = *j;
replace(s.begin(), s.end(), '\n', ' ');
cout << s;
if (column < nrColumns - 1)
cout << string(widths[column] - j->size() + 2, ' ');
cout << string(widths[column] - s.size() + 2, ' ');
}
cout << std::endl;
}
@@ -1075,9 +1093,7 @@ static void opQuery(Globals & globals,
XMLWriter xml(true, *(xmlOutput ? &cout : &dummy));
XMLOpenElement xmlRoot(xml, "items");
for (vector<DrvInfo>::iterator i = elems2.begin();
i != elems2.end(); ++i)
{
foreach (vector<DrvInfo>::iterator, i, elems2) {
try {
/* For table output. */
@@ -1164,7 +1180,8 @@ static void opQuery(Globals & globals,
if (printDescription) {
MetaInfo meta = i->queryMetaInfo(globals.state);
string descr = meta["description"];
MetaValue value = meta["description"];
string descr = value.type == MetaValue::tpString ? value.stringValue : "";
if (xmlOutput) {
if (descr != "") attrs["description"] = descr;
} else
@@ -1178,8 +1195,23 @@ static void opQuery(Globals & globals,
for (MetaInfo::iterator j = meta.begin(); j != meta.end(); ++j) {
XMLAttrs attrs2;
attrs2["name"] = j->first;
attrs2["value"] = j->second;
xml.writeEmptyElement("meta", attrs2);
if (j->second.type == MetaValue::tpString) {
attrs2["type"] = "string";
attrs2["value"] = j->second.stringValue;
xml.writeEmptyElement("meta", attrs2);
} else if (j->second.type == MetaValue::tpInt) {
attrs2["type"] = "int";
attrs2["value"] = (format("%1%") % j->second.intValue).str();
xml.writeEmptyElement("meta", attrs2);
} else if (j->second.type == MetaValue::tpStrings) {
attrs2["type"] = "strings";
XMLOpenElement m(xml, "meta", attrs2);
foreach (Strings::iterator, k, j->second.stringValues) {
XMLAttrs attrs3;
attrs3["value"] = *k;
xml.writeEmptyElement("string", attrs3);
}
}
}
}
else
@@ -1230,12 +1262,13 @@ static void switchGeneration(Globals & globals, int dstGen)
(dstGen >= 0 && i->number == dstGen))
dst = *i;
if (!dst)
if (!dst) {
if (dstGen == prevGen)
throw Error(format("no generation older than the current (%1%) exists")
% curGen);
else
throw Error(format("generation %1% does not exist") % dstGen);
}
printMsg(lvlInfo, format("switching from generation %1% to %2%")
% curGen % dst.number);

View File

@@ -34,6 +34,8 @@ struct Decoder
void pushChar(char c);
void finishLine();
void decodeFile(istream & st);
};
@@ -122,6 +124,7 @@ void Decoder::finishLine()
if (line[i] == '<') cout << "&lt;";
else if (line[i] == '&') cout << "&amp;";
else if (line[i] == '\r') ; /* ignore carriage return */
else if (line[i] < 32 && line[i] != 9) cout << "&#xfffd;";
else if (i + sz + 33 < line.size() &&
string(line, i, sz) == storeDir &&
@@ -158,16 +161,26 @@ void Decoder::finishLine()
}
void Decoder::decodeFile(istream & st)
{
int c;
cout << "<logfile>" << endl;
while ((c = st.get()) != EOF) {
pushChar(c);
}
if (line.size()) finishLine();
while (level--) cout << "</nest>" << endl;
cout << "</logfile>" << endl;
}
int main(int argc, char * * argv)
{
Decoder dec;
int c;
cout << "<logfile>" << endl;
while ((c = getchar()) != EOF) {
dec.pushChar(c);
}
cout << "</logfile>" << endl;
dec.decodeFile(cin);
}

View File

@@ -96,7 +96,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
if (isDerivation(*i)) drvPaths.insert(*i);
store->buildDerivations(drvPaths);
foreach (Strings::iterator, i,opArgs)
foreach (Strings::iterator, i, opArgs)
cout << format("%1%\n") % realisePath(*i);
}
@@ -125,11 +125,11 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
if (opArgs.empty())
throw UsageError("first argument must be hash algorithm");
string hashAlgo = opArgs.front();
HashType hashAlgo = parseHashType(opArgs.front());
opArgs.pop_front();
for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
cout << format("%1%\n") % store->addToStore(*i, true, recursive, hashAlgo);
cout << format("%1%\n") % store->addToStore(*i, recursive, hashAlgo);
}
@@ -151,14 +151,17 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs)
if (*i == "--recursive") recursive = true;
else throw UsageError(format("unknown flag `%1%'") % *i);
if (opArgs.size() != 3)
throw UsageError(format("`--print-fixed-path' requires three arguments"));
Strings::iterator i = opArgs.begin();
string hashAlgo = *i++;
HashType hashAlgo = parseHashType(*i++);
string hash = *i++;
string name = *i++;
cout << format("%1%\n") %
makeFixedOutputPath(recursive, hashAlgo,
parseHash16or32(parseHashType(hashAlgo), hash), name);
parseHash16or32(hashAlgo, hash), name);
}
@@ -528,7 +531,10 @@ static void opGC(Strings opFlags, Strings opArgs)
else if (*i == "--print-live") options.action = GCOptions::gcReturnLive;
else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead;
else if (*i == "--delete") options.action = GCOptions::gcDeleteDead;
else if (*i == "--max-freed") options.maxFreed = getIntArg(*i, i, opFlags.end());
else if (*i == "--max-freed") {
options.maxFreed = getIntArg(*i, i, opFlags.end());
if (options.maxFreed == 0) options.maxFreed = 1;
}
else if (*i == "--max-links") options.maxLinks = getIntArg(*i, i, opFlags.end());
else if (*i == "--use-atime") options.useAtime = true;
else if (*i == "--max-atime") {
@@ -614,16 +620,19 @@ static void opExport(Strings opFlags, Strings opArgs)
static void opImport(Strings opFlags, Strings opArgs)
{
bool requireSignature = false;
for (Strings::iterator i = opFlags.begin();
i != opFlags.end(); ++i)
foreach (Strings::iterator, i, opFlags)
if (*i == "--require-signature") requireSignature = true;
else throw UsageError(format("unknown flag `%1%'") % *i);
if (!opArgs.empty()) throw UsageError("no arguments expected");
FdSource source(STDIN_FILENO);
while (readInt(source) == 1)
while (true) {
unsigned long long n = readLongLong(source);
if (n == 0) break;
if (n != 1) throw Error("input doesn't look like something created by `nix-store --export'");
cout << format("%1%\n") % store->importPath(requireSignature, source) << std::flush;
}
}

View File

@@ -112,12 +112,12 @@ static void sigPollHandler(int sigNo)
_isInterrupted = 1;
blockInt = 1;
canSendStderr = false;
string s = "SIGPOLL\n";
write(STDERR_FILENO, s.c_str(), s.size());
const char * s = "SIGPOLL\n";
write(STDERR_FILENO, s, strlen(s));
}
} else {
string s = "spurious SIGPOLL\n";
write(STDERR_FILENO, s.c_str(), s.size());
const char * s = "spurious SIGPOLL\n";
write(STDERR_FILENO, s, strlen(s));
}
}
catch (Error & e) {
@@ -223,6 +223,43 @@ struct TunnelSource : Source
};
/* If the NAR archive contains a single file at top-level, then save
the contents of the file to `s'. Otherwise barf. */
struct RetrieveRegularNARSink : ParseSink
{
string s;
void createDirectory(const Path & path)
{
throw Error("regular file expected");
}
void receiveContents(unsigned char * data, unsigned int len)
{
s.append((const char *) data, len);
}
void createSymlink(const Path & path, const string & target)
{
throw Error("regular file expected");
}
};
/* Adapter class of a Source that saves all data read to `s'. */
struct SavingSourceAdapter : Source
{
Source & orig;
string s;
SavingSourceAdapter(Source & orig) : orig(orig) { }
void operator () (unsigned char * data, unsigned int len)
{
orig(data, len);
s.append((const char *) data, len);
}
};
static void performOp(unsigned int clientVersion,
Source & from, Sink & to, unsigned int op)
{
@@ -288,19 +325,33 @@ static void performOp(unsigned int clientVersion,
}
case wopAddToStore: {
/* !!! uberquick hack */
string baseName = readString(from);
bool fixed = readInt(from) == 1;
bool fixed = readInt(from) == 1; /* obsolete */
bool recursive = readInt(from) == 1;
string hashAlgo = readString(from);
Path tmp = createTempDir();
AutoDelete delTmp(tmp);
Path tmp2 = tmp + "/" + baseName;
restorePath(tmp2, from);
string s = readString(from);
/* Compatibility hack. */
if (!fixed) {
s = "sha256";
recursive = true;
}
HashType hashAlgo = parseHashType(s);
SavingSourceAdapter savedNAR(from);
RetrieveRegularNARSink savedRegular;
if (recursive) {
/* Get the entire NAR dump from the client and save it to
a string so that we can pass it to
addToStoreFromDump(). */
ParseSink sink; /* null sink; just parse the NAR */
parseDump(sink, savedNAR);
} else {
parseDump(savedRegular, from);
}
startWork();
Path path = store->addToStore(tmp2, fixed, recursive, hashAlgo);
Path path = dynamic_cast<LocalStore *>(store.get())
->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
stopWork();
writeString(path, to);
@@ -401,6 +452,10 @@ static void performOp(unsigned int clientVersion,
options.ignoreLiveness = readInt(from);
options.maxFreed = readLongLong(from);
options.maxLinks = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 5) {
options.useAtime = readInt(from);
options.maxAtime = readInt(from);
}
GCResults results;

View File

@@ -1,46 +1,35 @@
TESTS_ENVIRONMENT = $(SHELL) -e
TESTS_ENVIRONMENT = $(bash) -e
extra1 = $(shell pwd)/test-tmp/shared
simple.sh substitutes.sh substitutes2.sh fallback.sh: simple.nix
dependencies.sh gc.sh nix-push.sh nix-pull.in logging.sh nix-build.sh install-package.sh check-refs.sh: dependencies.nix
locking.sh: locking.nix
parallel.sh: parallel.nix
build-hook.sh: build-hook.nix
gc-concurrent.sh: gc-concurrent.nix gc-concurrent2.nix
user-envs.sh: user-envs.nix
fixed.sh: fixed.nix
gc-runtime.sh: gc-runtime.nix
check-refs.sh: check-refs.nix
filter-source.sh: filter-source.nix
TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
locking.sh parallel.sh build-hook.sh substitutes.sh substitutes2.sh \
parallel.sh build-hook.sh substitutes.sh substitutes2.sh \
fallback.sh nix-push.sh gc.sh gc-concurrent.sh verify.sh nix-pull.sh \
referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
remote-store.sh export.sh
remote-store.sh export.sh export-graph.sh negative-caching.sh
XFAIL_TESTS =
include ../substitute.mk
$(TESTS): common.sh
$(TESTS): common.sh config.nix
EXTRA_DIST = $(TESTS) \
simple.nix.in simple.builder.sh \
config.nix.in \
simple.nix simple.builder.sh \
hash-check.nix \
dependencies.nix.in dependencies.builder*.sh \
locking.nix.in locking.builder.sh \
parallel.nix.in parallel.builder.sh \
build-hook.nix.in build-hook.hook.sh \
dependencies.nix dependencies.builder*.sh \
parallel.nix parallel.builder.sh \
build-hook.nix build-hook.hook.sh \
substituter.sh substituter2.sh \
gc-concurrent.nix.in gc-concurrent.builder.sh \
gc-concurrent2.nix.in gc-concurrent2.builder.sh \
user-envs.nix.in user-envs.builder.sh \
fixed.nix.in fixed.builder1.sh fixed.builder2.sh \
gc-runtime.nix.in \
check-refs.nix.in \
filter-source.nix.in \
gc-concurrent.nix gc-concurrent.builder.sh gc-concurrent2.builder.sh \
user-envs.nix user-envs.builder.sh \
fixed.nix fixed.builder1.sh fixed.builder2.sh \
gc-runtime.nix \
check-refs.nix \
filter-source.nix \
export-graph.nix \
negative-caching.nix \
$(wildcard lang/*.nix) $(wildcard lang/*.exp) $(wildcard lang/*.exp.xml) $(wildcard lang/*.flags) \
common.sh.in

View File

@@ -1,13 +1,28 @@
source common.sh
file=./add.sh
path1=$($nixstore --add ./dummy)
echo $path1
path=$($nixstore --add $file)
path2=$($nixstore --add-fixed sha256 --recursive ./dummy)
echo $path2
echo $path
if test "$path1" != "$path2"; then
echo "nix-store --add and --add-fixed mismatch"
exit 1
fi
hash=$($nixstore -q --hash $path)
path3=$($nixstore --add-fixed sha256 ./dummy)
echo $path3
test "$path1" != "$path3" || exit 1
echo $hash
path4=$($nixstore --add-fixed sha1 --recursive ./dummy)
echo $path4
test "$path1" != "$path4" || exit 1
test "$hash" = "sha256:$($nixhash --type sha256 --base32 $file)"
hash1=$($nixstore -q --hash $path1)
echo $hash1
hash2=$($nixhash --type sha256 --base32 ./dummy)
echo $hash2
test "$hash1" = "sha256:$hash2"

View File

@@ -11,11 +11,11 @@ outPath=$(sed 's/Derive(\[("out",\"\([^\"]*\)\".*/\1/' $drv)
echo "output path is $outPath" >&2
if $(echo $outPath | grep -q input-1); then
echo "accept" >&3
read x <&4
echo "# accept" >&2
read x
echo "got $x"
mkdir $outPath
echo "BAR" > $outPath/foo
else
echo "decline" >&3
echo "# decline" >&2
fi

21
tests/build-hook.nix Normal file
View File

@@ -0,0 +1,21 @@
with import ./config.nix;
let
input1 = mkDerivation {
name = "build-hook-input-1";
builder = ./dependencies.builder1.sh;
};
input2 = mkDerivation {
name = "build-hook-input-2";
builder = ./dependencies.builder2.sh;
};
in
mkDerivation {
name = "build-hook";
builder = ./dependencies.builder0.sh;
inherit input1 input2;
}

View File

@@ -1,28 +0,0 @@
let {
input1 = derivation {
name = "build-hook-input-1";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder1.sh];
PATH = "@testPath@";
};
input2 = derivation {
name = "build-hook-input-2";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder2.sh];
PATH = "@testPath@";
};
body = derivation {
name = "build-hook";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder0.sh];
PATH = "@testPath@";
inherit input1 input2;
};
}

View File

@@ -2,11 +2,7 @@ source common.sh
export NIX_BUILD_HOOK="build-hook.hook.sh"
drvPath=$($nixinstantiate build-hook.nix)
echo "derivation is $drvPath"
outPath=$($nixstore -quf "$drvPath")
outPath=$($nixbuild build-hook.nix)
echo "output path is $outPath"

58
tests/check-refs.nix Normal file
View File

@@ -0,0 +1,58 @@
with import ./config.nix;
rec {
dep = import ./dependencies.nix;
makeTest = nr: args: mkDerivation ({
name = "check-refs-" + toString nr;
} // args);
src = builtins.toFile "aux-ref" "bla bla";
test1 = makeTest 1 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
inherit dep;
};
test2 = makeTest 2 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link";
inherit dep;
};
test3 = makeTest 3 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
allowedReferences = [];
inherit dep;
};
test4 = makeTest 4 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
allowedReferences = [dep];
inherit dep;
};
test5 = makeTest 5 {
builder = builtins.toFile "builder.sh" "mkdir $out";
allowedReferences = [];
inherit dep;
};
test6 = makeTest 6 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
allowedReferences = [];
inherit dep;
};
test7 = makeTest 7 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
allowedReferences = ["out"];
inherit dep;
};
test8 = makeTest 8 {
builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link";
inherit dep;
};
}

View File

@@ -1,59 +0,0 @@
rec {
dep = import ./dependencies.nix;
makeTest = nr: args: derivation ({
name = "check-refs-" + toString nr;
system = "@system@";
builder = "@shell@";
PATH = "@testPath@";
} // args);
src = builtins.toFile "aux-ref" "bla bla";
test1 = makeTest 1 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link")];
inherit dep;
};
test2 = makeTest 2 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link")];
inherit dep;
};
test3 = makeTest 3 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link")];
allowedReferences = [];
inherit dep;
};
test4 = makeTest 4 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link")];
allowedReferences = [dep];
inherit dep;
};
test5 = makeTest 5 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out")];
allowedReferences = [];
inherit dep;
};
test6 = makeTest 6 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link")];
allowedReferences = [];
inherit dep;
};
test7 = makeTest 7 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link")];
allowedReferences = ["out"];
inherit dep;
};
test8 = makeTest 8 {
args = ["-e" "-x" (builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link")];
inherit dep;
};
}

View File

@@ -39,6 +39,11 @@ export dot=@dot@
export xmllint="@xmllint@"
export xmlflags="@xmlflags@"
export xsltproc="@xsltproc@"
export SHELL="@shell@"
# Hack to get "atdiff" to run on Cygwin (Windows looks for
# DLLs in $PATH).
export PATH=$aterm_bin/../lib:$PATH
export version=@version@
export system=@system@
@@ -54,6 +59,11 @@ readLink() {
ls -l "$1" | sed 's/.*->\ //'
}
clearProfiles() {
profiles="$NIX_STATE_DIR"/profiles
rm -f $profiles/*
}
clearStore() {
echo "clearing store..."
chmod -R +w "$NIX_STORE_DIR"
@@ -62,13 +72,16 @@ clearStore() {
rm -rf "$NIX_DB_DIR"
mkdir "$NIX_DB_DIR"
$nixstore --init
}
clearProfiles() {
profiles="$NIX_STATE_DIR"/profiles
rm -f $profiles/*
clearProfiles
rm -f "$NIX_STATE_DIR"/gcroots/auto/*
rm -f "$NIX_STATE_DIR"/gcroots/ref
}
clearManifests() {
rm -f $NIX_STATE_DIR/manifests/*
}
fail() {
echo "$1"
exit 1
}

17
tests/config.nix.in Normal file
View File

@@ -0,0 +1,17 @@
rec {
shell = "@shell@";
path = "@testPath@";
system = "@system@";
shared = "@extra1@";
mkDerivation = args:
derivation ({
inherit system;
builder = shell;
args = ["-e" args.builder];
PATH = path;
} // removeAttrs args ["builder"]);
}

View File

@@ -5,3 +5,5 @@ ln -s $input2 $out/input-2
# Self-reference.
ln -s $out $out/self
echo FOO

22
tests/dependencies.nix Normal file
View File

@@ -0,0 +1,22 @@
with import ./config.nix;
let {
input1 = mkDerivation {
name = "dependencies-input-1";
builder = ./dependencies.builder1.sh;
};
input2 = mkDerivation {
name = "dependencies-input-2";
builder = ./. ~ "dependencies.builder2.sh";
};
body = mkDerivation {
name = "dependencies";
builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
input1 = input1 + "/.";
inherit input2;
};
}

View File

@@ -1,29 +0,0 @@
let {
input1 = derivation {
name = "dependencies-input-1";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder1.sh];
PATH = "@testPath@";
};
input2 = derivation {
name = "dependencies-input-2";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" (./. ~ "dependencies.builder2.sh")];
PATH = "@testPath@";
};
body = derivation {
name = "dependencies";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" (./dependencies.builder0.sh + "/FOOBAR/../.")];
PATH = "@testPath@";
input1 = input1 + "/.";
inherit input2;
};
}

View File

@@ -1,5 +1,7 @@
source common.sh
clearStore
drvPath=$($nixinstantiate dependencies.nix)
echo "derivation is $drvPath"
@@ -13,7 +15,7 @@ if test -n "$dot"; then
$dot < $TEST_ROOT/graph
fi
outPath=$($nixstore -rvv "$drvPath")
outPath=$($nixstore -rvv "$drvPath") || fail "build failed"
# Test Graphviz graph generation.
$nixstore -q --graph "$outPath" > $TEST_ROOT/graph

29
tests/export-graph.nix Normal file
View File

@@ -0,0 +1,29 @@
with import ./config.nix;
rec {
printRefs =
''
echo $exportReferencesGraph
while read path; do
read drv
read nrRefs
echo "$path has $nrRefs references"
echo "$path" >> $out
for ((n = 0; n < $nrRefs; n++)); do read ref; echo "ref $ref"; test -e "$ref"; done
done < refs
'';
runtimeGraph = mkDerivation {
name = "dependencies";
builder = builtins.toFile "build-graph-builder" "${printRefs}";
exportReferencesGraph = ["refs" (import ./dependencies.nix)];
};
buildGraph = mkDerivation {
name = "dependencies";
builder = builtins.toFile "build-graph-builder" "${printRefs}";
exportReferencesGraph = ["refs" (import ./dependencies.nix).drvPath];
};
}

30
tests/export-graph.sh Normal file
View File

@@ -0,0 +1,30 @@
source common.sh
clearStore
clearProfiles
checkRef() {
$nixstore -q --references ./result | grep -q "$1" || fail "missing reference $1"
}
# Test the export of the runtime dependency graph.
outPath=$($nixbuild ./export-graph.nix -A runtimeGraph)
test $($nixstore -q --references ./result | wc -l) = 2 || fail "bad nr of references"
checkRef input-2
for i in $(cat $outPath); do checkRef $i; done
# Test the export of the build-time dependency graph.
$nixstore --gc # should force rebuild of input-1
outPath=$($nixbuild ./export-graph.nix -A buildGraph)
checkRef input-1
checkRef input-1.drv
checkRef input-2
checkRef input-2.drv
for i in $(cat $outPath); do checkRef $i; done

View File

@@ -2,7 +2,7 @@ source common.sh
clearStore
outPath=$($nixstore -r $($nixinstantiate dependencies.nix))
outPath=$($nixbuild dependencies.nix)
$nixstore --export $outPath > $TEST_ROOT/exp

View File

@@ -1,8 +1,8 @@
derivation {
with import ./config.nix;
mkDerivation {
name = "filter";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" (builtins.toFile "builder" "PATH=@testPath@; ln -s $input $out")];
builder = builtins.toFile "builder" "ln -s $input $out";
input =
let filter = path: type:
type != "symlink"

View File

@@ -1,14 +1,13 @@
with import ./config.nix;
rec {
f2 = dummy: builder: mode: algo: hash: derivation {
f2 = dummy: builder: mode: algo: hash: mkDerivation {
name = "fixed";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" builder];
inherit builder;
outputHashMode = mode;
outputHashAlgo = algo;
outputHash = hash;
PATH = "@testPath@";
inherit dummy;
impureEnvVars = ["IMPURE_VAR1" "IMPURE_VAR2"];
};
@@ -20,7 +19,6 @@ rec {
(f ./fixed.builder1.sh "flat" "sha1" "a0b65939670bc2c010f4d5d6a0b3e4e4590fb92b")
(f ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
(f ./fixed.builder2.sh "recursive" "sha1" "vw46m23bizj4n8afrc0fj19wrp7mj3c0")
(f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik")
];
good2 = [
@@ -30,6 +28,9 @@ rec {
(f ./fixed.builder2.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858")
];
sameAsAdd =
f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik";
bad = [
(f ./fixed.builder1.sh "flat" "md5" "0ddd8be4b179a529afa5f2ffae4b9858")
];

View File

@@ -6,22 +6,16 @@ export IMPURE_VAR1=foo
export IMPURE_VAR2=bar
echo 'testing good...'
drvs=$($nixinstantiate fixed.nix -A good)
echo $drvs
$nixstore -r $drvs
$nixbuild fixed.nix -A good
echo 'testing good2...'
drvs=$($nixinstantiate fixed.nix -A good2)
echo $drvs
$nixstore -r $drvs
$nixbuild fixed.nix -A good2
echo 'testing bad...'
drvs=$($nixinstantiate fixed.nix -A bad)
echo $drvs
if $nixstore -r $drvs; then false; fi
$nixbuild fixed.nix -A bad && fail "should fail"
echo 'testing reallyBad...'
if $nixinstantiate fixed.nix -A reallyBad; then false; fi
$nixinstantiate fixed.nix -A reallyBad && fail "should fail"
# While we're at it, check attribute selection a bit more.
echo 'testing attribute selection...'
@@ -31,6 +25,28 @@ test $($nixinstantiate fixed.nix -A good.1 | wc -l) = 1
# Only one should run at the same time.
echo 'testing parallelSame...'
clearStore
drvs=$($nixinstantiate fixed.nix -A parallelSame)
echo $drvs
$nixstore -r $drvs -j2
$nixbuild fixed.nix -A parallelSame -j2
# Fixed-output derivations with a recursive SHA-256 hash should
# produce the same path as "nix-store --add".
echo 'testing sameAsAdd...'
out=$($nixbuild fixed.nix -A sameAsAdd)
# This is what fixed.builder2 produces...
rm -rf $TEST_ROOT/fixed
mkdir $TEST_ROOT/fixed
mkdir $TEST_ROOT/fixed/bla
echo "Hello World!" > $TEST_ROOT/fixed/foo
ln -s foo $TEST_ROOT/fixed/bar
out2=$($nixstore --add $TEST_ROOT/fixed)
echo $out2
test "$out" = "$out2" || exit 1
out3=$($nixstore --add-fixed --recursive sha256 $TEST_ROOT/fixed)
echo $out3
test "$out" = "$out3" || exit 1
out4=$($nixstore --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed)
echo $out4
test "$out" = "$out4" || exit 1

27
tests/gc-concurrent.nix Normal file
View File

@@ -0,0 +1,27 @@
with import ./config.nix;
rec {
input1 = mkDerivation {
name = "dependencies-input-1";
builder = ./dependencies.builder1.sh;
};
input2 = mkDerivation {
name = "dependencies-input-2";
builder = ./dependencies.builder2.sh;
};
test1 = mkDerivation {
name = "gc-concurrent";
builder = ./gc-concurrent.builder.sh;
inherit input1 input2;
};
test2 = mkDerivation {
name = "gc-concurrent2";
builder = ./gc-concurrent2.builder.sh;
inherit input1 input2;
};
}

View File

@@ -1,28 +0,0 @@
let {
input1 = derivation {
name = "dependencies-input-1";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder1.sh];
PATH = "@testPath@";
};
input2 = derivation {
name = "dependencies-input-2";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder2.sh];
PATH = "@testPath@";
};
body = derivation {
name = "gc-concurrent";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./gc-concurrent.builder.sh];
PATH = "@testPath@";
inherit input1 input2;
};
}

View File

@@ -1,27 +1,34 @@
source common.sh
$NIX_BIN_DIR/nix-collect-garbage -vvvvv
clearStore
drvPath1=$($nixinstantiate gc-concurrent.nix)
drvPath1=$($nixinstantiate gc-concurrent.nix -A test1)
outPath1=$($nixstore -q $drvPath1)
drvPath2=$($nixinstantiate gc-concurrent2.nix)
drvPath2=$($nixinstantiate gc-concurrent.nix -A test2)
outPath2=$($nixstore -q $drvPath2)
rm -f "$NIX_STATE_DIR"/gcroots/foo
drvPath3=$($nixinstantiate simple.nix)
outPath3=$($nixstore -r $drvPath3)
! test -e $outPath3.lock
touch $outPath3.lock
rm -f "$NIX_STATE_DIR"/gcroots/foo*
ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo
ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2
# Start build #1 in the background. It starts immediately.
$nixstore -rvv "$drvPath1" &
pid1=$!
# Start build #2 in the background after 6 seconds.
(sleep 6 && $nixstore -rvv "$drvPath2") &
# Start build #2 in the background after 10 seconds.
(sleep 10 && $nixstore -rvv "$drvPath2") &
pid2=$!
# Run the garbage collector while the build is running.
sleep 4
$NIX_BIN_DIR/nix-collect-garbage -vvvvv
sleep 6
$NIX_BIN_DIR/nix-collect-garbage
# Wait for build #1/#2 to finish.
echo waiting for pid $pid1 to finish...
@@ -39,4 +46,13 @@ cat $outPath1/input-2/bar
# derivation is a GC root.
cat $outPath2/foobar
rm "$NIX_STATE_DIR"/gcroots/foo
rm -f "$NIX_STATE_DIR"/gcroots/foo*
# The collector should have deleted lock files for paths that have
# been built previously.
! test -e $outPath3.lock
# If we run the collector now, it should delete outPath1/2.
$NIX_BIN_DIR/nix-collect-garbage
! test -e $outPath1
! test -e $outPath2

View File

@@ -1,28 +0,0 @@
let {
input1 = derivation {
name = "dependencies-input-1";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder1.sh];
PATH = "@testPath@";
};
input2 = derivation {
name = "dependencies-input-2";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./dependencies.builder2.sh];
PATH = "@testPath@";
};
body = derivation {
name = "gc-concurrent2";
system = "@system@";
builder = "@shell@";
args = ["-e" "-x" ./gc-concurrent2.builder.sh];
PATH = "@testPath@";
inherit input1 input2;
};
}

17
tests/gc-runtime.nix Normal file
View File

@@ -0,0 +1,17 @@
with import ./config.nix;
mkDerivation {
name = "gc-runtime";
builder =
# Test inline source file definitions.
builtins.toFile "builder.sh" ''
mkdir $out
cat > $out/program <<EOF
#! ${shell}
sleep 10000
EOF
chmod +x $out/program
'';
}

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