Compare commits

..

75 Commits

Author SHA1 Message Date
Eelco Dolstra
a10da8466f * Option to turn off position information to test the impact on
maximal sharing.
2007-10-17 12:36:37 +00:00
Eelco Dolstra
e23d134b85 * Memoize the substitution function.
* Print some substitution statistics.
* Option to turn off the closed term optimization.
2007-10-15 12:08:31 +00:00
Eelco Dolstra
c3a79daaf3 * Short-circuiting of function call evaluation.
With maximal laziness, you would expect that a function like this

    fib = n:
      if n == 0 then 0 else
      if n == 1 then 1 else
      builtins.add (fib (builtins.sub n 1)) (fib (builtins.sub n 2));

  can be evaluated efficiently, because maximal laziness should
  implictly memoize the recursive calls to "fib".  However, non-strictness
  interferes with this: the argument "n" is generally not in a form
  that allows the memoization to work (e.g., it will be something like
  (20 - 1 - 2 - 2) rather than 15).  By the time that "n" is
  evaluated (in "if n == 0 ..."), we're already deep in the evaluation
  of the call.

  (Strictness solves this:

      builtins.add (strict fib (builtins.sub n 1)) (strict fib (builtins.sub n 2));

  but that's not a very nice approach.)

  With short-circuiting, the evaluator will check after evaluating a
  term, whether that term is the argument of a function call that
  we're currently evaluating.  If so, it will check to see if the same
  call but with the evaluated argument is in the normal form cache.

  For instance, after evaluating (20 - 1 - 2 - 2) to 15, if we see
  that "fib (20 - 1 - 2 - 2)" is currently being evaluated, we check
  to see if "fib 15" is in the normal form cache.  If so, we unwind
  the stack (by throwing an exception) up to the evalExpr call
  responsible for "fib (20 - 1 - 2 - 2)", which can then immediately
  return the normal form for "fib 15".  And indeed this makes "fib"
  run in O(n) time.

  The overhead for checking the active function calls (which isn't
  very smart yet) seems to be modest, about 2% for "nix-env -qa
  --drv-path --out-path" on Nixpkgs.
2007-10-12 17:53:47 +00:00
Eelco Dolstra
74ce938e18 * Simulate conventional laziness a bit better still by "allocating"
all local variables when entering a new scope.  I.e., don't do
  implicit let-floating.
2007-10-11 22:42:09 +00:00
Eelco Dolstra
981afe821c * Some hacks to simulate conventional laziness. 2007-10-11 21:58:37 +00:00
Eelco Dolstra
cd9d10d4e3 * Caching of parse results for fairer comparisons. 2007-10-11 20:02:08 +00:00
Eelco Dolstra
c1179badd5 * Playing with strictness. 2007-10-11 14:07:00 +00:00
Eelco Dolstra
3d14ed9270 * A primop for calling functions strictly (i.e. forcing evaluation of
argument).  Necessary to actually get memoisation of functions like
  "fib" with maximal laziness.
2007-10-11 14:06:43 +00:00
Eelco Dolstra
8e0488370d * Environment variable to disable normal form caching. 2007-10-11 12:09:06 +00:00
Eelco Dolstra
b57f8bd38d * PEPM related hacks. 2007-10-11 12:07:49 +00:00
Eelco Dolstra
14e47e9c2c * Doh. 2007-10-10 14:58:00 +00:00
Eelco Dolstra
d04291cfab * Doh! Don't change the permissions on /nix/store. 2007-10-10 13:52:38 +00:00
Eelco Dolstra
315183f194 * nix-store --optimise: flag "--dry-run" to just query what the disk
savings would be.
2007-10-10 13:43:04 +00:00
Eelco Dolstra
a8629de827 * New command `nix-store --optimise' to reduce Nix store disk space
usage by finding identical files in the store and hard-linking them
  to each other.  It typically reduces the size of the store by
  something like 25-35%.  This is what the optimise-store.pl script
  did, but the new command is faster and more correct (it's safe wrt
  garbage collection and concurrent builds).
2007-10-09 22:14:27 +00:00
Eelco Dolstra
27a0662828 * listToAttrs: the list now should consist of {name, value} attribute
sets instead of {attr, value}.  "name" is better than "attr" because
  the *combination* of the two forms the attribute.
2007-10-09 12:51:25 +00:00
Eelco Dolstra
8a9fe6c11c * Manpage for nix-copy-closure. 2007-09-19 14:01:41 +00:00
Eelco Dolstra
27f57c9018 * Ignore dangling symlinks in ~/.nix-defexpr. 2007-09-18 14:01:14 +00:00
Eelco Dolstra
f3441e6122 * Pass various options to the worker so that flags like -K or -j work
in multi-user Nix (NIX-72).
* Client/worker: exchange a protocol version number for future
  compatibility.
2007-09-18 09:11:20 +00:00
Eelco Dolstra
26f981c2e5 * Remove garbage. 2007-09-18 08:26:55 +00:00
Eelco Dolstra
c8ea8a09b8 * nix-env -qa: make the "-A" flag do the expected thing, namely follow
the given attribute path (just as -A does with other option)
  (NIX-83).  So you can now say

  $ nix-env -qa -A nixpkgs_unstable.gnome \*
  atk-1.12.4
  esound-0.2.36
  ...

  to see the packages in the "gnome" attribute in Nixpkgs.

  To *print* the attribute path, you should now use "--attr-path" /
  "-P" (running out of letters...).
2007-09-17 19:24:07 +00:00
Eelco Dolstra
055608227f * nix-env: allow ~/.nix-defexpr to be a directory. If it is, then the
Nix expressions in that directory are combined into an attribute set
  {file1 = import file1; file2 = import file2; ...}, i.e. each Nix
  expression is an attribute with the file name as the attribute
  name.  Also recurses into directories.

* nix-env: removed the "--import" (-I) option which set the
  ~/.nix-defexpr symlink.

* nix-channel: don't use "nix-env --import", instead symlink
  ~/.nix-defexpr/channels.  So finally nix-channel --update doesn't
  override any default Nix expressions but combines with them.

  This means that you can have (say) a local Nixpkgs SVN tree and use
  it as a default for nix-env:

  $ ln -s .../path-to-nixpkgs-tree ~/.nix-defexpr/nixpkgs_svn

  and be subscribed to channels (including Nixpkgs) at the same time.
  (If there is any ambiguity, the -A flag can be used to
  disambiguate, e.g. "nix-env -i -A nixpkgs_svn.pan".)
2007-09-17 16:08:24 +00:00
Eelco Dolstra
3339f85447 * Test the impureEnvVars feature. 2007-09-11 13:32:04 +00:00
Eelco Dolstra
9441550acb * nix-push / generate-patches: bzip the manifest. 2007-09-04 15:38:09 +00:00
Marc Weber
7b20c0ed4b explanation what happens when passing true / false and null values to derivation builders added 2007-09-02 10:36:59 +00:00
Eelco Dolstra
0d65fc08e2 * Create the Nix daemon socket in a separate directory
(/nix/var/nix/daemon-socket).  This allows access to the Nix daemon
  to be restricted by setting the mode/ownership on that directory as
  desired, e.g.

    $ chmod 770 /nix/var/nix/daemon-socket
    $ chown root.wheel /nix/var/nix/daemon-socket

  to allow only users in the wheel group to use Nix.

  Setting the ownership on a socket is much trickier, since the socket
  must be deleted and recreated every time the daemon is started
  (which would require additional Nix configuration file directives to
  specify the mode/ownership, and wouldn't support arbitrary ACLs),
  some BSD variants appear to ignore permissions on sockets, and it's
  not clear whether the umask is respected on every platform when
  creating sockets.
2007-08-30 09:50:44 +00:00
Eelco Dolstra
cb1c1004cd * When there are multiple substituters, make sure to release the
lock on the output path after trying each.  Otherwise the
  pathIsLockedByMe() test gets confused.
2007-08-28 16:22:08 +00:00
Eelco Dolstra
c970b28ba0 * Fix a race condition with parallel builds where multiple
fixed-output derivations or substitutions try to build the same
  store path at the same time.  Locking generally catches this, but
  not between multiple goals in the same process.  This happened
  especially often (actually, only) in the build farm with fetchurl
  downloads of the same file being executed on multiple machines and
  then copied back to the main machine where they would clobber each
  other (NIXBF-13).

  Solution: if a goal notices that the output path is already locked,
  then go to sleep until another goal finishes (hopefully the one
  locking the path) and try again.
2007-08-28 11:36:17 +00:00
Eelco Dolstra
bc0429b1cd * Doh! Broken test. 2007-08-28 11:31:44 +00:00
Eelco Dolstra
e0d7e47862 * PathLocks::lockPaths: don't allow reacquiring a lock we already
hold.
2007-08-28 09:39:03 +00:00
Eelco Dolstra
455a7b9577 * Test case to show that parallel builds of different fixed-output
derivations that produce the same output path don't work properly
  wrt locking.  This happens a lot in the build farm when fetchurl
  derivations downloading the same file on different platforms are
  executed in parallel and then copied back to the main machine.
2007-08-28 09:21:47 +00:00
Eelco Dolstra
7d5836b34d * nix-channel: supports users who don't have write permission to the
manifests directory.  In that case, we don't do a nix-pull, so the
  user gets pure source deployment.

  The directory /nix/var/nix/gcroots/per-user/$USER should be
  writable.  (It's created automatically if
  /nix/var/nix/gcroots/per-user is writable, e.g. if it has 1777
  permission.)
2007-08-22 14:52:22 +00:00
Marc Weber
2629998e91 primop functions listToAttrs (+test), __isAttrs, __trace added
new configuration style proposal in lib/default-unstable.nix
2007-08-18 22:12:00 +00:00
Eelco Dolstra
dbc0170ed9 * Show errors in nix-prefetch-url. 2007-08-15 09:24:06 +00:00
Eelco Dolstra
5c793ad03e * Hopefully this fixes the test on FreeBSD. 2007-08-14 13:43:51 +00:00
Eelco Dolstra
ffa1c61cd5 * Fix the tests. 2007-08-14 13:15:59 +00:00
Eelco Dolstra
a7e1a51fdf * A test for the nix-worker. 2007-08-13 14:56:40 +00:00
Eelco Dolstra
550ba9ebb4 * Fix the tests. 2007-08-13 13:15:02 +00:00
Eelco Dolstra
3757ee589f * Bump the Nix database schema version number; delete the substitutes
table.
2007-08-13 11:37:39 +00:00
Eelco Dolstra
59afc1a15c * Backwards compatibility. 2007-08-13 11:15:13 +00:00
Eelco Dolstra
9e975458b4 * Get rid of the substitutes database table (NIX-47). Instead, if we
need any info on substitutable paths, we just call the substituters
  (such as download-using-manifests.pl) directly.  This means that
  it's no longer necessary for nix-pull to register substitutes or for
  nix-channel to clear them, which makes those operations much faster
  (NIX-95).  Also, we don't have to worry about keeping nix-pull
  manifests (in /nix/var/nix/manifests) and the database in sync with
  each other.

  The downside is that there is some overhead in calling an external
  program to get the substitutes info.  For instance, "nix-env -qas"
  takes a bit longer.

  Abolishing the substitutes table also makes the logic in
  local-store.cc simpler, as we don't need to store info for invalid
  paths.  On the downside, you cannot do things like "nix-store -qR"
  on a substitutable but invalid path (but nobody did that anyway).

* Never catch interrupts (the Interrupted exception).
2007-08-12 00:29:28 +00:00
Eelco Dolstra
4695f4edd6 * nix-pull: support bzipped manifests: when doing a nix-pull on $url,
try $url.bz2 first.
2007-08-10 01:42:00 +00:00
Eelco Dolstra
911bc01454 * Enable nix-prefetch-url caching in nix-channel. 2007-08-10 00:28:44 +00:00
Eelco Dolstra
5e52df18fe * Don't rely on /dev/stdin. 2007-08-10 00:22:21 +00:00
Eelco Dolstra
c5e934dcc9 * nix-pull: using nix-prefetch-url (so that we get caching for free),
and store the manifests in the Nix store.  (So now
  /nix/var/nix/manifests/ just contains symlinks to the store and is
  searched for GC roots.)
2007-08-09 23:52:53 +00:00
Eelco Dolstra
f881f7a017 * nix-prefetch-url: support caching. If the environment variable
NIX_DOWNLOAD_CACHE is set, then nix-prefetch-url will store the hash
  and timestamp of downloaded files in the directory
  $NIX_DOWNLOAD_CACHE.  This allows it to figure out if the file is
  still in the Nix store.
2007-08-09 23:16:44 +00:00
Eelco Dolstra
ef240bc0d5 * Use the new patched version of the aterm library. 2007-08-07 23:40:39 +00:00
Eelco Dolstra
d71cc503a6 * Don't allocate input files on the stack. 2007-08-07 15:00:13 +00:00
Eelco Dolstra
4cad125e08 * Optionally warn about packages that give an assertion failure. 2007-08-06 16:08:09 +00:00
Eelco Dolstra
6da6fbfbe9 * Properly keep packages during upgrades. 2007-07-05 20:33:04 +00:00
Eelco Dolstra
6d1a1191b0 * Support queryDeriver() in multi-user installations. 2007-06-12 16:53:44 +00:00
Eelco Dolstra
9d9e1c5c41 * Distribute bzip2 1.0.4. 2007-06-11 13:59:07 +00:00
Eelco Dolstra
9bff7ad728 * Check against creation of GC roots in the store. Those roots don't
work, because findRoots() stops when it encounters a symlink to the
  store.  And of course the store is supposed to be read-only.
2007-06-11 11:36:22 +00:00
Eelco Dolstra
f3ebd03bb1 * Remove debug statement. 2007-06-04 12:03:33 +00:00
Armijn Hemel
40b6f06f09 change default NIX_HASH_ALGO 2007-06-02 15:03:54 +00:00
Eelco Dolstra
b47da5ea21 2007-05-16 22:07:39 +00:00
Eelco Dolstra
bddc83a148 * New builtin function "isFunction". You're not supposed to use it
;-)
* Channels: fix channels that are plain lists of derivations (like
  strategoxt-unstable) instead  of functions (like nixpkgs-unstable).
  This fixes the error message "error: the left-hand side of the
  function call is neither a function nor a primop (built-in
  operation) but a list".
2007-05-16 16:17:04 +00:00
Eelco Dolstra
ca00aa1171 * Allow empty argument lists in function definitions (e.g., `{}:
bla').  Also allow trailing commas (`{x, y,}: ...') as a unintented
  consequence.  Hopefully the reduce/reduce conflict won't cause any
  problems.
2007-05-15 12:14:37 +00:00
Eelco Dolstra
7046c35843 * Typo (reported by Marc Weber). 2007-05-14 12:16:41 +00:00
Eelco Dolstra
e5da9c8803 * Create the database directory if it doesn't exist. 2007-05-07 16:59:19 +00:00
Eelco Dolstra
30beeb27a9 * Set the right priorities when recovering from a directory
collision.
2007-05-02 19:38:02 +00:00
Eelco Dolstra
34d4c9388b 2007-05-02 16:06:04 +00:00
Eelco Dolstra
93aefd9fc0 * Give unpacked channels more sensible names than 0, 1, ... They now
get the basename of the channel URL (e.g., nixpkgs-unstable).  The
  top-level Nix expression of the channel is now an attribute set, the
  attributes of which are the individual channels (e.g.,
  {nixpkgs_unstable = ...; strategoxt_unstable = ...}).  This makes
  attribute paths ("nix-env -qaA" and "nix-env -iA") more sensible,
  e.g., "nix-env -iA nixpkgs_unstable.subversion".
2007-05-01 23:16:38 +00:00
Eelco Dolstra
a9d15d4f43 * nix-env -i: instead of breaking package ties by version, break them
by priority and version install.  That is, if there are multiple
  packages with the same name, then pick the package with the highest
  priority, and only use the version if there are multiple packages
  with the same priority.

  This makes it possible to mark specific versions/variant in Nixpkgs
  more or less desirable than others.  A typical example would be a
  beta version of some package (e.g., "gcc-4.2.0rc1") which should not
  be installed even though it is the highest version, except when it
  is explicitly selected (e.g., "nix-env -i gcc-4.2.0rc1").

* Idem for nix-env -u, only the semantics are a bit trickier since we
  also need to take into account the priority of the currently
  installed package (we never upgrade to a lower priority, unless
  --always is given).
2007-05-01 20:33:18 +00:00
Eelco Dolstra
cbfac2fdcc * Set a terminate() handler to ensure that we leave the BDB
environment cleanly even when an exception is thrown from a
  destructor.  We still crash, but we don't take all other Nix
  processes with us.
2007-05-01 15:16:17 +00:00
Eelco Dolstra
644946feed * Make --verify more interruptable. 2007-05-01 13:21:05 +00:00
Eelco Dolstra
bd1f66453a * `nix-env -q --xml --meta' to show all meta attributes. 2007-05-01 11:30:52 +00:00
Eelco Dolstra
e20f0da22c * Doh. 2007-04-30 18:41:27 +00:00
Eelco Dolstra
89c1d2b202 * Package flag "keep" that prevents a package from being removed from
a user environment by an install or upgrade action.  This is
  particularly useful if you have a version installed that you don't
  want to upgrade (e.g., because the newer versions are broken).
  Example:

    $ nix-env -u zapping --dry-run
    (dry run; not doing anything)
    upgrading `zapping-0.9.6' to `zapping-0.10cvs6'

    $ nix-env --set-flag keep true zapping

    $ nix-env -u zapping --dry-run
    (dry run; not doing anything)

  However, "-e" will still uninstall the package.  (Maybe we should
  require the keep flag to be explicitly set to false before it can be
  uninstalled.)
2007-04-27 23:48:14 +00:00
Eelco Dolstra
a46db5d013 * Package conflict resolution through priority levels. If there is a
user environment collission between two packages due to overlapping
  file names, then a package with a higher priority will overwrite the
  symlinks of a package with a lower priority.  E.g.,

    $ nix-env --set-flag priority 5 gcc
    $ nix-env --set-flag priority 10 binutils

  gives gcc a higher priority than binutils (higher number = lower
  priority).
2007-04-27 23:28:44 +00:00
Eelco Dolstra
3d05166086 * Allow conflicting packages to be kept in a user environment, and
allow switching between them (NIX-80).

  Example: two versions of Pan:

    $ nix-env -q pan
    pan-0.128
    pan-0.14.2.91

    $ readlink $(which pan)
    /nix/store/l38jrbilw269drpjkx7kinhrxj6fjh59-pan-0.14.2.91/bin/pan

  At most one of them can be active any given time.  Assuming than
  0.14.2.91 is active, you can active 0.128 as follows:

    $ nix-env --set-flag active false pan-0.14.2.91
    $ nix-env --set-flag active true pan-0.128

    $ readlink $(which pan)
    /nix/store/nziqwnlzy7xl385kglxhg75pfl5i936n-pan-0.128/bin/pan

  More flags to follow.
2007-04-27 22:40:59 +00:00
Eelco Dolstra
b7f0f65c19 * nix-env -q now has a flag --prebuilt-only (-b<) that causes nix-env
to show only those derivations whose output is already in the Nix
  store or that can be substituted (i.e., downloaded from somewhere).
  In other words, it shows the packages that can be installed “quickly”,
  i.e., don’t need to be built from source.
2007-04-26 14:20:31 +00:00
Eelco Dolstra
5dc05b76ab * Updated dependency information. 2007-04-16 16:08:44 +00:00
Eelco Dolstra
5f2492eaec * New primop "throw <string>" to throw an error. This is like abort,
only thrown errors are caught by the top-level derivation evaluation
  in nix-env -qa / -i.
2007-04-16 15:03:19 +00:00
Eelco Dolstra
0a8eeea9d8 * Remove a warning. 2007-04-16 14:45:25 +00:00
Eelco Dolstra
2716f9bc5f * It seems that svnversion prints a carriage return on Cygwin, so we
get a invalid #define VERSION.  Use "svnversion -n" to leave out the
  newline.  Fix provided by Marc Weber.
2007-04-16 12:00:13 +00:00
122 changed files with 2054 additions and 10616 deletions

View File

@@ -46,15 +46,9 @@ else
init-state:
endif
init-ext3cow-header-hack:
@echo "Symlinking ext3cow header file into src"
ln -sf $(ext3cowheader) src/libext3cow/
svn-revision:
svnversion . > svn-revision
all: init-ext3cow-header-hack
all-local: NEWS
NEWS: doc/manual/NEWS.txt

3
TODO
View File

@@ -1,3 +0,0 @@
- runtimeStateArgs now must be set to someting (or it will see it as a hardcoded path)
- import and export of state paths
-

View File

@@ -1,6 +1,6 @@
#! /bin/sh -e
mkdir -p config
libtoolize --force --copy
libtoolize --copy
aclocal
autoheader
automake --add-missing --copy

View File

@@ -1,4 +1,4 @@
AC_INIT(nix, 0.12)
AC_INIT(nix, 0.11)
AC_CONFIG_SRCDIR(README)
AC_CONFIG_AUX_DIR(config)
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
@@ -99,19 +99,9 @@ static char buf[1024];]],
AC_LANG_POP(C++)
# Check for chroot support (requires chroot() and bind mounts).
AC_CHECK_FUNCS([chroot])
AC_CHECK_HEADERS([sys/param.h], [], [], [])
AC_CHECK_HEADERS([sys/mount.h], [], [],
[#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
# endif
])
# Check for <locale>
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([locale], [], [], [])
AC_CHECK_HEADERS([locale])
AC_LANG_POP(C++)
@@ -124,7 +114,7 @@ fi
])
NEED_PROG(curl, curl)
NEED_PROG(shell, bash)
NEED_PROG(shell, sh)
NEED_PROG(patch, patch)
AC_PATH_PROG(xmllint, xmllint, false)
AC_PATH_PROG(xsltproc, xsltproc, false)
@@ -135,7 +125,6 @@ AC_PATH_PROG(bison, bison, false)
NEED_PROG(perl, perl)
NEED_PROG(tar, tar)
AC_PATH_PROG(dot, dot)
AC_PATH_PROG(dblatex, dblatex)
AC_PATH_PROG(openssl_prog, openssl, openssl) # if not found, call openssl in $PATH
AC_SUBST(openssl_prog)
@@ -176,11 +165,6 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
storedir=$withval, storedir='${prefix}/store')
AC_SUBST(storedir)
AC_ARG_WITH(store-state-dir, AC_HELP_STRING([--with-store-state-dir=PATH],
[path of the Nix state store]),
storestatedir=$withval, storestatedir='${prefix}/state')
AC_SUBST(storestatedir)
AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH],
[prefix of Berkeley DB]),
bdb=$withval, bdb=)
@@ -195,18 +179,6 @@ fi
AC_SUBST(bdb_lib)
AC_SUBST(bdb_include)
AC_ARG_WITH(ext3cow-header, AC_HELP_STRING([--with-ext3cow-header=PATH],
[path of the ext3cow header ext3cow_fs.h]),
ext3cowheader=$withval, ext3cowheader=)
AC_SUBST(ext3cowheader)
AC_CHECK_HEADER(${ext3cowheader})
NEED_PROG(rsync, rsync)
AC_ARG_WITH(rsync, AC_HELP_STRING([--with-rsync=PATH],
[path to the rsync binary.]),
rsync=$withval)
AC_SUBST(rsync)
AC_ARG_WITH(aterm, AC_HELP_STRING([--with-aterm=PATH],
[prefix of CWI ATerm library]),
aterm=$withval, aterm=)
@@ -273,10 +245,6 @@ AM_CONDITIONAL(INIT_STATE, test "$init_state" = "yes")
AC_CHECK_FUNCS([setresuid setreuid lchown])
# Nice to have, but not essential.
AC_CHECK_FUNCS([strsignal])
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
# and the Nix libraries are dynamic.
if test "$(uname)" = "Darwin"; then
@@ -294,9 +262,7 @@ AC_CONFIG_FILES([Makefile
src/libutil/Makefile
src/libstore/Makefile
src/libmain/Makefile
src/libext3cow/Makefile
src/nix-store/Makefile
src/nix-state/Makefile
src/nix-hash/Makefile
src/libexpr/Makefile
src/nix-instantiate/Makefile

View File

@@ -3,39 +3,24 @@
use strict;
use Cwd;
use IO::Handle;
use Fcntl;
STDOUT->autoflush(1);
my $out = $ENV{"out"};
mkdir "$out", 0755 || die "error creating $out";
sub readlink_or_StateWrapper;
my $symlinks = 0;
my %path_state_identifier = ();
my %priorities;
my $nixBinDir = $ENV{"nixBinDir"};
my $nixStore = $ENV{"nixStore"};
# For each activated package, create symlinks.
sub createLinks {
my $srcDir = shift;
#Lookup each $stateIdentifiers in $path_state_identifier
#we strip $srcDir to its rootdir e.g. /nix/store/......./
my @srcDirParts = split /\// , substr($srcDir, length ($nixStore), length ($srcDir));
my $srcDirRoot = $nixStore . "/" . $srcDirParts[1];
# print "srcDirRoot $srcDirRoot \n";
my $dstDir = shift;
my $priority = shift;
my $pkgStateIdentifier = $path_state_identifier{$srcDirRoot}; # We have to look it up each time since recursion can change the $srcDir, but not the identifier
#print "createLinks $srcDir to $dstDir with iden $pkgStateIdentifier \n";
my @srcFiles = glob("$srcDir/*");
@@ -44,21 +29,20 @@ sub createLinks {
$baseName =~ s/^.*\///g; # strip directory
my $dstFile = "$dstDir/$baseName";
# Urgh, hacky...
if ($srcFile =~ /\/propagated-build-inputs$/ ||
# Urgh, hacky...
if ($srcFile =~ /\/propagated-build-inputs$/ ||
$srcFile =~ /\/nix-support$/ ||
$srcFile =~ /\/perllocal.pod$/ ||
$srcFile =~ /\/info\/dir$/ ||
$srcFile =~ /\/log$/)
{
# Do nothing.
}
}
elsif (-d $srcFile) {
lstat $dstFile;
#go recursive on directorys
if (-d _) {
createLinks($srcFile, $dstFile, $priority);
}
@@ -69,15 +53,13 @@ sub createLinks {
die "collission between directory `$srcFile' and non-directory `$target'";
}
unlink $dstFile or die "error unlinking `$dstFile': $!";
mkdir $dstFile, 0755 || die "error creating directory `$dstFile': $!";
mkdir $dstFile, 0755 ||
die "error creating directory `$dstFile': $!";
createLinks($target, $dstFile, $priorities{$dstFile});
createLinks($srcFile, $dstFile, $priority);
}
else {
#print "1ST DIR LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n";
symlink($srcFile, $dstFile) ||
die "error creating link `$dstFile': $!";
$priorities{$dstFile} = $priority;
@@ -87,67 +69,23 @@ sub createLinks {
else {
# print "ELSE LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n";
# if we have a state component with a identifier different then ""
if($pkgStateIdentifier ne "__NOSTATE__" && $pkgStateIdentifier ne ""){
my @pathparts = split /\// , $srcFile;
my $parentDir = $pathparts[scalar(@pathparts) - 2];
if( $parentDir eq "bin" || $parentDir eq "sbin"){ #hacky....
print "STATELINK $srcFile to $dstFile - $pkgStateIdentifier \n";
my $new_dstFile;
my $new_stateIdentifier;
if($pkgStateIdentifier eq "__EMTPY__"){
$new_dstFile = $dstFile;
$new_stateIdentifier = "";
}
else{
$new_dstFile = "$dstFile-$pkgStateIdentifier";
$new_stateIdentifier = $pkgStateIdentifier;
}
# We also check with -e if the wrapperscript-file exists, and if is it a symlink (with -l)
if (-l $new_dstFile || -e $new_dstFile) {
my $target = readlink_or_StateWrapper $new_dstFile;
die "(state) collission between `$srcFile' and `$target' (over $new_dstFile)";
}
sysopen (DSTFILEHANDLE, $new_dstFile, O_RDWR|O_EXCL|O_CREAT, 0755);
printf DSTFILEHANDLE "#! @shell@ \n";
printf DSTFILEHANDLE "$nixBinDir/nix-state --run --identifier=$new_stateIdentifier $srcFile \"\$@\" \n";
close (DSTFILEHANDLE);
}
}
elsif($pkgStateIdentifier ne "__NOSTATE__" && $pkgStateIdentifier eq ""){ #TODO we now dont create symlinks for state packages with a empty identifier
#TODO but we must do it if there is no normal non-state pacakge
}
else {
if (-l $dstFile || -e $dstFile) {
my $target = readlink_or_StateWrapper $dstFile;
my $prevPriority = $priorities{$dstFile};
die ( "Collission between `$srcFile' and `$target'. "
. "Suggested solution: use `nix-env --set-flag "
. "priority NUMBER PKGNAME' to change the priority of "
. "one of the conflicting packages.\n" )
if $prevPriority == $priority;
next if $prevPriority < $priority;
unlink $dstFile or die;
}
# print "2ND LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n";
symlink($srcFile, $dstFile) ||
die "error creating link `$dstFile': $!";
$priorities{$dstFile} = $priority;
$symlinks++;
}
}
if (-l $dstFile) {
my $target = readlink $dstFile;
my $prevPriority = $priorities{$dstFile};
die ( "Collission between `$srcFile' and `$target'. "
. "Suggested solution: use `nix-env --set-flag "
. "priority NUMBER PKGNAME' to change the priority of "
. "one of the conflicting packages.\n" )
if $prevPriority == $priority;
next if $prevPriority < $priority;
unlink $dstFile or die;
}
symlink($srcFile, $dstFile) ||
die "error creating link `$dstFile': $!";
$priorities{$dstFile} = $priority;
$symlinks++;
}
}
}
@@ -178,24 +116,6 @@ sub addPkg {
}
}
sub readlink_or_StateWrapper {
my $src = shift;
my $target;
if (-l $src)
{ $target = readlink $src; }
else{
open(DAT, $src) || die("Could not open file!");
my @raw_data=<DAT>;
close(DAT);
$target = $raw_data[1];
}
return $target
}
my @stateIdentifiers = split ' ', $ENV{"stateIdentifiers"};
my $si_counter = 0;
# Convert the stuff we get from the environment back into a coherent
# data type.
@@ -211,23 +131,15 @@ my %pkgs;
for (my $n = 0; $n < scalar @paths; $n++) {
$pkgs{$paths[$n]} =
{ active => $active[$n]
, priority => $priority[$n]
, stateidentifier => $stateIdentifiers[$n]
};
$path_state_identifier{$paths[$n]} = $stateIdentifiers[$n];
, priority => $priority[$n] };
}
# Symlink to the packages that have been installed explicitly by the
# user.
foreach my $pkg (sort (keys %pkgs)) {
#print "SP: $pkg \n";
#print "SI: $pkgs{$pkg}->{stateidentifier} \n";
#print "PR: $pkgs{$pkg}->{priority} \n";
#print $pkg, " ", $pkgs{$pkg}->{priority}, "\n";
addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false";
$si_counter++;
}
@@ -237,7 +149,6 @@ foreach my $pkg (sort (keys %pkgs)) {
# priority in case of collisions.
my $priorityCounter = 1000; # don't care about collisions
while (scalar(keys %postponed) > 0) {
my @pkgDirs = keys %postponed;
%postponed = ();
foreach my $pkgDir (sort @pkgDirs) {
@@ -245,7 +156,8 @@ while (scalar(keys %postponed) > 0) {
}
}
print STDERR "created $symlinks symlinks in user environment\n";
symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";
symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";

View File

@@ -1,13 +1,11 @@
{system, derivations, stateIdentifiers, manifest, nixBinDir, nixStore}:
{system, derivations, manifest}:
derivation {
name = "user-environment";
system = system;
builder = ./builder.pl;
stateIdentifiers = stateIdentifiers;
manifest = manifest;
inherit nixBinDir nixStore;
# !!! grmbl, need structured data for passing this in a clean way.
paths = derivations;

View File

@@ -1,21 +0,0 @@
#! /bin/sh -e
dir1=releases
dir2=nix-state
mkdir -p $dir1
cd $dir1
rm -rf $dir2
mkdir -p $dir2
cd $dir2
svn co https://svn.cs.uu.nl:12443/repos/trace/nix/branches/state ./
revision=`svn info | grep ^Revision | sed 's/Revision: //g'`
cd ..
date=`date +%Y%m%d`
tarfile=snix-${date}-rev${revision}.tar.gz
tar -cvf $tarfile \
--preserve-permissions \
--atime-preserve \
--gzip \
--verbose \
--no-ignore-command-error \
$dir2/

View File

@@ -1 +1 @@
SUBDIRS =
SUBDIRS = manual

View File

@@ -20,7 +20,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
FIGURES = figures/user-environments.png
MANUAL_SRCS = manual.xml introduction.xml installation.xml \
package-management.xml writing-nix-expressions.xml builtins.xml \
package-management.xml writing-nix-expressions.xml \
build-farm.xml \
$(man1_MANS:.1=.xml) \
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
@@ -47,14 +47,6 @@ manual.html: $(MANUAL_SRCS) manual.is-valid images
$(XSLTPROC) --nonet --xinclude --output manual.html \
$(docbookxsl)/html/docbook.xsl manual.xml
manual.pdf: $(MANUAL_SRCS) manual.is-valid images
if test "$(dblatex)" != ""; then \
$(dblatex) manual.xml; \
else \
echo "Please install dblatex and rerun configure."; \
exit 1; \
fi
NEWS_OPTS = \
--stringparam generate.toc "article nop" \

View File

@@ -1 +0,0 @@
New state nix version by wouter ...

View File

@@ -36,10 +36,10 @@ build farm, since:
builds, and Nix expressions are self-contained.</para></listitem>
<listitem><para>Nix will only rebuild things that have actually
changed. For instance, if the sources of a package haven't changed
between runs of the build farm, the package won't be rebuilt (unless
it was garbage-collected). Also, dependencies typically don't
change very often, so they only need to be built
changed. For instance, if the sources of a component haven't
changed between runs of the build farm, the component won't be
rebuild (unless it was garbage-collected). Also, dependencies
typically don't change very often, so they only need to be built
once.</para></listitem>
<listitem><para>The results of a Nix build farm can be made

View File

@@ -1,760 +0,0 @@
<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id='ssec-builtins'>
<title>Built-in functions</title>
<para>This section lists the functions and constants built into the
Nix expression evaluator. (The built-in function
<function>derivation</function> is discussed above.) Some built-ins,
such as <function>derivation</function>, are always in scope of every
Nix expression; you can just access them right away. But to prevent
polluting the namespace too much, most built-ins are not in scope.
Instead, you can access them through the <varname>builtins</varname>
built-in value, which is an attribute set that contains all built-in
functions and values. For instance, <function>derivation</function>
is also available as <function>builtins.derivation</function>.</para>
<variablelist>
<varlistentry><term><function>abort</function> <replaceable>s</replaceable></term>
<listitem><para>Abort Nix expression evaluation, print error
message <replaceable>s</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.add</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the sum of the integers
<replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.attrNames</function>
<replaceable>attrs</replaceable></term>
<listitem><para>Return the names of the attributes in the
attribute set <replaceable>attrs</replaceable> in a sorted list.
For instance, <literal>builtins.attrNames {y = 1; x =
"foo";}</literal> evaluates to <literal>["x" "y"]</literal>.
There is no built-in function <function>attrValues</function>, but
you can easily define it yourself:
<programlisting>
attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs);</programlisting>
</para></listitem>
</varlistentry>
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
<listitem><para>Return the <emphasis>base name</emphasis> of the
string <replaceable>s</replaceable>, that is, everything following
the final slash in the string. This is similar to the GNU
<command>basename</command> command.</para></listitem>
</varlistentry>
<varlistentry><term><varname>builtins</varname></term>
<listitem><para>The attribute set <varname>builtins</varname>
contains all the built-in functions and values. You can use
<varname>builtins</varname> to test for the availability of
features in the Nix installation, e.g.,
<programlisting>
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>
</varlistentry>
<varlistentry
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
<listitem><para>The built-in value <varname>currentSystem</varname>
evaluates to the Nix platform identifier for the Nix installation
on which the expression is being evaluated, such as
<literal>"i686-linux"</literal> or
<literal>"powerpc-darwin"</literal>.</para></listitem>
</varlistentry>
<!--
<varlistentry><term><function>currentTime</function></term>
<listitem><para>The built-in value <varname>currentTime</varname>
returns the current system time in seconds since 00:00:00 1/1/1970
UTC. Due to the evaluation model of Nix expressions
(<emphasis>maximal laziness</emphasis>), it always yields the same
value within an execution of Nix.</para></listitem>
</varlistentry>
-->
<!--
<varlistentry><term><function>dependencyClosure</function></term>
<listitem><para>TODO</para></listitem>
</varlistentry>
-->
<varlistentry><term><function>derivation</function>
<replaceable>attrs</replaceable></term>
<listitem><para><function>derivation</function> is described in
<xref linkend='ssec-derivation' />.</para></listitem>
</varlistentry>
<varlistentry><term><function>dirOf</function> <replaceable>s</replaceable></term>
<listitem><para>Return the directory part of the string
<replaceable>s</replaceable>, that is, everything before the final
slash in the string. This is similar to the GNU
<command>dirname</command> command.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.filterSource</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem>
<para>This function allows you to copy sources into the Nix
store while filtering certain files. For instance, suppose that
you want to use the directory <filename>source-dir</filename> as
an input to a Nix expression, e.g.
<programlisting>
stdenv.mkDerivation {
...
src = ./source-dir;
}
</programlisting>
However, if <filename>source-dir</filename> is a Subversion
working copy, then all those annoying <filename>.svn</filename>
subdirectories will also be copied to the store. Worse, the
contents of those directories may change a lot, causing lots of
spurious rebuilds. With <function>filterSource</function> you
can filter out the <filename>.svn</filename> directories:
<programlisting>
src = builtins.filterSource
(path: type: type != "directory" || baseNameOf path != ".svn")
./source-dir;
</programlisting>
</para>
<para>Thus, the first argument <replaceable>e1</replaceable>
must be a predicate function that is called for each regular
file, directory or symlink in the source tree
<replaceable>e2</replaceable>. If the function returns
<literal>true</literal>, the file is copied to the Nix store,
otherwise it is omitted. The function is called with two
arguments. The first is the full path of the file. The second
is a string that identifies the type of the file, which is
either <literal>"regular"</literal>,
<literal>"directory"</literal>, <literal>"symlink"</literal> or
<literal>"unknown"</literal> (for other kinds of files such as
device nodes or fifos — but note that those cannot be copied to
the Nix store, so if the predicate returns
<literal>true</literal> for them, the copy will fail).</para>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.getAttr</function>
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
<listitem><para><function>getAttr</function> returns the attribute
named <replaceable>s</replaceable> from the attribute set
<replaceable>attrs</replaceable>. Evaluation aborts if the
attribute doesnt exist. This is a dynamic version of the
<literal>.</literal> operator, since <replaceable>s</replaceable>
is an expression rather than an identifier.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.getEnv</function>
<replaceable>s</replaceable></term>
<listitem><para><function>getEnv</function> returns the value of
the environment variable <replaceable>s</replaceable>, or an empty
string if the variable doesnt exist. This function should be
used with care, as it can introduce all sorts of nasty environment
dependencies in your Nix expression.</para>
<para><function>getEnv</function> is used in Nix Packages to
locate the file <filename>~/.nixpkgs/config.nix</filename>, which
contains user-local settings for Nix Packages. (That is, it does
a <literal>getEnv "HOME"</literal> to locate the users home
directory.)</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.hasAttr</function>
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
<listitem><para><function>hasAttr</function> returns
<literal>true</literal> if the attribute set
<replaceable>attrs</replaceable> has an attribute named
<replaceable>s</replaceable>, and <literal>false</literal>
otherwise. This is a dynamic version of the <literal>?</literal>
operator, since <replaceable>s</replaceable> is an expression
rather than an identifier.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.head</function>
<replaceable>list</replaceable></term>
<listitem><para>Return the first element of a list; abort
evaluation if the argument isnt a list or is an empty list. You
can test whether a list is empty by comparing it with
<literal>[]</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>import</function>
<replaceable>path</replaceable></term>
<listitem><para>Load, parse and return the Nix expression in the
file <replaceable>path</replaceable>. Evaluation aborts if the
file doesnt exist or contains an incorrect Nix
expression. <function>import</function> implements Nixs module
system: you can put any Nix expression (such as an attribute set
or a function) in a separate file, and use it from Nix expressions
in other files.</para>
<para>A Nix expression loaded by <function>import</function> must
not contain any <emphasis>free variables</emphasis> (identifiers
that are not defined in the Nix expression itself and are not
built-in). Therefore, it cannot refer to variables that are in
scope at the call site. For instance, if you have a calling
expression
<programlisting>
rec {
x = 123;
y = import ./foo.nix;
}</programlisting>
then the following <filename>foo.nix</filename> will give an
error:
<programlisting>
x + 456</programlisting>
since <varname>x</varname> is not in scope in
<filename>foo.nix</filename>. If you want <varname>x</varname>
to be available in <filename>foo.nix</filename>, you should pass
it as a function argument:
<programlisting>
rec {
x = 123;
y = import ./foo.nix x;
}</programlisting>
and
<programlisting>
x: x + 456</programlisting>
(The function argument doesnt have to be called
<varname>x</varname> in <filename>foo.nix</filename>; any name
would work.)</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isAttrs</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to an attribute set, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isList</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a list, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isFunction</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to a function, and
<literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>isNull</function>
<replaceable>e</replaceable></term>
<listitem><para>Return <literal>true</literal> if
<replaceable>e</replaceable> evaluates to <literal>null</literal>,
and <literal>false</literal> otherwise.</para>
<warning><para>This function is <emphasis>deprecated</emphasis>;
just write <literal>e == null</literal> instead.</para></warning>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.lessThan</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return <literal>true</literal> if the integer
<replaceable>e1</replaceable> is less than the integer
<replaceable>e2</replaceable>, and <literal>false</literal>
otherwise. Evaluation aborts if either
<replaceable>e1</replaceable> or <replaceable>e2</replaceable>
does not evaluate to an integer.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.listToAttrs</function>
<replaceable>e</replaceable></term>
<listitem><para>Construct an attribute set from a list specifying
the names and values of each attribute. Each element of the list
should be an attribute set consisting of a string-valued attribute
<varname>name</varname> specifying the name of the attribute, and
an attribute <varname>value</varname> specifying its value.
Example:
<programlisting>
builtins.listToAttrs [
{name = "foo"; value = 123;}
{name = "bar"; value = 456;}
]
</programlisting>
evaluates to
<programlisting>
{ foo = 123; bar = 456; }
</programlisting>
</para></listitem>
</varlistentry>
<varlistentry><term><function>map</function>
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Apply the function <replaceable>f</replaceable> to
each element in the list <replaceable>list</replaceable>. For
example,
<programlisting>
map (x: "foo" + x) ["bar" "bla" "abc"]</programlisting>
evaluates to <literal>["foobar" "foobla"
"fooabc"]</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.pathExists</function>
<replaceable>path</replaceable></term>
<listitem><para>Return <literal>true</literal> if the path
<replaceable>path</replaceable> exists, and
<literal>false</literal> otherwise. One application of this
function is to conditionally include a Nix expression containing
user configuration:
<programlisting>
let
fileName = builtins.getEnv "CONFIG_FILE";
config =
if fileName != "" &amp;&amp; builtins.pathExists (builtins.toPath fileName)
then import (builtins.toPath fileName)
else { someSetting = false; }; <lineannotation># default configuration</lineannotation>
in config.someSetting</programlisting>
(Note that <envar>CONFIG_FILE</envar> must be an absolute path for
this to work.)</para></listitem>
</varlistentry>
<!--
<varlistentry><term><function>relativise</function></term>
<listitem><para>TODO</para></listitem>
</varlistentry>
-->
<varlistentry><term><function>builtins.readFile</function>
<replaceable>path</replaceable></term>
<listitem><para>Return the contents of the file
<replaceable>path</replaceable> as a string.</para></listitem>
</varlistentry>
<varlistentry><term><function>removeAttrs</function>
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Remove the attributes listed in
<replaceable>list</replaceable> from the attribute set
<replaceable>attrs</replaceable>. The attributes dont have to
exist in <replaceable>attrs</replaceable>. For instance,
<screen>
removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"]</screen>
evaluates to <literal>{y = 2;}</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.stringLength</function>
<replaceable>e</replaceable></term>
<listitem><para>Return the length of the string
<replaceable>e</replaceable>. If <replaceable>e</replaceable> is
not a string, evaluation is aborted.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.sub</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Return the difference between the integers
<replaceable>e1</replaceable> and
<replaceable>e2</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.substring</function>
<replaceable>start</replaceable> <replaceable>len</replaceable>
<replaceable>s</replaceable></term>
<listitem><para>Return the substring of
<replaceable>s</replaceable> from character position
<replaceable>start</replaceable> (zero-based) up to but not
including <replaceable>start + len</replaceable>. If
<replaceable>start</replaceable> is greater than the length of the
string, an empty string is returned, and if <replaceable>start +
len</replaceable> lies beyond the end of the string, only the
substring up to the end of the string is returned.
<replaceable>start</replaceable> must be
non-negative.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.tail</function>
<replaceable>list</replaceable></term>
<listitem><para>Return the second to last elements of a list;
abort evaluation if the argument isnt a list or is an empty
list.</para></listitem>
</varlistentry>
<varlistentry><term><function>throw</function>
<replaceable>s</replaceable></term>
<listitem><para>Throw an error message
<replaceable>s</replaceable>. This usually aborts Nix expression
evaluation, but in <command>nix-env -qa</command> and other
commands that try to evaluate a set of derivations to get
information about those derivations, a derivation that throws an
error is silently skipped (which is not the case for
<function>abort</function>).</para></listitem>
</varlistentry>
<varlistentry
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
<listitem><para>Store the string <replaceable>s</replaceable> in a
file in the Nix store and return its path. The file has suffix
<replaceable>name</replaceable>. This file can be used as an
input to derivations. One application is to write builders
“inline”. For instance, the following Nix expression combines
<xref linkend='ex-hello-nix' /> and <xref
linkend='ex-hello-builder' /> into one file:
<programlisting>
{stdenv, fetchurl, perl}:
stdenv.mkDerivation {
name = "hello-2.1.1";
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
PATH=$perl/bin:$PATH
tar xvfz $src
cd hello-*
./configure --prefix=$out
make
make install
";
src = fetchurl {
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
md5 = "70c9ccf9fac07f762c24f2df2290784d";
};
inherit perl;
}</programlisting>
</para>
<para>It is even possible for one file to refer to another, e.g.,
<programlisting>
builder = let
configFile = builtins.toFile "foo.conf" "
# This is some dummy configuration file.
<replaceable>...</replaceable>
";
in builtins.toFile "builder.sh" "
source $stdenv/setup
<replaceable>...</replaceable>
cp ${configFile} $out/etc/foo.conf
";</programlisting>
Note that <literal>${configFile}</literal> is an antiquotation
(see <xref linkend='ssec-values' />), so the result of the
expression <literal>configFile</literal> (i.e., a path like
<filename>/nix/store/m7p7jfny445k...-foo.conf</filename>) will be
spliced into the resulting string.</para>
<para>It is however <emphasis>not</emphasis> allowed to have files
mutually referring to each other, like so:
<programlisting>
let
foo = builtins.toFile "foo" "...${bar}...";
bar = builtins.toFile "bar" "...${foo}...";
in foo</programlisting>
This is not allowed because it would cause a cyclic dependency in
the computation of the cryptographic hashes for
<varname>foo</varname> and <varname>bar</varname>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
<listitem><para>Convert the string value
<replaceable>s</replaceable> into a path value. The string
<replaceable>s</replaceable> must represent an absolute path
(i.e., must start with <literal>/</literal>). The path need not
exist. The resulting path is canonicalised, e.g.,
<literal>builtins.toPath "//foo/xyzzy/../bar/"</literal> returns
<literal>/foo/bar</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
<listitem><para>Convert the expression
<replaceable>e</replaceable> to a string.
<replaceable>e</replaceable> can be a string (in which case
<function>toString</function> is a no-op) or a path (e.g.,
<literal>toString /foo/bar</literal> yields
<literal>"/foo/bar"</literal>.</para></listitem>
</varlistentry>
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
<listitem><para>Return a string containing an XML representation
of <replaceable>e</replaceable>. The main application for
<function>toXML</function> is to communicate information with the
builder in a more structured format than plain environment
variables.</para>
<!-- TODO: more formally describe the schema of the XML
representation -->
<para><xref linkend='ex-toxml' /> shows an example where this is
the case. The builder is supposed to generate the configuration
file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
servlet container</link>. A servlet container contains a number
of servlets (<filename>*.war</filename> files) each exported under
a specific URI prefix. So the servlet configuration is a list of
attribute sets containing the <varname>path</varname> and
<varname>war</varname> of the servlet (<xref
linkend='ex-toxml-co-servlets' />). This kind of information is
difficult to communicate with the normal method of passing
information through an environment variable, which just
concatenates everything together into a string (which might just
work in this case, but wouldnt work if fields are optional or
contain lists themselves). Instead the Nix expression is
converted to an XML representation with
<function>toXML</function>, which is unambiguous and can easily be
processed with the appropriate tools. For instance, in the
example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
/>) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
generate the XML configuration file for the Jetty server. The XML
representation produced from <xref linkend='ex-toxml-co-servlets'
/> by <function>toXML</function> is shown in <xref
linkend='ex-toxml-result' />.</para>
<para>Note that <xref linkend='ex-toxml' /> uses the <function
linkend='builtin-toFile'>toFile</function> built-in to write the
builder and the stylesheet “inline” in the Nix expression. The
path of the stylesheet is spliced into the builder at
<literal>xsltproc ${stylesheet}
<replaceable>...</replaceable></literal>.</para>
<example xml:id='ex-toxml'><title>Passing information to a builder
using <function>toXML</function></title>
<programlisting><![CDATA[
{stdenv, fetchurl, libxslt, jira, uberwiki}:
stdenv.mkDerivation (rec {
name = "web-server";
buildInputs = [libxslt];
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
mkdir $out
echo $servlets | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
";
stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
"<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:template match='/'>
<Configure>
<xsl:for-each select='/expr/list/attrs'>
<Call name='addWebApplication'>
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
</Call>
</xsl:for-each>
</Configure>
</xsl:template>
</xsl:stylesheet>
";
servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
];
})]]></programlisting>
</example>
<example xml:id='ex-toxml-result'><title>XML representation produced by
<function>toXML</function></title>
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
<expr>
<list>
<attrs>
<attr name="path">
<string value="/bugtracker" />
</attr>
<attr name="war">
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
</attr>
</attrs>
<attrs>
<attr name="path">
<string value="/wiki" />
</attr>
<attr name="war">
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
</attr>
</attrs>
</list>
</expr>]]></programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.trace</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
<listitem><para>Evaluate <replaceable>e1</replaceable> and print its
abstract syntax representation on standard error. Then return
<replaceable>e2</replaceable>. This function is useful for
debugging.</para></listitem>
</varlistentry>
</variablelist>
</section>

View File

@@ -118,123 +118,6 @@ env-keep-derivations = false
</varlistentry>
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
<listitem>
<para>This option defines the maximum number of seconds that a
builder can go without producing any data on standard output or
standard error. This is useful (for instance in a automated
build system) to catch builds that are stuck in an infinite
loop, or to catch remote builds that are hanging due to network
problems. It can be overriden using the <option
linkend="opt-max-silent-time">--max-silent-time</option> command
line switch.</para>
<para>The value <literal>0</literal> means that there is no
timeout. This is also the default.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
<listitem><para>This options specifies the Unix group containing
the Nix build user accounts. In multi-user Nix installations,
builds should not be performed by the Nix account since that would
allow users to arbitrarily modify the Nix store and database by
supplying specially crafted builders; and they cannot be performed
by the calling user since that would allow him/her to influence
the build result.</para>
<para>Therefore, if this option is non-empty and specifies a valid
group, builds will be performed under the user accounts that are a
member of the group specified here (as listed in
<filename>/etc/group</filename>). Those user accounts should not
be used for any other purpose!</para>
<para>Nix will never run two builds under the same user account at
the same time. This is to prevent an obvious security hole: a
malicious user writing a Nix expression that modifies the build
result of a legitimate Nix expression being built by another user.
Therefore it is good to have as many Nix build user accounts as
you can spare. (Remember: uids are cheap.)</para>
<para>The build users should have permission to create files in
the Nix store, but not delete them. Therefore,
<filename>/nix/store</filename> should be owned by the Nix
account, its group should be the group specified here, and its
mode should be <literal>1775</literal>.</para>
<para>If the build users group is empty, builds will be performed
under the uid of the Nix process (that is, the uid of the caller
if <envar>NIX_REMOTE</envar> is empty, the uid under which the Nix
daemon runs if <envar>NIX_REMOTE</envar> is
<literal>daemon</literal>, or the uid that owns the setuid
<command>nix-worker</command> program if <envar>NIX_REMOTE</envar>
is <literal>slave</literal>). Obviously, this should not be used
in multi-user settings with untrusted users.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>build-use-chroot</literal></term>
<listitem><para>If set to <literal>true</literal>, builds will be
performed in a <emphasis>chroot environment</emphasis>, i.e., the
build will be isolated from the normal file system hierarchy and
will only see the Nix store, the temporary build directory, and
the directories configured with the <link
linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
option</link> (such as <filename>/proc</filename> and
<filename>/dev</filename>). This is useful to prevent undeclared
dependencies on files in directories such as
<filename>/usr/bin</filename>.</para>
<para>The use of a chroot requires that Nix is run as root (but
you can still use the <link
linkend='conf-build-users-group'>“build users” feature</link> to
perform builds under different users than root). Currently,
chroot builds only work on Linux because Nix uses “bind mounts” to
make the Nix store and other directories available inside the
chroot.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
<listitem><para>When builds are performed in a chroot environment,
Nix will mount (using <command>mount --bind</command> on Linux)
some directories from the normal file system hierarchy inside the
chroot. These are the Nix store, the temporary build directory
(usually
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
and the directories listed here. The default is <literal>dev
/proc</literal>. Files in <filename>/dev</filename> (such as
<filename>/dev/null</filename>) are needed by many builds, and
some files in <filename>/proc</filename> may also be needed
occasionally.</para>
<para>The value used on NixOS is
<programlisting>
build-use-chroot = /dev /proc /bin</programlisting>
to make the <filename>/bin/sh</filename> symlink available (which
is still needed by many builders).</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>system</literal></term>
<listitem><para>This option specifies the canonical Nix system

View File

@@ -263,17 +263,6 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
</varlistentry>
<varlistentry xml:id="envar-remote"><term><envar>NIX_REMOTE</envar></term>
<listitem><para>This variable should be set to
<literal>daemon</literal> if you want to use the Nix daemon to
executed Nix operations, which is necessary in <link
linkend="ssec-multi-user">multi-user Nix installations</link>.
Otherwise, it should be left unset.</para></listitem>
</varlistentry>
</variablelist>

View File

@@ -74,9 +74,9 @@
<glossentry><glossterm>Nix expression</glossterm>
<glossdef><para>A high-level description of software packages and
<glossdef><para>A high-level description of software components and
compositions thereof. Deploying software using Nix entails writing
Nix expressions for your packages. Nix expressions are translated
Nix expressions for your components. Nix expressions are translated
to derivations that are stored in the Nix store. These derivations
can then be built.</para></glossdef>

View File

@@ -42,8 +42,9 @@ platforms as well.</para>
<section><title>Obtaining Nix</title>
<para>The easiest way to obtain Nix is to download a <link
xlink:href="http://nix.cs.uu.nl/">source distribution</link>. RPMs
for Red Hat, SuSE, and Fedora Core are also available.</para>
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">source
distribution</link>. RPMs for Red Hat, SuSE, and Fedora Core are also
available.</para>
<para>Alternatively, the most recent sources of Nix can be obtained
from its <link
@@ -99,16 +100,14 @@ ubiquitous 2.5.4a won't. Note that these are only required if you
modify the parser or when you are building from the Subversion
repository.</para>
<para>Nix uses Sleepycat's Berkeley DB, CWI's ATerm library and the
bzip2 compressor (including the bzip2 library). These are included in
the Nix source distribution. If you build from the Subversion
repository, you must download them yourself and place them in the
<filename>externals/</filename> directory. See
<para>Nix uses Sleepycat's Berkeley DB and CWI's ATerm library. These
are included in the Nix source distribution. If you build from the
Subversion repository, you must download them yourself and place them
in the <filename>externals/</filename> directory. See
<filename>externals/Makefile.am</filename> for the precise URLs of
these packages. Alternatively, if you already have them installed,
you can use <command>configure</command>'s
<option>--with-bdb</option>, <option>--with-aterm</option> and
<option>--with-bzip2</option> options to point to their respective
you can use <command>configure</command>'s <option>--with-bdb</option>
and <option>--with-aterm</option> options to point to their respective
locations. Note that Berkeley DB <emphasis>must</emphasis> be version
4.5; other versions may not have compatible database formats.</para>
@@ -119,21 +118,19 @@ locations. Note that Berkeley DB <emphasis>must</emphasis> be version
<para>After unpacking or checking out the Nix sources, issue the
following commands:
</para>
<screen>
$ ./configure <replaceable>options...</replaceable>
$ make
$ make install</screen>
</para>
<para>When building from the Subversion repository, these should be
preceded by the command:
</para>
<screen>
$ ./bootstrap</screen>
</para>
$ ./boostrap</screen>
<para>The installation path can be specified by passing the
<option>--prefix=<replaceable>prefix</replaceable></option> to
@@ -160,32 +157,28 @@ options.</para>
<section><title>Installing from RPMs</title>
<para>RPM packages of Nix can be downloaded from <link
xlink:href="http://nix.cs.uu.nl/" />. These RPMs should work for most
fairly recent releases of SuSE and Red Hat Linux. They have been
known to work work on SuSE Linux 8.1 and 9.0, and Red Hat 9.0. In
fact, it should work on any RPM-based Linux distribution based on
<literal>glibc</literal> 2.3 or later.</para>
<para>RPM packages of Nix can be downloaded from <uri
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">http://www.cs.uu.nl/groups/ST/Trace/Nix</uri>.
These RPMs should work for most fairly recent releases of SuSE and Red
Hat Linux. They have been known to work work on SuSE Linux 8.1 and
9.0, and Red Hat 9.0. In fact, it should work on any RPM-based Linux
distribution based on <literal>glibc</literal> 2.3 or later.</para>
<para>Once downloaded, the RPMs can be installed or upgraded using
<command>rpm -U</command>. For example,
<command>rpm -U</command>. For example,</para>
<screen>
$ rpm -U nix-0.5pre664-1.i386.rpm</screen>
</para>
<para>The RPMs install into the directory <filename>/nix</filename>.
Nix can be uninstalled using <command>rpm -e nix</command>. After
this it will be necessary to manually remove the Nix store and other
auxiliary data:
auxiliary data:</para>
<screen>
$ rm -rf /nix/store
$ rm -rf /nix/var</screen>
</para>
</section>
@@ -194,7 +187,7 @@ $ rm -rf /nix/var</screen>
<para>You can install the latest stable version of Nix through Nix
itself by subscribing to the channel <link
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-stable" />,
or the latest unstable version by subscribing to the channel <link
or the latest unstable version by subscribing to the channel<link
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-unstable" />.
You can also do a <link linkend="sec-one-click">one-click
installation</link> by clicking on the package links at <link
@@ -238,215 +231,33 @@ class="username">root</systemitem> all the time.</para>
</section>
<section xml:id="ssec-multi-user"><title>Multi-user mode</title>
<section><title>Multi-user mode</title>
<para>To allow a Nix store to be shared safely among multiple users,
it is important that users are not able to run builders that modify
the Nix store or database in arbitrary ways, or that interfere with
builds started by other users. If they could do so, they could
install a Trojan horse in some package and compromise the accounts of
other users.</para>
<para></para>
<para>To prevent this, the Nix store and database are owned by some
privileged user (usually <literal>root</literal>) and builders are
executed under special user accounts (usually named
<literal>nixbld1</literal>, <literal>nixbld2</literal>, etc.). When a
unprivileged user runs a Nix command, actions that operate on the Nix
store (such as builds) are forwarded to a <emphasis>Nix
daemon</emphasis> running under the owner of the Nix store/database
that performs the operation.</para>
<!--
warning: the nix-builders group should contain *only* the Nix
builders, and nothing else. If the Nix account is compromised, you
can execute programs under the accounts in the nix-builders group, so
it obviously shouldnt contain any “real” user accounts. So dont use
an existing group like <literal>users</literal> — just create a new
one.
-->
<note><para>Multi-user mode has one important limitation: only
<systemitem class="username">root</systemitem> can run <command
linkend="sec-nix-pull">nix-pull</command> to register the availability
of pre-built binaries. However, those registrations are shared by all
users, so they still get the benefit from <command>nix-pull</command>s
done by <systemitem class="username">root</systemitem>.</para></note>
<section><title>Setting up the build users</title>
<para>The <emphasis>build users</emphasis> are the special UIDs under
which builds are performed. They should all be members of the
<emphasis>build users group</emphasis> (usually called
<literal>nixbld</literal>). This group should have no other members.
The build users should not be members of any other group.</para>
<para>Here is a typical <filename>/etc/group</filename> definition of
the build users group with 10 build users:
<programlisting>
nixbld:!:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10
</programlisting>
In this example the <literal>nixbld</literal> group has UID 30000, but
of course it can be anything that doesnt collide with an existing
group.</para>
<para>Here is the corresponding part of
<filename>/etc/passwd</filename>:
<programlisting>
nixbld1:x:30001:65534:Nix build user 1:/var/empty:/noshell
nixbld2:x:30002:65534:Nix build user 2:/var/empty:/noshell
nixbld3:x:30003:65534:Nix build user 3:/var/empty:/noshell
...
nixbld10:x:30010:65534:Nix build user 10:/var/empty:/noshell
</programlisting>
The home directory of the build users should not exist or should be an
empty directory to which they do not have write access.</para>
<para>The build users should have write access to the Nix store, but
they should not have the right to delete files. Thus the Nix stores
group should be the build users group, and it should have the sticky
bit turned on (like <filename>/tmp</filename>):
<screen>
$ chgrp nixbld /nix/store
$ chmod 1777 /nix/store
</screen>
</para>
<para>Finally, you should tell Nix to use the build users by
specifying the build users group in the <link
linkend="conf-build-users-group"><literal>build-users-group</literal>
option</link> in the <link linkend="sec-conf-file">Nix configuration
file</link> (<literal>/nix/etc/nix/nix.conf</literal>):
<programlisting>
build-users-group = nixbld
</programlisting>
</para>
of pre-built binaries. However, those registrations
<emphasis>are</emphasis> used by all users to speed up
builds.</para></note>
</section>
<section><title>Nix store/database owned by root</title>
<para>The simplest setup is to let <literal>root</literal> own the Nix
store and database. I.e.,
<screen>
$ chown -R root /nix/store /nix/var/nix</screen>
</para>
<para>The Nix daemon should be started as follows (as
<literal>root</literal>):
<screen>
$ nix-worker --daemon</screen>
Youll want to put that line somewhere in your systems boot
scripts.</para>
<para>To let unprivileged users use the daemon, they should set the
<link linkend="envar-remote"><envar>NIX_REMOTE</envar> environment
variable</link> to <literal>daemon</literal>. So you should put a
line like
<programlisting>
export NIX_REMOTE=daemon</programlisting>
into the users login scripts.</para>
</section>
<section><title>Nix store/database not owned by root</title>
<para>It is also possible to let the Nix store and database be owned
by a non-root user, which should be more secure<footnote><para>Note
however that even when the Nix daemon runs as root, not
<emphasis>that</emphasis> much code is executed as root: Nix
expression evaluation is performed by the calling (unprivileged) user,
and builds are performed under the special build user accounts. So
only the code that accesses the database and starts builds is executed
as <literal>root</literal>.</para></footnote>. Typically, this user
is a special account called <literal>nix</literal>, but it can be
named anything. It should own the Nix store and database:
<screen>
$ chown -R root /nix/store /nix/var/nix</screen>
and of course <command>nix-worker --daemon</command> should be started
under that user, e.g.,
<screen>
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
</para>
<para>There is a catch, though: non-<literal>root</literal> users
cannot start builds under the build user accounts, since the
<function>setuid</function> system call is obviously privileged. To
allow a non-<literal>root</literal> Nix daemon to use the build user
feature, it calls a setuid-root helper program,
<command>nix-setuid-helper</command>. This program is installed in
<filename><replaceable>prefix</replaceable>/libexec/nix-setuid-helper</filename>.
To set the permissions properly (Nixs <command>make install</command>
doesnt do this, since we dont want to ship setuid-root programs
out-of-the-box):
<screen>
$ chown root.root /nix/libexec/nix-setuid-helper
$ chmod 4755 /nix/libexec/nix-setuid-helper
</screen>
(This example assumes that the Nix binaries are installed in
<filename>/nix</filename>.)</para>
<para>Of course, the <command>nix-setuid-helper</command> command
should not be usable by just anybody, since then anybody could run
commands under the Nix build user accounts. For that reason there is
a configuration file <filename>/etc/nix-setuid.conf</filename> that
restricts the use of the helper. This file should be a text file
containing precisely two lines, the first being the Nix daemon user
and the second being the build users group, e.g.,
<programlisting>
nix
nixbld
</programlisting>
The setuid-helper barfs if it is called by a user other than the one
specified on the first line, or if it is asked to execute a build
under a user who is not a member of the group specified on the second
line. The file <filename>/etc/nix-setuid.conf</filename> must be
owned by root, and must not be group- or world-writable. The
setuid-helper barfs if this is not the case.</para>
</section>
<section><title>Restricting access</title>
<para>To limit which users can perform Nix operations, you can use the
permissions on the directory
<filename>/nix/var/nix/daemon-socket</filename>. For instance, if you
want to restrict the use of Nix to the members of a group called
<literal>nix-users</literal>, do
<screen>
$ chgrp nix-users /nix/var/nix/daemon-socket
$ chmod ug=rwx,o= /nix/var/nix/daemon-socket
</screen>
This way, users who are not in the <literal>nix-users</literal> group
cannot connect to the Unix domain socket
<filename>/nix/var/nix/daemon-socket/socket</filename>, so they cannot
perform Nix operations.</para>
</section>
</section> <!-- end of multi-user -->
</section> <!-- end of security -->
</section> <!-- end of permissions section -->
<section><title>Using Nix</title>

View File

@@ -1,304 +1,135 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id="chap-introduction">
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Introduction</title>
<section><title>About Nix</title>
<para>Nix is a <emphasis>purely functional package manager</emphasis>.
This means that it treats packages like values in purely functional
programming languages such as Haskell — they are built by functions
that dont have side-effects, and they never change after they have
been built. Nix stores packages in the <emphasis>Nix
store</emphasis>, usually the directory
<filename>/nix/store</filename>, where each package has its own unique
subdirectory such as
<programlisting>
/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/
</programlisting>
where <literal>r8vvq9kq…</literal> is a unique identifier for the
package that captures all its dependencies (its a cryptographic hash
of the packages build dependency graph). This enables many powerful
features.</para>
<simplesect><title>Multiple versions</title>
<para>You can have multiple versions or variants of a package
installed at the same time. This is especially important when
different applications have dependencies on different versions of the
same package — it prevents the “DLL hell”. Because of the hashing
scheme, different versions of a package end up in different paths in
the Nix store, so they dont interfere with each other.</para>
<para>An important consequence is that operations like upgrading or
uninstalling an application cannot break other applications, since
these operations never “destructively” update or delete files that are
used by other packages.</para>
</simplesect>
<simplesect><title>Complete dependencies</title>
<para>Nix helps you make sure that package dependency specifications
are complete. In general, when youre making a package for a package
management system like RPM, you have to specify for each package what
its dependencies are, but there are no guarantees that this
specification is complete. If you forget a dependency, then the
package will build and work correctly on <emphasis>your</emphasis>
machine if you have the dependency installed, but not on the end
user's machine if it's not there.</para>
<para>Since Nix on the other hand doesnt install packages in “global”
locations like <filename>/usr/bin</filename> but in package-specific
directories, the risk of incomplete dependencies is greatly reduced.
This is because tools such as compilers dont search in per-packages
directories such as
<filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>,
so if a package builds correctly on your system, this is because you
specified the dependency explicitly.</para>
<para>Runtime dependencies are found by scanning binaries for the hash
parts of Nix store paths (such as <literal>r8vvq9kq…</literal>). This
sounds risky, but it works extremely well.</para>
</simplesect>
<simplesect><title>Multi-user support</title>
<para>Starting at version 0.11, Nix has multi-user support. This
means that non-privileged users can securely install software. Each
user can have a different <emphasis>profile</emphasis>, a set of
packages in the Nix store that appear in the users
<envar>PATH</envar>. If a user installs a package that another user
has already installed previously, the package wont be built or
downloaded a second time. At the same time, it is not possible for
one user to inject a Trojan horse into a package that might be used by
another user.</para>
<!--
<para>More details can be found in Section 3 of our <a
href="docs/papers.html#securesharing">ASE 2005 paper</a>.</para>
-->
</simplesect>
<simplesect><title>Atomic upgrades and rollbacks</title>
<para>Since package management operations never overwrite packages in
the Nix store but just add new versions in different paths, they are
<emphasis>atomic</emphasis>. So during a package upgrade, there is no
time window in which the package has some files from the old version
and some files from the new version — which would be bad because a
program might well crash if its started during that period.</para>
<para>And since package arent overwritten, the old versions are still
there after an upgrade. This means that you can <emphasis>roll
back</emphasis> to the old version:</para>
<screen>
$ nix-env --upgrade <replaceable>some-packages</replaceable>
$ nix-env --rollback
</screen>
</simplesect>
<simplesect><title>Garbage collection</title>
<para>When you install a package like this…
<screen>
$ nix-env --uninstall firefox
</screen>
the package isnt deleted from the system right away (after all, you
might want to do a rollback, or it might be in the profiles of other
users). Instead, unused packages can be deleted safely by running the
<emphasis>garbage collector</emphasis>:
<screen>
$ nix-collect-garbage
</screen>
This deletes all packages that arent in use by any user profile or by
a currently running program.</para>
</simplesect>
<simplesect><title>Functional package language</title>
<para>Packages are built from <emphasis>Nix expressions</emphasis>,
which is a simple functional language. A Nix expression describes
everything that goes into a package build action (a “derivation”):
other packages, sources, the build script, environment variables for
the build script, etc. Nix tries very hard to ensure that Nix
expressions are <emphasis>deterministic</emphasis>: building a Nix
expression twice should yield the same result.</para>
<para>Because its a functional language, its easy to support
building variants of a package: turn the Nix expression into a
function and call it any number of times with the appropriate
arguments. Due to the hashing scheme, variants dont conflict with
each other in the Nix store.</para>
</simplesect>
<simplesect><title>Transparent source/binary deployment</title>
<para>Nix expressions generally describe how to build a package from
source, so an installation action like
<screen>
$ nix-env --install firefox
</screen>
<emphasis>could</emphasis> cause quite a bit of build activity, as not
only Firefox but also all its dependencies (all the way up to the C
library and the compiler) would have to built, at least if they are
not already in the Nix store. This is a <emphasis>source deployment
model</emphasis>. For most users, building from source is not very
pleasant as it takes far too long. However, Nix can automatically
skip building from source and download a pre-built binary instead if
it knows about it. <emphasis>Nix channels</emphasis> provide Nix
expressions along with pre-built binaries.</para>
<!--
<para>source deployment model (like <a
href="http://www.gentoo.org/">Gentoo</a>) and a binary model (like
RPM)</para>
-->
</simplesect>
<simplesect><title>Binary patching</title>
<para>In addition to downloading binaries automatically if theyre
available, Nix can download binary deltas that patch an existing
package in the Nix store into a new version. This speeds up
upgrades.</para>
</simplesect>
<simplesect><title>Nix Packages collection</title>
<para>We provide a large set of Nix expressions containing hundreds of
existing Unix packages, the <emphasis>Nix Packages
collection</emphasis> (Nixpkgs).</para>
</simplesect>
<simplesect><title>Service deployment</title>
<para>Nix can be used not only for rolling out packages, but also
complete <emphasis>configurations</emphasis> of services. This is
done by treating all the static bits of a service (such as software
packages, configuration files, control scripts, static web pages,
etc.) as “packages” that can be built by Nix expressions. As a
result, all the features above apply to services as well: for
instance, you can roll back a web server configuration if a
configuration change turns out to be undesirable, you can easily have
multiple instances of a service (e.g., a test and production server),
and because the whole service is built in a purely functional way from
a Nix expression, it is repeatable so you can easily reproduce the
service on another machine.</para>
<!--
<para>You can read more about this in our <a
href="docs/papers.html#servicecm">SCM-12 paper</a>.</para>
-->
</simplesect>
<simplesect><title>Portability</title>
<para>Nix should run on most Unix systems, including Linux, FreeBSD and
Mac OS X. It is also supported on Windows using Cygwin.</para>
</simplesect>
<simplesect><title>NixOS</title>
<para>NixOS is a Linux distribution based on Nix. It uses Nix not
just for package management but also to manage the system
configuration (e.g., to build configuration files in
<filename>/etc</filename>). This means, among other things, that its
possible to easily roll back the entire configuration of the system to
an earlier state. Also, users can install software without root
privileges. For more information and downloads, see the <link
xlink:href="http://nix.cs.uu.nl/nixos/">NixOS homepage</link>.</para>
</simplesect>
<!-- other features:
- build farms
- reproducibility (Nix expressions allows whole configuration to be rebuilt)
-->
</section>
<section><title>About us</title>
<para>Nix was developed at the <link
xlink:href="http://www.cs.uu.nl/">Department of Information and
Computing Sciences</link>, Utrecht University by the <link
xlink:href="http://www.cs.uu.nl/wiki/Trace/WebHome">TraCE
project</link>. The project is funded by the Software Engineering
Research Program <link
xlink:href="http://www.jacquard.nl/">Jacquard</link> to improve the
support for variability in software systems.</para>
</section>
<section><title>About this manual</title>
<para>Nix is a system for the deployment of software. Software
deployment is concerned with the creation, distribution, and
management of software components (<quote>packages</quote>). Its main
features are:
<itemizedlist>
<listitem><para>It helps you make sure that dependency specifications
are complete. In general in a deployment system you have to specify
for each component what its dependencies are, but there are no
guarantees that this specification is complete. If you forget a
dependency, then the component will build and work correctly on
<emphasis>your</emphasis> machine if you have the dependency
installed, but not on the end user's machine if it's not
there.</para></listitem>
<listitem><para>It is possible to have <emphasis>multiple versions or
variants</emphasis> of a component installed at the same time. In
contrast, in systems such as RPM different versions of the same
package tend to install to the same location in the file system, so
installing one version will remove the other. This is especially
important if you want to use applications that have conflicting
requirements on different versions of a component (e.g., application A
requires version 1.0 of library X, while application B requires a
non-backwards compatible version 1.1).</para></listitem>
<listitem><para>Users can have different <quote>views</quote>
(<quote>profiles</quote> in Nix parlance) on the set of installed
applications in a system. For instance, one user can have version 1.0
of some package visible, while another is using version 1.1, and a
third doesn't use it at all.</para></listitem>
<listitem><para>It is possible to atomically
<emphasis>upgrade</emphasis> software. I.e., there is no time window
during an upgrade in which part of the old version and part of the new
version are simultaneously visible (which might well cause the
component to fail).</para></listitem>
<listitem><para>Likewise, it is possible to atomically roll back after
an install, upgrade, or uninstall action. That is, in a fast (O(1))
operation the previous configuration of the system can be restored.
This is because upgrade or uninstall actions don't actually remove
components from the system.</para></listitem>
<listitem><para>Unused components can be
<emphasis>garbage-collected</emphasis> automatically and safely: when
you remove an application from a profile, its dependencies will be
deleted by the garbage collector only if there are no other active
applications using them.</para></listitem>
<listitem><para>Nix supports both source-based deployment models
(where you distribute <emphasis>Nix expressions</emphasis> that tell
Nix how to build software from source) and binary-based deployment
models. The latter is more-or-less transparent: installation of
components is always based on Nix expressions, but if the expressions
have been built before and Nix knows that the resulting binaries are
available somewhere, it will use those instead.</para></listitem>
<listitem><para>Nix is flexible in the deployment policies that it
supports. There is a clear separation between the tools that
implement basic Nix <emphasis>mechanisms</emphasis> (e.g., building
Nix expressions), and the tools that implement various deployment
<emphasis>policies</emphasis>. For instance, there is a concept of
<quote>Nix channels</quote> that can be used to keep software
installations up-to-date automatically from a network source. This is
a policy that is implemented by a fairly short Perl script, which can
be adapted easily to achieve similar policies.</para></listitem>
<listitem><para>Nix component builds aim to be <quote>pure</quote>;
that is, unaffected by anything other than the declared dependencies.
This means that if a component was built successfully once, it can be
rebuilt again on another machine and the result will be the same. We
cannot <emphasis>guarantee</emphasis> this (e.g., if the build depends
on the time-of-day), but Nix (and the tools in the Nix Packages
collection) takes special care to help achieve this.</para></listitem>
<listitem><para>Nix expressions (the things that tell Nix how to build
components) are self-contained: they describe not just components but
complete compositions. In other words, Nix expressions also describe
how to build all the dependencies. This is in contrast to component
specification languages like RPM spec files, which might say that a
component X depends on some other component Y, but since it does not
describe <emphasis>exactly</emphasis> what Y is, the result of
building or running X might be different on different machines.
Combined with purity, self-containedness ensures that a component that
<quote>works</quote> on one machine also works on another, when
deployed using Nix.</para></listitem>
<listitem><para>The Nix expression language makes it easy to describe
variability in components (e.g., optional features or
dependencies).</para></listitem>
<listitem><para>Nix is ideal for building build farms that do
continuous builds of software from a version management system, since
it can take care of building all the dependencies as well. Also, Nix
only rebuilds components that have changed, so there are no
unnecessary builds. In addition, Nix can transparently distribute
build jobs over different machines, including different
platforms.</para></listitem>
<listitem><para>Nix can be used not only for software deployment, but
also for <emphasis>service deployment</emphasis>, such as the
deployment of a complete web server with all its configuration files,
static pages, software dependencies, and so on. Nix's advantages for
software deployment also apply here: for instance, the ability
trivially to have multiple configurations at the same time, or the
ability to do rollbacks.</para></listitem>
<listitem><para>Nix can efficiently upgrade between different versions
of a component through <emphasis>binary patching</emphasis>. If
patches are available on a server, and you try to install a new
version of some component, Nix will automatically apply a patch (or
sequence of patches), if available, to transform the installed
component into the new version.</para></listitem>
</itemizedlist>
</para>
<para>This manual tells you how to install and use Nix and how to
write Nix expressions for software not already in the Nix Packages
collection. It also discusses some advanced topics, such as setting
up a Nix-based build farm.</para>
up a Nix-based build farm, and doing service deployment using
Nix.</para>
</section>
<section><title>License</title>
<para>Nix is free software; you can redistribute it and/or modify it
under the terms of the <link
xlink:href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General
Public License</link> as published by the <link
xlink:href="http://www.fsf.org/">Free Software Foundation</link>;
either version 2.1 of the License, or (at your option) any later
version. Nix is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.</para>
</section>
<section><title>More information</title>
<para>Some background information on Nix can be found in a number of
papers. The ICSE 2004 paper <citetitle
<note><para>Some background information on Nix can be found in a
number of papers. The ICSE 2004 paper <citetitle
xlink:href='http://www.cs.uu.nl/~eelco/pubs/immdsd-icse2004-final.pdf'>Imposing
a Memory Management Discipline on Software Deployment</citetitle>
discusses the hashing mechanism used to ensure reliable dependency
@@ -310,27 +141,10 @@ gives a more general discussion of Nix from a system-administration
perspective. The CBSE 2005 paper <citetitle
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
Upgrading in a Purely Functional Component Deployment Model
</citetitle> is about transparent patch deployment in Nix. The SCM-12
paper <citetitle
</citetitle> is about transparent patch deployment in Nix. Finally,
the SCM-12 paper <citetitle
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
Service Configuration Management</citetitle> shows how services (e.g.,
web servers) can be deployed and managed through Nix. A short
overview of NixOS is given in the HotOS XI paper <citetitle
xlink:href="http://www.cs.uu.nl/~eelco/pubs/hotos-final.pdf">Purely
Functional System Configuration Management</citetitle>. The Nix
homepage has <link
xlink:href="http://nix.cs.uu.nl/docs/papers.html">an up-to-date list
of Nix-related papers</link>.</para>
<para>Nix is the subject of Eelco Dolstras PhD thesis <citetitle
xlink:href="http://igitur-archive.library.uu.nl/dissertations/2006-0118-200031/index.htm">The
Purely Functional Software Deployment Model</citetitle>, which
contains most of the papers listed above.</para>
<para>Nix has a homepage at <link
xlink:href="http://nix.cs.uu.nl/"/>.</para>
</section>
web servers) can be deployed and managed through Nix.</para></note>
</chapter>

View File

@@ -19,7 +19,6 @@
<command>nix-build</command>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>

View File

@@ -62,11 +62,11 @@ also <xref linkend="sec-channels" />.</para>
<varlistentry><term><option>--update</option></term>
<listitem><para>Downloads the Nix expressions of all subscribed
channels, makes them the default for <command>nix-env</command>
operations (by symlinking them in the directory
<filename>~/.nix-defexpr</filename>), and performs a
<command>nix-pull</command> on the manifests of all channels to
make pre-built binaries available.</para></listitem>
channels, makes the conjunction of these the default for
<command>nix-env</command> operations (by calling <command>nix-env
-I</command>), and performs a <command>nix-pull</command> on the
manifests of all channels to make pre-built binaries
available.</para></listitem>
</varlistentry>

View File

@@ -19,7 +19,13 @@
<command>nix-env</command>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
<replaceable>attrPath</replaceable>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--file</option></arg>
@@ -39,6 +45,9 @@
<replaceable>system</replaceable>
</arg>
<arg><option>--dry-run</option></arg>
<arg><option>--from-expression</option></arg>
<arg><option>-E</option></arg>
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
<arg choice='plain'><replaceable>operation</replaceable></arg>
<arg rep='repeat'><replaceable>options</replaceable></arg>
<arg rep='repeat'><replaceable>arguments</replaceable></arg>
@@ -49,7 +58,7 @@
<refsection><title>Description</title>
<para>The command <command>nix-env</command> is used to manipulate Nix
user environments. User environments are sets of software packages
user environments. User environments are sets of software components
available to a user at some point in time. In other words, they are a
synthesised view of the programs available in the Nix store. There
may be many user environments: different users can have different
@@ -142,33 +151,13 @@ linkend="sec-common-options" />.</para>
<varlistentry><term><filename>~/.nix-defexpr</filename></term>
<listitem><para>A directory that contains the default Nix
expressions used by the <option>--install</option>,
<option>--upgrade</option>, and <option>--query
--available</option> operations to obtain derivations. The
<option>--file</option> option may be used to override this
default.</para>
<para>The Nix expressions in this directory are combined into a
single attribute set, with each file as an attribute that has the
name of the file. Thus, if <filename>~/.nix-defexpr</filename>
contains two files, <filename>foo</filename> and
<filename>bar</filename>, then the default Nix expression will
essentially be
<programlisting>
{
foo = import ~/.nix-defexpr/foo;
bar = import ~/.nix-defexpr/bar;
}</programlisting>
</para>
<para>The command <command>nix-channel</command> places symlinks
to the downloaded Nix expressions from each subscribed channel in
this directory.</para>
</listitem>
<!-- !!! .nix-defexpr can be a directory now -->
<listitem><para>The default Nix expression used by the
<option>--install</option>, <option>--upgrade</option>, and
<option>--query --available</option> operations to obtain
derivations. The <option>--file</option> option may be used to
override this default.</para></listitem>
</varlistentry>
@@ -201,7 +190,6 @@ linkend="sec-common-options" />.</para>
<arg choice='plain'><option>--install</option></arg>
<arg choice='plain'><option>-i</option></arg>
</group>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<group choice='opt'>
<arg choice='plain'><option>--preserve-installed</option></arg>
<arg choice='plain'><option>-P</option></arg>
@@ -233,21 +221,11 @@ number of possible ways:
<para>If there are multiple derivations matching a name in
<replaceable>args</replaceable> that have the same name (e.g.,
<literal>gcc-3.3.6</literal> and <literal>gcc-4.1.1</literal>), then
the derivation with the highest <emphasis>priority</emphasis> is
used. A derivation can define a priority by declaring the
<varname>meta.priority</varname> attribute. This attribute should
be a number, with a higher value denoting a lower priority. The
default priority is <literal>0</literal>.</para>
<para>If there are multiple matching derivations with the same
priority, then the derivation with the highest version will be
installed.</para>
<para>You can force the installation of multiple derivations with
the same name by being specific about the versions. For instance,
<literal>nix-env -i gcc-3.3.6 gcc-4.1.1</literal> will install both
version of GCC (and will probably cause a user environment
conflict!).</para></listitem>
only the highest version will be installed. You can force the
installation of multiple derivations with the same name by being
specific about the versions. For instance, <literal>nix-env -i
gcc-3.3.6 gcc-4.1.1</literal> will install both version of GCC (and
will probably cause a user environment conflict!).</para></listitem>
<listitem><para>If <link
linkend='opt-attr'><option>--attr</option></link>
@@ -294,15 +272,6 @@ number of possible ways:
<variablelist>
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
<listitem><para>Use only derivations for which a substitute is
registered, i.e., there is a pre-built binary available that can
be downloaded in lieu of building the derivation. Thus, no
packages will be built from source.</para></listitem>
</varlistentry>
<varlistentry><term><option>--preserve-installed</option></term>
<term><option>-P</option></term>
@@ -418,7 +387,7 @@ the following paths will be substituted:
<!--######################################################################-->
<refsection xml:id="rsec-nix-env-upgrade"><title>Operation <option>--upgrade</option></title>
<refsection><title>Operation <option>--upgrade</option></title>
<refsection><title>Synopsis</title>
@@ -428,7 +397,6 @@ the following paths will be substituted:
<arg choice='plain'><option>--upgrade</option></arg>
<arg choice='plain'><option>-u</option></arg>
</group>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
<group choice='opt'>
<arg choice='plain'><option>--lt</option></arg>
<arg choice='plain'><option>--leq</option></arg>
@@ -503,9 +471,6 @@ installed.</para>
</variablelist>
<para>For the other flags, see <option
linkend="rsec-nix-env-install">--install</option>.</para>
</refsection>
<refsection><title>Examples</title>
@@ -615,111 +580,6 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
<!--######################################################################-->
<refsection xml:id="rsec-nix-env-set-flag"><title>Operation <option>--set-flag</option></title>
<refsection><title>Synopsis</title>
<cmdsynopsis>
<command>nix-env</command>
<arg choice='plain'><option>--set-flag</option></arg>
<arg choice='plain'><replaceable>name</replaceable></arg>
<arg choice='plain'><replaceable>value</replaceable></arg>
<arg choice='plain' rep='repeat'><replaceable>drvnames</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The <option>--set-flag</option> operation allows meta attributes
of installed packages to be modified. There are several attributes
that can be usefully modified, because they affect the behaviour of
<command>nix-env</command> or the user environment build
script:
<itemizedlist>
<listitem><para><varname>priority</varname> can be changed to
resolve filename clashes. The user environment build script uses
the <varname>meta.priority</varname> attribute of derivations to
resolve filename collisions between packages. Lower priority values
denote a higher priority. For instance, the GCC wrapper package and
the Binutils package in Nixpkgs both have a file
<filename>bin/ld</filename>, so previously if you tried to install
both you would get a collision. Now, on the other hand, the GCC
wrapper declares a higher priority than Binutils, so the formers
<filename>bin/ld</filename> is symlinked in the user
environment.</para></listitem>
<listitem><para><varname>keep</varname> can be set to
<literal>true</literal> to prevent the package from being upgraded
or replaced. This is useful if you want to hang on to an older
version of a package.</para></listitem>
<listitem><para><varname>active</varname> can be set to
<literal>false</literal> to “disable” the package. That is, no
symlinks will be generated to the files of the package, but it
remains part of the profile (so it wont be garbage-collected). It
can be set back to <literal>true</literal> to re-enable the
package.</para></listitem>
</itemizedlist>
</para>
</refsection>
<refsection><title>Examples</title>
<para>To prevent the currently installed Firefox from being upgraded:
<screen>
$ nix-env --set-flag keep true firefox</screen>
After this, <command>nix-env -u</command> will ignore Firefox.</para>
<para>To disable the currently installed Firefox, then install a new
Firefox while the old remains part of the profile:
<screen>
$ nix-env -q \*
firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
$ nix-env --preserve-installed -i firefox-2.0.0.11
installing `firefox-2.0.0.11'
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
Collission between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
<lineannotation>(i.e., cant have two active at the same time)</lineannotation>
$ nix-env --set-flag active false firefox
setting flag on `firefox-2.0.0.9'
$ nix-env --preserve-installed -i firefox-2.0.0.11
installing `firefox-2.0.0.11'
$ nix-env -q \*
firefox-2.0.0.11 <lineannotation>(the enabled one)</lineannotation>
firefox-2.0.0.9 <lineannotation>(the disabled one)</lineannotation></screen>
</para>
<para>To make files from <literal>binutils</literal> take precedence
over files from <literal>gcc</literal>:
<screen>
$ nix-env --set-flag priority 5 binutils
$ nix-env --set-flag priority 10 gcc</screen>
</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--query</option></title>
@@ -732,14 +592,13 @@ $ nix-env --set-flag priority 10 gcc</screen>
<arg choice='plain'><option>--query</option></arg>
<arg choice='plain'><option>-q</option></arg>
</group>
<arg><option>--xml</option></arg>
<group choice='opt'>
<arg choice='plain'><option>--installed</option></arg>
<arg choice='plain'><option>--available</option></arg>
<arg choice='plain'><option>-a</option></arg>
</group>
<sbr />
<arg>
<group choice='req'>
<arg choice='plain'><option>--status</option></arg>
@@ -748,8 +607,8 @@ $ nix-env --set-flag priority 10 gcc</screen>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr-path</option></arg>
<arg choice='plain'><option>-P</option></arg>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
</arg>
<arg><option>--no-name</option></arg>
@@ -763,28 +622,6 @@ $ nix-env --set-flag priority 10 gcc</screen>
<arg><option>--drv-path</option></arg>
<arg><option>--out-path</option></arg>
<arg><option>--description</option></arg>
<arg><option>--meta</option></arg>
<sbr />
<arg><option>--xml</option></arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--prebuilt-only</option></arg>
<arg choice='plain'><option>-b</option></arg>
</group>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
<replaceable>attribute-path</replaceable>
</arg>
<sbr />
<arg choice='plain' rep='repeat'><replaceable>names</replaceable></arg>
</cmdsynopsis>
@@ -861,16 +698,6 @@ user environment elements, etc. -->
</varlistentry>
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
<listitem><para>Show only derivations for which a substitute is
registered, i.e., there is a pre-built binary available that can
be downloaded in lieu of building the derivation. Thus, this
shows all packages that probably can be installed
quickly.</para></listitem>
</varlistentry>
<varlistentry><term><option>--status</option></term>
<term><option>-s</option></term>
@@ -890,8 +717,8 @@ user environment elements, etc. -->
</varlistentry>
<varlistentry><term><option>--attr-path</option></term>
<term><option>-P</option></term>
<varlistentry><term><option>--attr</option></term>
<term><option>-a</option></term>
<listitem><para>Print the <emphasis>attribute path</emphasis> of
the derivation, which can be used to unambiguously select it using
@@ -914,35 +741,35 @@ user environment elements, etc. -->
<listitem><para>Compare installed versions to available versions,
or vice versa (if <option>--available</option> is given). This is
useful for quickly seeing whether upgrades for installed
packages are available in a Nix expression. A column is added
components are available in a Nix expression. A column is added
with the following meaning:
<variablelist>
<varlistentry><term><literal>&lt;</literal> <replaceable>version</replaceable></term>
<listitem><para>A newer version of the package is available
<listitem><para>A newer version of the component is available
or installed.</para></listitem>
</varlistentry>
<varlistentry><term><literal>=</literal> <replaceable>version</replaceable></term>
<listitem><para>At most the same version of the package is
<listitem><para>At most the same version of the component is
available or installed.</para></listitem>
</varlistentry>
<varlistentry><term><literal>></literal> <replaceable>version</replaceable></term>
<listitem><para>Only older versions of the package are
<listitem><para>Only older versions of the component are
available or installed.</para></listitem>
</varlistentry>
<varlistentry><term><literal>- ?</literal></term>
<listitem><para>No version of the package is available or
<listitem><para>No version of the component is available or
installed.</para></listitem>
</varlistentry>
@@ -983,14 +810,6 @@ user environment elements, etc. -->
</varlistentry>
<varlistentry><term><option>--meta</option></term>
<listitem><para>Print all of the meta-attributes of the
derivation. This option is only available with
<option>--xml</option>.</para></listitem>
</varlistentry>
</variablelist>
</refsection>

View File

@@ -779,178 +779,4 @@ archive is read from standard input.</para>
</refsection>
<!--######################################################################-->
<refsection xml:id='refsec-nix-store-export'><title>Operation <option>--export</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--export</option></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--export</option> writes a serialisation
of the specified store paths to standard output in a format that can
be imported into another Nix store with <command
linkend="refsec-nix-store-import">nix-store --import</command>. This
is like <command linkend="refsec-nix-store-dump">nix-store
--dump</command>, except that the NAR archive produced by that command
doesnt contain the necessary meta-information to allow it to be
imported into another Nix store (namely, the set of references of the
path).</para>
<para>This command does not produce a <emphasis>closure</emphasis> of
the specified paths, so if a store path references other store paths
that are missing in the target Nix store, the import will fail. To
copy a whole closure, do something like
<screen>
$ nix-store --export $(nix-store -qR <replaceable>paths</replaceable>) > out</screen>
</para>
<para>For an example of how <option>--export</option> and
<option>--import</option> can be used, see the source of the <command
linkend="sec-nix-copy-closure">nix-copy-closure</command>
command.</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection xml:id='refsec-nix-store-import'><title>Operation <option>--import</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--import</option></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--export</option> reads a serialisation of
a set of store paths produced by <command
linkend="refsec-nix-store-export">nix-store --import</command> from
standard input and adds those store paths to the Nix store. Paths
that already exist in the Nix store are ignored. If a path refers to
another path that doesnt exist in the Nix store, the import
fails.</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--optimise</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--optimise</option></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--optimise</option> reduces Nix store disk
space usage by finding identical files in the store and hard-linking
them to each other. It typically reduces the size of the store by
something like 25-35%. Only regular files and symlinks are
hard-linked in this manner. Files are considered identical when they
have the same NAR archive serialisation: that is, regular files must
have the same contents and permission (executable or non-executable),
and symlinks must have the same contents.</para>
<para>After completion, or when the command is interrupted, a report
on the achieved savings is printed on standard error.</para>
<para>Use <option>-vv</option> or <option>-vvv</option> to get some
progress indication.</para>
</refsection>
<refsection><title>Example</title>
<screen>
$ nix-store --optimise
hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1'
<replaceable>...</replaceable>
541838819 bytes (516.74 MiB) freed by hard-linking 54143 files;
there are 114486 files with equal contents out of 215894 files in total
</screen>
</refsection>
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--read-log</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<group choice='req'>
<arg choice='plain'><option>--read-log</option></arg>
<arg choice='plain'><option>-l</option></arg>
</group>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--read-log</option> prints the build log
of the specified store paths on standard output. The build log is
whatever the builder of a derivation wrote to standard output and
standard error. If a store path is not a derivation, the deriver of
the store path is used.</para>
<para>Build logs are kept in
<filename>/nix/var/log/nix/drvs</filename>. However, there is no
guarantee that a build log is available for any particular store
path. For instance, if the path was downloaded as a pre-built binary
through a substitute, then the log is unavailable.</para>
</refsection>
<refsection><title>Example</title>
<screen>
$ nix-store -l $(which ktorrent)
building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1
unpacking sources
unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz
ktorrent-2.2.1/
ktorrent-2.2.1/NEWS
<replaceable>...</replaceable>
</screen>
</refsection>
</refsection>
<!-- TODO: export, import operations -->
</refentry>

View File

@@ -13,10 +13,6 @@
</group>
<replaceable>number</replaceable>
</arg>
<arg>
<arg><option>--max-silent-time</option></arg>
<replaceable>number</replaceable>
</arg>
<arg><option>--keep-going</option></arg>
<arg><option>-k</option></arg>
<arg><option>--keep-failed</option></arg>

View File

@@ -103,17 +103,6 @@
</varlistentry>
<varlistentry xml:id="opt-max-silent-time"><term><option>--max-silent-time</option></term>
<listitem><para>Sets the maximum number of seconds that a builder
can go without producing any data on standard output or standard
error. The default is specified by the <link
linkend='conf-build-max-silent-time'><literal>build-max-silent-time</literal></link>
configuration setting. <literal>0</literal> means no
time-out.</para></listitem>
</varlistentry>
<varlistentry><term><option>--keep-going</option></term>
<term><option>-k</option></term>
@@ -268,17 +257,6 @@
</varlistentry>
<varlistentry><term><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
<listitem><para>This option is like <option>--arg</option>, only the
value is not a Nix expression but a string. So instead of
<literal>--arg system \"i686-linux\"</literal> (the outer quotes are
to keep the shell happy) you can say <literal>--argstr system
i686-linux</literal>.</para></listitem>
</varlistentry>
<varlistentry xml:id="opt-attr"><term><option>--attr</option> / <option>-A</option>
<replaceable>attrPath</replaceable></term>

View File

@@ -1,22 +0,0 @@
<nop xmlns="http://docbook.org/ns/docbook">
<arg>
<group choice='req'>
<arg choice='plain'><option>--prebuilt-only</option></arg>
<arg choice='plain'><option>-b</option></arg>
</group>
</arg>
<arg>
<group choice='req'>
<arg choice='plain'><option>--attr</option></arg>
<arg choice='plain'><option>-A</option></arg>
</group>
</arg>
<arg><option>--from-expression</option></arg>
<arg><option>-E</option></arg>
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
</nop>

View File

@@ -6,9 +6,9 @@
<para>This chapter discusses how to do package management with Nix,
i.e., how to obtain, install, upgrade, and erase packages. This is
i.e., how to obtain, install, upgrade, and erase components. This is
the “users” perspective of the Nix system — people
who want to <emphasis>create</emphasis> packages should consult
who want to <emphasis>create</emphasis> components should consult
<xref linkend='chap-writing-nix-expressions' />.</para>
@@ -16,8 +16,8 @@ who want to <emphasis>create</emphasis> packages should consult
<para>The main command for package management is <link
linkend="sec-nix-env"><command>nix-env</command></link>. You can use
it to install, upgrade, and erase packages, and to query what
packages are installed or are available for installation.</para>
it to install, upgrade, and erase components, and to query what
components are installed or are available for installation.</para>
<para>In Nix, different users can have different “views”
on the set of installed applications. That is, there might be lots of
@@ -30,10 +30,10 @@ environment</emphasis>, which is just a directory tree consisting of
symlinks to the files of the active applications. </para>
<para>Components are installed from a set of <emphasis>Nix
expressions</emphasis> that tell Nix how to build those packages,
expressions</emphasis> that tell Nix how to build those components,
including, if necessary, their dependencies. There is a collection of
Nix expressions called the Nix Package collection that contains
packages ranging from basic development stuff such as GCC and Glibc,
components ranging from basic development stuff such as GCC and Glibc,
to end-user applications like Mozilla Firefox. (Nix is however not
tied to the Nix Package collection; you could write your own Nix
expressions based on it, or completely new ones.) You can download
@@ -41,7 +41,7 @@ the latest version from <link
xlink:href='http://nix.cs.uu.nl/dist/nix' />.</para>
<para>Assuming that you have downloaded and unpacked a release of Nix
Packages, you can view the set of available packages in the release:
Packages, you can view the set of available components in the release:
<screen>
$ nix-env -qaf nixpkgs-<replaceable>version</replaceable> '*'
@@ -74,7 +74,7 @@ gcc-4.1.1</screen>
</para>
<para>It is also possible to see the <emphasis>status</emphasis> of
available packages, i.e., whether they are installed into the user
available components, i.e., whether they are installed into the user
environment and/or present in the system:
<screen>
@@ -86,24 +86,24 @@ IPS bison-1.875d
...</screen>
The first character (<literal>I</literal>) indicates whether the
package is installed in your current user environment. The second
component is installed in your current user environment. The second
(<literal>P</literal>) indicates whether it is present on your system
(in which case installing it into your user environment would be a
very quick operation). The last one (<literal>S</literal>) indicates
whether there is a so-called <emphasis>substitute</emphasis> for the
package, which is Nixs mechanism for doing binary deployment. It
just means that Nix knows that it can fetch a pre-built package from
component, which is Nixs mechanism for doing binary deployment. It
just means that Nix knows that it can fetch a pre-built component from
somewhere (typically a network server) instead of building it
locally.</para>
<para>So now that we have a set of Nix expressions we can build the
packages contained in them. This is done using <literal>nix-env
components contained in them. This is done using <literal>nix-env
-i</literal>. For instance,
<screen>
$ nix-env -f nixpkgs-<replaceable>version</replaceable> -i subversion</screen>
will install the package called <literal>subversion</literal> (which
will install the component called <literal>subversion</literal> (which
is, of course, the <link
xlink:href='http://subversion.tigris.org/'>Subversion version
management system</link>).</para>
@@ -112,7 +112,7 @@ management system</link>).</para>
Subversion and all its dependencies. This will take quite a while —
typically an hour or two on modern machines. Fortunately, there is a
faster way (so do a Ctrl-C on that install operation!): you just need
to tell Nix that pre-built binaries of all those packages are
to tell Nix that pre-built binaries of all those components are
available somewhere. This is done using the
<command>nix-pull</command> command, which must be supplied with a URL
containing a <emphasis>manifest</emphasis> describing what binaries
@@ -153,7 +153,7 @@ expressions, use <parameter>-i</parameter> instead of
<parameter>-u</parameter>; <parameter>-i</parameter> will remove
whatever version is already installed.</para>
<para>You can also upgrade all packages for which there are newer
<para>You can also upgrade all components for which there are newer
versions:
<screen>
@@ -199,19 +199,19 @@ set.</para></footnote></para>
implementing the ability to allow different users to have different
configurations, and to do atomic upgrades and rollbacks. To
understand how they work, its useful to know a bit about how Nix
works. In Nix, packages are stored in unique locations in the
works. In Nix, components are stored in unique locations in the
<emphasis>Nix store</emphasis> (typically,
<filename>/nix/store</filename>). For instance, a particular version
of the Subversion package might be stored in a directory
of the Subversion component might be stored in a directory
<filename>/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/</filename>,
while another version might be stored in
<filename>/nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2</filename>.
The long strings prefixed to the directory names are cryptographic
hashes<footnote><para>160-bit truncations of SHA-256 hashes encoded in
a base-32 notation, to be precise.</para></footnote> of
<emphasis>all</emphasis> inputs involved in building the package
<emphasis>all</emphasis> inputs involved in building the component
sources, dependencies, compiler flags, and so on. So if two
packages differ in any way, they end up in different locations in
components differ in any way, they end up in different locations in
the file system, so they dont interfere with each other. <xref
linkend='fig-user-environments' /> shows a part of a typical Nix
store.</para>
@@ -231,12 +231,12 @@ $ /nix/store/dpmvp969yhdq...-subversion-1.1.3/bin/svn</screen>
every time you want to run Subversion. Of course we could set up the
<envar>PATH</envar> environment variable to include the
<filename>bin</filename> directory of every package we want to use,
<filename>bin</filename> directory of every component we want to use,
but this is not very convenient since changing <envar>PATH</envar>
doesnt take effect for already existing processes. The solution Nix
uses is to create directory trees of symlinks to
<emphasis>activated</emphasis> packages. These are called
<emphasis>user environments</emphasis> and they are packages
<emphasis>activated</emphasis> components. These are called
<emphasis>user environments</emphasis> and they are components
themselves (though automatically generated by
<command>nix-env</command>), so they too reside in the Nix store. For
instance, in <xref linkend='fig-user-environments' /> the user
@@ -285,8 +285,8 @@ operation, a new user environment and generation link are created
based on the current one, and finally the <filename>default</filename>
symlink is made to point at the new generation. This last step is
atomic on Unix, which explains how we can do atomic upgrades. (Note
that the building/installing of new packages doesnt interfere in
any way with old packages, since they are stored in different
that the building/installing of new components doesnt interfere in
any way with old components, since they are stored in different
locations in the Nix store.)</para>
<para>If you find that you want to undo a <command>nix-env</command>
@@ -352,18 +352,18 @@ This will <emphasis>not</emphasis> change the
<para><command>nix-env</command> operations such as upgrades
(<option>-u</option>) and uninstall (<option>-e</option>) never
actually delete packages from the system. All they do (as shown
actually delete components from the system. All they do (as shown
above) is to create a new user environment that no longer contains
symlinks to the “deleted” packages.</para>
symlinks to the “deleted” components.</para>
<para>Of course, since disk space is not infinite, unused packages
<para>Of course, since disk space is not infinite, unused components
should be removed at some point. You can do this by running the Nix
garbage collector. It will remove from the Nix store any package
garbage collector. It will remove from the Nix store any component
not used (directly or indirectly) by any generation of any
profile.</para>
<para>Note however that as long as old generations reference a
package, it will not be deleted. After all, we wouldnt be able to
component, it will not be deleted. After all, we wouldnt be able to
do a rollback otherwise. So in order for garbage collection to be
effective, you should also delete (some) old generations. Of course,
this should only be done if you are certain that you will not need to
@@ -486,7 +486,7 @@ makes the union of each channels Nix expressions the default for
<screen>
$ nix-env -u '*'</screen>
to upgrade all packages in your profile to the latest versions
to upgrade all components in your profile to the latest versions
available in the subscribed channels.</para>
</section>

View File

@@ -11,7 +11,7 @@ to the following chapters.</para>
<orderedlist>
<listitem><para>Download a source tarball or RPM from <link
xlink:href='http://nix.cs.uu.nl/'/>. Build source
xlink:href='http://www.cs.uu.nl/groups/ST/Trace/Nix'/>. Build source
distributions using the regular sequence:
<screen>
@@ -22,9 +22,8 @@ $ make install <lineannotation>(as root)</lineannotation></screen>
This will install Nix in <filename>/nix</filename>. You shouldn't
change the prefix if at all possible since that will make it
impossible to use pre-built binaries from the Nixpkgs channel and
other channels. Alternatively, you could grab an RPM if you're on an
RPM-based system. You should also add
impossible to use our pre-built components. Alternatively, you could
grab an RPM if you're on an RPM-based system. You should also add
<filename>/nix/etc/profile.d/nix.sh</filename> to your
<filename>~/.bashrc</filename> (or some other login
file).</para></listitem>
@@ -41,14 +40,14 @@ $ nix-channel --add \
<screen>
$ nix-channel --update</screen>
Note that this in itself doesn't download any packages, it just
Note that this in itself doesn't download any components, it just
downloads the Nix expressions that build them and stores them
somewhere (under <filename>~/.nix-defexpr</filename>, in case you're
curious). Also, it registers the fact that pre-built binaries are
available remotely.</para></listitem>
<listitem><para>See what installable packages are currently available
in the channel:
<listitem><para>See what installable components are currently
available in the channel:
<screen>
$ nix-env -qa * <lineannotation>(mind the quotes!)</lineannotation>
@@ -60,13 +59,13 @@ libxslt-1.1.0
</para></listitem>
<listitem><para>Install some packages from the channel:
<listitem><para>Install some components from the channel:
<screen>
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
This should download pre-built packages; it should not build them
locally (if it does, something went wrong).</para></listitem>
This should download the pre-built components; it should not build
them locally (if it does, something went wrong).</para></listitem>
<listitem><para>Test that they work:
@@ -93,8 +92,8 @@ $ nix-env -e hello</screen>
$ nix-channel --update
$ nix-env -u '*'</screen>
The latter command will upgrade each installed package for which there
is a “newer” version (as determined by comparing the version
The latter command will upgrade each installed component for which
there is a “newer” version (as determined by comparing the version
numbers).</para></listitem>
<listitem><para>You can also install specific packages directly from
@@ -108,7 +107,7 @@ appear asking you whether its okay to install the package. Say
installed.</para></listitem>
<listitem><para>If you're unhappy with the result of a
<command>nix-env</command> action (e.g., an upgraded package turned
<command>nix-env</command> action (e.g., an upgraded component turned
out not to work properly), you can go back:
<screen>
@@ -125,7 +124,7 @@ $ nix-collect-garbage -d</screen>
<!--
The first command deletes old “generations” of your profile (making
rollbacks impossible, but also making the packages in those old
rollbacks impossible, but also making the components in those old
generations available for garbage collection), while the second
command actually deletes them.-->

View File

@@ -8,271 +8,115 @@
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (TBA)</title>
<itemizedlist>
<listitem><para><command>nix-store --dump-db / --load-db</command>.</para></listitem>
</itemizedlist>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (December 31,
2007)</title>
<para>Nix 0.11 has many improvements over the previous stable release.
The most important improvement is secure multi-user support. It also
features many usability enhancements and language extensions, many of
them prompted by NixOS, the purely functional Linux distribution based
on Nix. Here is an (incomplete) list:</para>
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (TBA)</title>
<itemizedlist>
<listitem><para>Secure multi-user support. A single Nix store can
now be shared between multiple (possible untrusted) users. This is
an important feature for NixOS, where it allows non-root users to
install software. The old setuid method for sharing a store between
multiple users has been removed. Details for setting up a
multi-user store can be found in the manual.</para></listitem>
<listitem><para>TODO: multi-user support. The old setuid method for
sharing a store between multiple users has been
removed.</para></listitem>
<listitem><para>The new command <command>nix-copy-closure</command>
gives you an easy and efficient way to exchange software between
machines. It copies the missing parts of the closure of a set of
store path to or from a remote machine via
<command>ssh</command>.</para></listitem>
store path to or from a remote machine.</para></listitem>
<listitem><para>A new kind of string literal: strings between double
single-quotes (<literal>''</literal>) have indentation
“intelligently” removed. This allows large strings (such as shell
scripts or configuration file fragments in NixOS) to cleanly follow
the indentation of the surrounding expression. It also requires
much less escaping, since <literal>''</literal> is less common in
most languages than <literal>"</literal>.</para></listitem>
<listitem><para><command>nix-env</command> <option>--set</option>
modifies the current generation of a profile so that it contains
exactly the specified derivation, and nothing else. For example,
<literal>nix-env -p /nix/var/nix/profiles/browser --set
firefox</literal> lets the profile named
<filename>browser</filename> contain just Firefox.</para></listitem>
<listitem><para><command>nix-env</command> now maintains
meta-information about installed packages in profiles. The
meta-information is the contents of the <varname>meta</varname>
attribute of derivations, such as <varname>description</varname> or
<varname>homepage</varname>. The command <literal>nix-env -q --xml
--meta</literal> shows all meta-information.</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now by default
computes the SHA-256 hash of the file instead of the MD5 hash. In
calls to <function>fetchurl</function> you should pass an
<literal>sha256</literal> attribute instead of
<literal>md5</literal>. You can pass either a hexadecimal or a
base-32 encoding of the hash.</para></listitem>
<listitem><para><command>nix-env</command> now uses the
<varname>meta.priority</varname> attribute of derivations to resolve
filename collisions between packages. Lower priority values denote
a higher priority. For instance, the GCC wrapper package and the
Binutils package in Nixpkgs both have a file
<filename>bin/ld</filename>, so previously if you tried to install
both you would get a collision. Now, on the other hand, the GCC
wrapper declares a higher priority than Binutils, so the formers
<filename>bin/ld</filename> is symlinked in the user
environment.</para></listitem>
<listitem><para><command>nix-env -i / -u</command>: instead of
breaking package ties by version, break them by priority and version
number. That is, if there are multiple packages with the same name,
then pick the package with the highest priority, and only use the
version if there are multiple packages with the same
priority.</para>
<para>This makes it possible to mark specific versions/variant in
Nixpkgs more or less desirable than others. A typical example would
be a beta version of some package (e.g.,
<literal>gcc-4.2.0rc1</literal>) which should not be installed even
though it is the highest version, except when it is explicitly
selected (e.g., <literal>nix-env -i
gcc-4.2.0rc1</literal>).</para></listitem>
<listitem><para><command>nix-env --set-flag</command> allows meta
attributes of installed packages to be modified. There are several
attributes that can be usefully modified, because they affect the
behaviour of <command>nix-env</command> or the user environment
build script:
<itemizedlist>
<listitem><para><varname>meta.priority</varname> can be changed
to resolve filename clashes (see above).</para></listitem>
<listitem><para><varname>meta.keep</varname> can be set to
<literal>true</literal> to prevent the package from being
upgraded or replaced. Useful if you want to hang on to an older
version of a package.</para></listitem>
<listitem><para><varname>meta.active</varname> can be set to
<literal>false</literal> to “disable” the package. That is, no
symlinks will be generated to the files of the package, but it
remains part of the profile (so it wont be garbage-collected).
Set it back to <literal>true</literal> to re-enable the
package.</para></listitem>
</itemizedlist>
</para></listitem>
<listitem><para><command>nix-env -q</command> now has a flag
<option>--prebuilt-only</option> (<option>-b</option>) that causes
<command>nix-env</command> to show only those derivations whose
output is already in the Nix store or that can be substituted (i.e.,
downloaded from somewhere). In other words, it shows the packages
that can be installed “quickly”, i.e., dont need to be built from
source. The <option>-b</option> flag is also available in
<command>nix-env -i</command> and <command>nix-env -u</command> to
filter out derivations for which no pre-built binary is
available.</para></listitem>
<listitem><para>The new option <option>--argstr</option> (in
<command>nix-env</command>, <command>nix-instantiate</command> and
<command>nix-build</command>) is like <option>--arg</option>, except
that the value is a string. For example, <literal>--argstr system
i686-linux</literal> is equivalent to <literal>--arg system
\"i686-linux\"</literal> (note that <option>--argstr</option>
prevents annoying quoting around shell arguments).</para></listitem>
<listitem><para><command>nix-store</command> has a new operation
<option>--read-log</option> (<option>-l</option>)
<parameter>paths</parameter> that shows the build log of the given
paths.</para></listitem>
<!--
<listitem><para>TODO: <varname>allowedReferences</varname> for
checking the set of references in the output of a
derivation.</para></listitem>
<listitem><para>TODO: semantic cleanups of string concatenation
etc. (mostly in r6740).</para></listitem>
-->
<listitem><para>Nix now uses Berkeley DB 4.5. The database is
upgraded automatically, but you should be careful not to use old
versions of Nix that still use Berkeley DB 4.4.</para></listitem>
<listitem><para>TODO: now using Berkeley DB 4.5.</para></listitem>
<!-- foo
<listitem><para>TODO: option <option>- -reregister</option> in
<command>nix-store - -register-validity</command>.</para></listitem>
-->
<listitem><para>TODO: option <option>--reregister</option> in
<command>nix-store --register-validity</command>.</para></listitem>
<listitem><para>The option <option>--max-silent-time</option>
(corresponding to the configuration setting
<literal>build-max-silent-time</literal>) allows you to set a
timeout on builds — if a build produces no output on
<literal>stdout</literal> or <literal>stderr</literal> for the given
number of seconds, it is terminated. This is useful for recovering
automatically from builds that are stuck in an infinite
loop.</para></listitem>
<listitem><para>TODO: magic <varname>exportReferencesGraph</varname>
attribute.</para></listitem>
<listitem><para><command>nix-channel</command>: each subscribed
channel is its own attribute in the top-level expression generated
for the channel. This allows disambiguation (e.g. <literal>nix-env
-i -A nixpkgs_unstable.firefox</literal>).</para></listitem>
<listitem><para>TODO: option <option>--max-silent-time</option>,
configuration setting
<literal>build-max-silent-time</literal>.</para></listitem>
<listitem><para>The substitutes table has been removed from the
database. This makes operations such as <command>nix-pull</command>
and <command>nix-channel --update</command> much, much
faster.</para></listitem>
<listitem><para>TODO: <command>nix-env</command>
<option>--set</option>.</para></listitem>
<listitem><para>TODO: <option>--argstr</option>.</para></listitem>
<listitem><para><command>nix-pull</command> now supports
bzip2-compressed manifests. This speeds up
channels.</para></listitem>
<listitem><para>TODO: <command>nix-env</command> now maintains meta
info about installed packages in user environments. <option>-q
--xml --meta</option> to show all meta info.</para></listitem>
<listitem><para>TODO: <command>nix-env</command>
<option>--set-flag</option>. Specific flags:
<literal>active</literal>, <literal>priority</literal>,
<literal>keep</literal>.</para></listitem>
<listitem><para>TODO: <command>nix-env</command> <option>-i</option>
/ <option>-u</option> take package priorities into
account.</para></listitem>
<listitem><para><command>nix-env -q</command> now has a flag
<option>--prebuilt-only</option> (<option>-b</option>) that causes
<command>nix-env</command> to show only those derivations whose
output is already in the Nix store or that can be substituted (i.e.,
downloaded from somewhere). In other words, it shows the packages
that can be installed “quickly”, i.e., dont need to be built from
source.</para></listitem>
<listitem><para>TODO: new built-ins
<function>builtins.attrNames</function>,
<function>builtins.filterSource</function>,
<function>builtins.sub</function>,
<function>builtins.stringLength</function>,
<function>builtins.substring</function>.</para></listitem>
<listitem><para>TODO: each subscribed channel is its own attribute
in the top-level expression generated for the channel, this allows
disambiguation (<command>nix-env -qaA</command>).</para></listitem>
<listitem><para>TODO: substitutes table is gone, registering
substitutes is now much faster.</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now has a
limited form of caching. This is used by
<command>nix-channel</command> to prevent unnecessary downloads when
the channel hasnt changed.</para></listitem>
<listitem><para><command>nix-prefetch-url</command> now by default
computes the SHA-256 hash of the file instead of the MD5 hash. In
calls to <function>fetchurl</function> you should pass the
<literal>sha256</literal> attribute instead of
<literal>md5</literal>. You can pass either a hexadecimal or a
base-32 encoding of the hash.</para></listitem>
<listitem><para>Nix can now perform builds in an automatically
generated “chroot”. This prevents a builder from accessing stuff
outside of the Nix store, and thus helps ensure purity. This is an
experimental feature.</para></listitem>
<listitem><para>The new command <command>nix-store
--optimise</command> reduces Nix store disk space usage by finding
identical files in the store and hard-linking them to each other.
It typically reduces the size of the store by something like
25-35%.</para></listitem>
<listitem><para><filename>~/.nix-defexpr</filename> can now be a
directory, in which case the Nix expressions in that directory are
combined into an attribute set, with the file names used as the
names of the attributes. The command <command>nix-env
--import</command> (which set the
<filename>~/.nix-defexpr</filename> symlink) is
removed.</para></listitem>
<listitem><para>Derivations can specify the new special attribute
<varname>allowedReferences</varname> to enforce that the references
in the output of a derivation are a subset of a declared set of
paths. For example, if <varname>allowedReferences</varname> is an
empty list, then the output must not have any references. This is
used in NixOS to check that generated files such as initial ramdisks
for booting Linux dont have any dependencies.</para></listitem>
<listitem><para>The new attribute
<varname>exportReferencesGraph</varname> allows builders access to
the references graph of their inputs. This is used in NixOS for
tasks such as generating ISO-9660 images that contain a Nix store
populated with the closure of certain paths.</para></listitem>
<listitem><para>Fixed-output derivations (like
<function>fetchurl</function>) can define the attribute
<varname>impureEnvVars</varname> to allow external environment
variables to be passed to builders. This is used in Nixpkgs to
support proxy configuration, among other things.</para></listitem>
<listitem><para>Several new built-in functions:
<function>builtins.attrNames</function>,
<function>builtins.filterSource</function>,
<function>builtins.isAttrs</function>,
<function>builtins.isFunction</function>,
<function>builtins.listToAttrs</function>,
<function>builtins.stringLength</function>,
<function>builtins.sub</function>,
<function>builtins.substring</function>,
<function>throw</function>,
<function>builtins.trace</function>,
<function>builtins.readFile</function>.</para></listitem>
</itemizedlist>

View File

@@ -46,11 +46,6 @@ h3 /* subsections */
font-size: 125%;
}
div.simplesect h2
{
font-size: 110%;
}
div.appendix h3
{
font-size: 150%;

View File

@@ -4,9 +4,7 @@
<title>Troubleshooting</title>
<para>This section provides solutions for some common problems. See
the <link xlink:href="https://bugs.cs.uu.nl/browse/NIX">Nix
bug tracker</link> for a list of currently known issues.</para>
<para>This section provides solutions for some common problems.</para>
<section><title>Berkeley DB: <quote>Cannot allocate memory</quote></title>
@@ -79,46 +77,6 @@ $ nix-store --verify</screen>
</section>
<section><title>Berkeley DB out of locks</title>
<para>It is possible, especially in <command>nix-store
--verify</command> or when running the garbage collector, to run out
of Berkeley DB locks, like this:
<screen>
$ nix-store --verify
checking path existence
checking path realisability
checking the derivers table
checking the references table
Berkeley DB error: Lock table is out of available object entries
error: Db::get: Cannot allocate memory</screen>
</para>
<para>A workaround is to increase the number of locks that Berkeley DB
allocates. (The real solution would be for Nix to not use so many
locks.) This can be done by putting the following in the file
<filename>/nix/var/nix/db/<link
xlink:href="http://www.oracle.com/technology/documentation/berkeley-db/db/ref/env/db_config.html">DB_CONFIG</link></filename>:
<programlisting>
set_lk_max_locks 100000
set_lk_max_lockers 100000
set_lk_max_objects 100000
</programlisting>
(Increase these numbers if necessary.) Then make sure that there are
no running Nix processes and delete the Berkeley DB environment:
<screen>
$ rm /nix/var/nix/db/__db.*</screen>
The Berkeley DB environment is automatically recreated with the new
limits when you run any Nix command.</para>
</section>
<section><title>Collisions in <command>nix-env</command></title>

File diff suppressed because it is too large Load Diff

View File

@@ -1,67 +0,0 @@
#! /bin/sh -e
make clean # comment this out when needed !!!
export nixstatepath=/nixstate2/nix
export ACLOCAL_PATH=/home/wouterdb/.nix-profile/share/aclocal
if [ "$1" = "full" ]; then
nix-env-all-pkgs.sh -i gcc
nix-env-all-pkgs.sh -i gnum4
nix-env-all-pkgs.sh -i autoconf
nix-env-all-pkgs.sh -i automake
nix-env-all-pkgs.sh -i gnused
nix-env-all-pkgs.sh -i db4
nix-env-all-pkgs.sh -i aterm
nix-env-all-pkgs.sh -i bzip2
nix-env-all-pkgs.sh -i flex
nix-env-all-pkgs.sh -i bsdiff
nix-env-all-pkgs.sh -i libtool
nix-env-all-pkgs.sh -i docbook5
nix-env-all-pkgs.sh -i docbook5-xsl
nix-env-all-pkgs.sh -i bison
nix-env-all-pkgs.sh -i gdb #optional for debugging
nix-env-all-pkgs.sh -i gnupatch
nix-env-all-pkgs.sh -i gnumake
nix-env-all-pkgs.sh -i ext3cow-tools
nix-env-all-pkgs.sh -i e3cfsprogs
nix-env-all-pkgs.sh -i rsync
fi
if [ "$1" = "full" ] || [ "$1" = "auto" ]; then
export AUTOCONF=autoconf
export AUTOHEADER=autoheader
export AUTOMAKE=automake
autoconf
autoreconf -f
aclocal
autoheader
automake
fi
./bootstrap.sh
./configure --with-aterm=$HOME/.nix-profile \
--with-bzip2=$HOME/.nix-profile \
--with-bdb=$HOME/.nix-profile \
--with-docbook-xsl=$HOME/.nix-profile \
--with-docbook-rng=/home/wouterdb/.nix-profile/xml/rng/docbook \
--with-docbook-xsl=/home/wouterdb/.nix-profile/xml/xsl/docbook \
--prefix=$nixstatepath \
--with-store-dir=/nix/store \
--with-store-state-dir=/nix/state \
--with-ext3cow-header=/nix/store/2sm0h2xd1zsm5had53q1pvzmnsn8fy8k-linux-2.6.21/lib/modules/2.6.21-ck1-default/build/include/linux/ext3cow_fs.h \
--localstatedir=/nix/var
#Options from the nix expr
#--disable-init-state
#--with-store-dir=/nix/store
#--localstatedir=/nix/var
#--with-aterm=/nix/store/pkmzbb613wa8cwngx8jjb5jaic8yhyzs-aterm-2.4.2-fixes
#--with-bdb=/nix/store/4yv4j1cd7i5j3mhs5wpc1kzlz1cj8n82-db4-4.5.20
#--with-bzip2=/nix/store/dh0mdgkvhv3pwrf8zp58phpzn9rcm49r-bzip2-1.0.3
#--disable-init-state
echo "New state nix version by wouter ..." > doc/manual/NEWS.txt
make

View File

@@ -1,16 +0,0 @@
#! /bin/sh -e
if [ $(whoami) = "root" ]
then
su - wouterdb -c "cd /home/wouterdb/dev/nix-state/; make"
make install
chown -R wouterdb.wouterdb /nixstate2/nix/
./restartDaemon.sh
else
echo "You must be ROOT to run this script."
exit 0
fi

View File

@@ -1,5 +0,0 @@
#! /bin/sh -e
make

View File

@@ -1,36 +0,0 @@
svn merge -r 10855:10943 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk
#already done:
# 8628
# 8632
# 8634
# 8636
# 8655
# 8691
# 8698
# 8711
# 8864
# 9063
# 9105
# 9207
# 9217
# 9332
# 9429
# 9433
# 9435
# 9437
# 9439
# 9445
# 9476
# 9506
# 9536
# 9549
# 9561
# 9584
# 9751
# 10133
# 10154
# 10531
# 10692
# 10855
# 10943

View File

@@ -1,35 +0,0 @@
" Vim syntax file
" Language: nix
" Maintainer: Marc Weber <marco-oweber@gmx.de>
" Modify and commit if you feel that way
" Last Change: 2007 Dec
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
finish
endif
syn keyword nixKeyword let throw inherit import true false null with
syn keyword nixConditional if else then
syn keyword nixBrace ( ) { } =
syn keyword nixBuiltin __currentSystem __currentTime __isFunction __getEnv __trace __toPath __pathExists
\ __readFile __toXML __toFile __filterSource __attrNames __getAttr __hasAttr __isAttrs __listToAttrs __isList
\ __head __tail __add __sub __lessThan __substring __stringLength
syn match nixAttr "\w\+\ze\s*="
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
hi def link nixKeyword Keyword
hi def link nixConditional Conditional
hi def link nixBrace Special
hi def link nixString String
hi def link nixBuiltin Special
hi def link nixStringParam Macro
hi def link nixMultiLineComment Comment
hi def link nixEndOfLineComment Comment
hi def link nixAttr Identifier
hi def link nixFuncArg Identifier

View File

@@ -80,7 +80,7 @@
### Option `build-max-silent-time'
#
# This option defines the maximum number of seconds that a builder can
# This option defines the maximum number of seconds that builder can
# go without producing any data on standard output or standard error.
# This is useful (for instance in a automated build system) to catch
# builds that are stuck in an infinite loop, or to catch remote builds
@@ -135,44 +135,6 @@
#build-users-group =
### Option `build-use-chroot'
#
# If set to `true', builds will be performed in a chroot environment,
# i.e., the build will be isolated from the normal file system
# hierarchy and will only see the Nix store, the temporary build
# directory, and the directories configured with the
# `build-chroot-dirs' option (such as /proc and /dev). This is useful
# to prevent undeclared dependencies on files in directories such as
# /usr/bin.
#
# The use of a chroot requires that Nix is run as root (but you can
# still use the "build users" feature to perform builds under
# different users than root). Currently, chroot builds only work on
# Linux because Nix uses "bind mounts" to make the Nix store and other
# directories available inside the chroot.
#
# The default is `false'.
#
# Example:
# build-use-chroot = true
#build-use-chroot = false
### Option `build-chroot-dirs'
#
# When builds are performed in a chroot environment, Nix will mount
# (using `mount --bind' on Linux) some directories from the normal
# file system hierarchy inside the chroot. These are the Nix store,
# the temporary build directory (usually /tmp/nix-<pid>-<number>) and
# the directories listed here. The default is "/dev /proc". Files
# in /dev (such as /dev/null) are needed by many builds, and some
# files in /proc may also be needed occasionally.
#
# Example:
# build-use-chroot = /dev /proc /bin
#build-chroot-dirs = /dev /proc
### Option `system'
#
# This option specifies the canonical Nix system name of the current

View File

@@ -1,9 +0,0 @@
#! /bin/sh
initctl stop nix-daemon
killproc.sh nix-worker
sleep 2
#/nixstate2/nix/bin/nix-worker --daemon > /dev/null 2>&1 &
/nixstate2/nix/bin/nix-worker --daemon
#gdb --args /nixstate2/nix/bin/nix-worker --daemon

View File

@@ -2,10 +2,9 @@ bin_SCRIPTS = nix-collect-garbage \
nix-pull nix-push nix-prefetch-url \
nix-install-package nix-channel nix-build \
nix-pack-closure nix-unpack-closure \
nix-copy-closure
nix-copy-closure
noinst_SCRIPTS = nix-profile.sh generate-patches.pl \
find-runtime-roots.pl build-remote.pl nix-reduce-build
noinst_SCRIPTS = nix-profile.sh generate-patches.pl find-runtime-roots.pl
nix-pull nix-push: readmanifest.pm readconfig.pm download-using-manifests.pl
@@ -18,7 +17,6 @@ install-exec-local: readmanifest.pm download-using-manifests.pl find-runtime-roo
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) generate-patches.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
include ../substitute.mk
@@ -34,6 +32,4 @@ EXTRA_DIST = nix-collect-garbage.in \
generate-patches.pl.in \
nix-pack-closure.in nix-unpack-closure.in \
nix-copy-closure.in \
find-runtime-roots.pl.in \
build-remote.pl.in \
nix-reduce-build.in
find-runtime-roots.pl.in

View File

@@ -1,208 +0,0 @@
#! @perl@ -w
use strict;
use Fcntl ':flock';
use English '-no_match_vars';
# General operation:
#
# Try to find a free machine of type $neededSystem. We do this as
# follows:
# - We acquire an exclusive lock on $currentLoad/main-lock.
# - For each machine $machine of type $neededSystem and for each $slot
# less than the maximum load for that machine, we try to get an
# exclusive lock on $currentLoad/$machine-$slot (without blocking).
# If we get such a lock, we send "accept" to the caller. Otherwise,
# we send "postpone" and exit.
# - We release the exclusive lock on $currentLoad/main-lock.
# - We perform the build on $neededSystem.
# - We release the exclusive lock on $currentLoad/$machine-$slot.
#
# The nice thing about this scheme is that if we die prematurely, the
# locks are released automatically.
my $loadIncreased = 0;
my $amWilling = shift @ARGV;
my $localSystem = shift @ARGV;
my $neededSystem = shift @ARGV;
my $drvPath = shift @ARGV;
sub sendReply {
my $reply = shift;
open OUT, ">&3" or die;
print OUT "$reply\n";
close OUT;
}
sub decline {
sendReply "decline";
exit 0;
}
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
decline unless defined $currentLoad;
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);
# Otherwise find a willing remote machine.
my %machines;
my %systemTypes;
my %sshKeys;
my %maxJobs;
my %curJobs;
# Read the list of machines.
open CONF, "< $conf" or die;
while (<CONF>) {
chomp;
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;
}
close CONF;
# Acquire the exclusive lock on $currentLoad/main-lock.
my $mainLock = "$currentLoad/main-lock";
open MAINLOCK, ">>$mainLock" or die;
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}) {
$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)) {
$machine = $cur;
last LOOP;
}
close SLOTLOCK;
$slot++;
}
}
}
close MAINLOCK;
# Didn't find one?
if (!defined $machine) {
if ($rightType) {
sendReply "postpone";
exit 0;
} else {
decline;
}
}
# Yes we did, accept.
sendReply "accept";
open IN, "<&4" or die;
my $x = <IN>;
chomp $x;
#print "got $x\n";
close IN;
if ($x ne "okay") {
exit 0;
}
# Do the actual job.
print "BUILDING REMOTE: $drvPath on $machine\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
# interactive.
$ENV{"DISPLAY"} = "";
$ENV{"SSH_PASSWORD_FILE="} = "";
$ENV{"SSH_ASKPASS="} = "";
my $sshOpts = "-i $sshKeys{$machine} -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
# Microsoft C compiler to fail with certain flags:
#
# http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99676
#
# So as a workaround, we pass a verbatim password. ssh tries to makes
# this very hard; the trick is to make it call SSH_ASKPASS to get the
# 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};
$passwordFile =~ s/^password://;
$sshOpts = "ssh -x";
$ENV{"SSH_PASSWORD_FILE"} = $passwordFile;
$ENV{"SSH_ASKPASS"} = "/tmp/writepass";
open WRITEPASS, ">/tmp/writepass" or die;
print WRITEPASS "#! /bin/sh\ncat \"\$SSH_PASSWORD_FILE\"";
close WRITEPASS;
chmod 0755, "/tmp/writepass" or die;
}
my $inputs = `cat inputs`; die if ($? != 0);
$inputs =~ s/\n/ /g;
my $outputs = `cat outputs`; die if ($? != 0);
$outputs =~ s/\n/ /g;
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: $?";
print "BUILDING...\n";
system("ssh $sshOpts $machine 'nix-store -rvvK $drvPath'") == 0
or die "remote build on $machine failed: $?";
print "REMOTE BUILD DONE: $drvPath on $machine\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: $?";
}

View File

@@ -19,6 +19,8 @@ chdir $tmpDir or die "cannot change to `$tmpDir': $!";
my $tmpNar = "$tmpDir/nar";
my $tmpNar2 = "$tmpDir/nar2";
END { unlink $tmpNar; unlink $tmpNar2; rmdir $tmpDir; }
# Load all manifests.
my %narFiles;
@@ -57,29 +59,12 @@ elsif ($ARGV[0] eq "--query-info") {
}
print "$storePath\n";
print "$info->{deriver}\n";
my @references = split " ", $info->{references};
my $count = scalar @references;
print "$count\n";
foreach my $reference (@references) {
print "$reference\n";
}
my $isStateStorePath = `@bindir@/nix-state --is-state-store-path-download-using-manifests $storePath`;
if($isStateStorePath ne "true" && $isStateStorePath ne "false"){
die "The call for isStateStorePath must return true or false.....";
}
if($isStateStorePath eq "true"){
my @stateReferences = split " ", $info->{stateReferences};
my $scount = scalar @stateReferences;
print "$scount\n";
foreach my $stateReference (@stateReferences) {
print "$stateReference\n";
}
print "$info->{revision}\n";
}
}
}
exit 0;
}

View File

@@ -77,9 +77,9 @@ EOF
push @instArgs, ("--attr", $ARGV[$n]);
}
elsif ($arg eq "--arg" || $arg eq "--argstr") {
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
elsif ($arg eq "--arg") {
die "$0: `--arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
push @instArgs, ("--arg", $ARGV[$n + 1], $ARGV[$n + 2]);
$n += 2;
}

View File

@@ -6,7 +6,7 @@ $binDir = "@bindir@" unless defined $binDir;
if (scalar @ARGV < 1) {
print STDERR <<EOF
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] [--gzip] PATHS...
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] PATHS...
EOF
;
exit 1;

View File

@@ -36,28 +36,6 @@ if test -n "$expHash"; then
fi
mkTempDir() {
local i=0
while true; do
if test -z "$TMPDIR"; then TMPDIR=/tmp; fi
tmpPath=$TMPDIR/nix-prefetch-url-$$-$i
if mkdir "$tmpPath"; then break; fi
# !!! to bad we can't check for ENOENT in mkdir, so this check
# is slightly racy (it bombs out if somebody just removed
# $tmpPath...).
if ! test -e "$tmpPath"; then exit 1; fi
i=$((i + 1))
done
trap removeTempDir EXIT SIGINT SIGQUIT
}
removeTempDir() {
if test -n "$tmpPath"; then
rm -rf "$tmpPath" || true
fi
}
doDownload() {
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
@@ -68,8 +46,9 @@ doDownload() {
# download the file and add it to the store.
if test -z "$finalPath"; then
mkTempDir
tmpPath=/tmp/nix-prefetch-url-$$ # !!! security?
tmpFile=$tmpPath/$name
mkdir $tmpPath # !!! retry if tmpPath already exists
# Optionally do timestamp-based caching of the download.
# Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
@@ -119,6 +98,8 @@ if test -z "$finalPath"; then
# Add the downloaded file to the Nix store.
finalPath=$(@bindir@/nix-store --add-fixed "$hashType" $tmpFile)
if test -n "$tmpPath"; then rm -rf $tmpPath || true; fi
if test -n "$expHash" -a "$expHash" != "$hash"; then
echo "hash mismatch for URL \`$url'" >&2
exit 1

View File

@@ -19,11 +19,6 @@ $stateDir = "@localstatedir@/nix" unless defined $stateDir;
my $storeDir = $ENV{"NIX_STORE_DIR"};
$storeDir = "@storedir@" unless defined $storeDir;
my $storeStateDir = $ENV{"NIX_STORE_STATE_DIR"};
$storeStateDir = "@storestatedir@" unless defined $storeStateDir;
my $ext3cowheader = $ENV{"NIX_EXT3_COW_HEADER"};
$ext3cowheader = "@ext3cowheader@" unless defined $ext3cowheader;
# Prevent access problems in shared-stored installations.
umask 0022;

View File

@@ -116,9 +116,9 @@ print NIX "]";
close NIX;
# Instantiate store derivations from the Nix expression.
# Instantiate store expressions from the Nix expression.
my @storeExprs;
print STDERR "instantiating store derivations...\n";
print STDERR "instantiating store expressions...\n";
my $pid = open(READ, "$binDir/nix-instantiate $nixExpr|")
or die "cannot run nix-instantiate";
while (<READ>) {
@@ -146,7 +146,7 @@ while (scalar @tmp > 0) {
# 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

@@ -1,68 +0,0 @@
#! @shell@
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
cd "$WORKING_DIRECTORY";
if test -z "$1" ; then
echo 'nix-reduce-build (paths or Nix expressions) -- (logins at remote computers)' >&2
echo As in: >&2
echo nix-reduce-build /etc/nixos/nixos -- user@somewhere.nowhere.example.org >&2
exit;
fi;
while ! test "$1" = "--" || test "$1" = "" ; do
echo "$1" >> initial; >&2
shift;
done
shift;
echo Will work on $(cat initial | wc -l) targets. >&2
while read ; do
case "$REPLY" in
${NIX_STORE_PATH:-/nix/store}/*)
echo "$REPLY" >> paths; >&2
;;
*)
nix-instantiate "$REPLY" >> paths;
;;
esac;
done < initial;
echo Proceeding $(cat paths | wc -l) paths. >&2
while read; do
case "$REPLY" in
*.drv)
echo "$REPLY" >> derivers; >&2
;;
*)
nix-store --query --deriver "$REPLY" >>derivers;
;;
esac;
done < paths;
echo Found $(cat derivers | wc -l) derivers. >&2
cat derivers | xargs nix-store --query -R > derivers-closure;
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
echo We need $(cat needed-paths | wc -l) paths. >&2
if test -z "$1" ; then
cat needed-paths;
fi;
for i in "$@"; do
cat needed-paths | while read; do
nix-copy-closure --from "$i" --gzip "$REPLY" </dev/null || true;
done;
mv needed-paths wanted-paths;
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
echo We still need $(cat needed-paths | wc -l) paths. >&2
done;
cd /
rm -r "$WORKING_DIRECTORY"

View File

@@ -1,5 +1,5 @@
SUBDIRS = bin2c boost libutil libext3cow libstore libmain nix-store nix-hash \
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
nix-log2xml bsdiff-4.3 nix-state
nix-log2xml bsdiff-4.3
EXTRA_DIST = aterm-helper.pl

View File

@@ -36,7 +36,6 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
static off_t offtin(u_char *buf)
{

View File

@@ -1,3 +1,5 @@
#include <iostream>
#include "eval.hh"
#include "parser.hh"
#include "hash.hh"
@@ -13,6 +15,19 @@
namespace nix {
int cacheTerms;
bool shortCircuit;
bool closedTerms; // don't substitute under terms known to be closed
bool substCache; // memoization of the term substitution function
bool posInfo; // attach position info to functions, assertions, attributes
#define maxActiveCalls 4096
ATerm activeCalls[maxActiveCalls];
unsigned int activeCallsCount = 0;
EvalState::EvalState()
@@ -23,6 +38,15 @@ EvalState::EvalState()
initNixExprHelpers();
addPrimOps();
if (!string2Int(getEnv("NIX_TERM_CACHE"), cacheTerms)) cacheTerms = 1;
shortCircuit = getEnv("NIX_SHORT_CIRCUIT", "0") == "1";
strictMode = getEnv("NIX_STRICT", "0") == "1";
closedTerms = getEnv("NIX_CLOSED_TERMS", "1") == "1";
substCache = getEnv("NIX_SUBST_CACHE", "1") == "1";
posInfo = getEnv("NIX_POS_INFO", "1") == "1";
ATprotectMemory(activeCalls, maxActiveCalls);
}
@@ -69,6 +93,19 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
}
Expr speculativeEval(EvalState & state, Expr e)
{
if (!state.strictMode) return e;
try {
return evalExpr(state, e);
} catch (EvalError & err) {
/* ignore, pass the original arg and depend on
laziness */
return e;
}
}
/* Substitute an argument set into the body of a function. */
static Expr substArgs(EvalState & state,
Expr body, ATermList formals, Expr arg)
@@ -80,7 +117,7 @@ static Expr substArgs(EvalState & state,
ATermMap args;
queryAllAttrs(arg, args);
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
subs.set(i->key, i->value);
subs.set(i->key, speculativeEval(state, i->value));
/* Get the formal arguments. */
ATermVector defsUsed;
@@ -389,7 +426,7 @@ Expr autoCallFunction(Expr e, const ATermMap & args)
Expr name, def, value; ATerm values, def2;
if (!matchFormal(*i, name, values, def2)) abort();
if ((value = args.get(name)))
actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
actualArgs.set(name, makeAttrRHS(allocCell(value), makeNoPos()));
else if (!matchDefaultValue(def2, def))
throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
% aterm2String(name));
@@ -457,7 +494,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
else if (matchFunction(fun, formals, body, pos)) {
arg = evalExpr(state, arg);
try {
return evalExpr(state, substArgs(state, body, formals, arg));
return evalExpr(state, substArgs(state, allocCells(body), formals, arg));
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the function at %1%:\n",
showPos(pos));
@@ -467,9 +504,10 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
else if (matchFunction1(fun, name, body, pos)) {
try {
arg = speculativeEval(state, arg);
ATermMap subs(1);
subs.set(name, arg);
return evalExpr(state, substitute(Substitution(0, &subs), body));
subs.set(name, allocCell(arg));
return evalExpr(state, substitute(Substitution(0, &subs), allocCells(body)));
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the function at %1%:\n",
showPos(pos));
@@ -624,6 +662,10 @@ Expr evalExpr2(EvalState & state, Expr e)
Expr e1, e2, e3;
ATerm name, pos;
int bla;
if (matchCell(e, bla, e1)) e = e1;
AFun sym = ATgetAFun(e);
/* Normal forms. */
@@ -715,41 +757,141 @@ Expr evalExpr2(EvalState & state, Expr e)
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
/* Barf. */
//printMsg(lvlError, format("%1%") % e);
abort();
}
class ShortCircuit
{
};
unsigned int fnord;
void maybeShortCircuit(EvalState & state, Expr e, Expr nf)
{
for (unsigned int i = 0; i < activeCallsCount; ++i) {
Expr fun, arg;
if (!matchCall(activeCalls[i], fun, arg)) abort();
if (arg == e) {
//printMsg(lvlError, format("blaat"));
//printMsg(lvlError, format("blaat %1% %2% %3%") % fun % arg % e);
Expr res = state.normalForms.get(makeCall(fun, nf));
if (res) {
fnord++;
//printMsg(lvlError, format("blaat"));
throw ShortCircuit();
}
}
}
}
Expr evalExpr(EvalState & state, Expr e)
{
checkInterrupt();
#if 0
#if 1
startNest(nest, lvlVomit,
format("evaluating expression: %1%") % e);
#endif
state.nrEvaluated++;
if (cacheTerms == 0) return evalExpr2(state, e);
if (cacheTerms == 2) {
int pseudoAddr;
Expr e2;
if (!matchCell(e, pseudoAddr, e2)) return evalExpr2(state, e);
}
/* Consult the memo table to quickly get the normal form of
previously evaluated expressions. */
Expr nf = state.normalForms.get(e);
if (nf) {
if (nf == makeBlackHole())
throwEvalError("infinite recursion encountered");
//if (nf == makeBlackHole())
// throwEvalError("infinite recursion encountered");
state.nrCached++;
return nf;
}
/* Otherwise, evaluate and memoize. */
state.normalForms.set(e, makeBlackHole());
try {
nf = evalExpr2(state, e);
} catch (Error & err) {
state.normalForms.remove(e);
throw;
Expr fun, arg;
if (shortCircuit && matchCall(e, fun, arg)) {
#if 0
Expr arg2 = state.normalForms.get(arg);
if (arg2) { /* the evaluated argument is now known */
//printMsg(lvlError, "foo");
/* do we know the result of the same function called
with the evaluated argument? */
Expr res = state.normalForms.get(makeCall(fun, arg2));
if (res) { /* woohoo! */
printMsg(lvlError, "dingdong");
state.normalForms.set(e, res);
return res;
}
}
#endif
assert(activeCallsCount < maxActiveCalls);
activeCalls[activeCallsCount++] = e;
//state.normalForms.set(e, makeBlackHole());
try {
nf = evalExpr2(state, e);
}
catch (ShortCircuit & exception) {
//printMsg(lvlError, "catch!");
Expr arg2 = state.normalForms.get(arg);
if (arg2) { /* the evaluated argument is now known */
/* do we know the result of the same function called
with the evaluated argument? */
Expr res = state.normalForms.get(makeCall(fun, arg2));
if (res) { /* woohoo! */
//printMsg(lvlError, "woohoo!");
//printMsg(lvlError, format("woohoo! %1% %2% %3% %4%") % fun % arg % arg2 % res);
activeCallsCount--;
state.normalForms.set(e, res);
maybeShortCircuit(state, e, res);
return res;
}
}
activeCallsCount--;
state.normalForms.remove(e);
throw; /* not for us */
}
catch (...) {
activeCallsCount--;
state.normalForms.remove(e);
throw;
}
activeCallsCount--;
state.normalForms.set(e, nf);
Expr arg2 = state.normalForms.get(arg);
if (arg2) state.normalForms.set(makeCall(fun, arg2), nf);
maybeShortCircuit(state, e, nf);
return nf;
}
else {
/* Otherwise, evaluate and memoize. */
//state.normalForms.set(e, makeBlackHole());
try {
nf = evalExpr2(state, e);
} catch (...) {
state.normalForms.remove(e);
throw;
}
state.normalForms.set(e, nf);
if (shortCircuit) maybeShortCircuit(state, e, nf);
return nf;
}
state.normalForms.set(e, nf);
return nf;
}
@@ -845,16 +987,24 @@ extern "C" {
unsigned long AT_calcAllocatedSize();
}
unsigned int substs = 0;
unsigned int substsCached = 0;
void printEvalStats(EvalState & state)
{
char x;
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
printMsg(lvlError, format("FNORD %1%") % fnord);
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% substitutions (%7% cached)")
% state.nrEvaluated % state.nrCached
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
% AT_calcAllocatedSize()
% (&x - deepestStack));
% (&x - deepestStack)
% substs
% substsCached);
if (showStats)
printATermMapStats();
}

View File

@@ -16,7 +16,7 @@ class Hash;
typedef std::map<Path, PathSet> DrvRoots;
typedef std::map<Path, Hash> DrvHashes;
/* Cache for calls to addToStore(); maps source paths to the store //THIS OK ????
/* Cache for calls to addToStore(); maps source paths to the store
paths. */
typedef std::map<Path, Path> SrcToStore;
@@ -27,6 +27,9 @@ struct EvalState;
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
extern int cacheTerms; // 0 = don't, 1 = do, 2 = "cell" terms only
struct EvalState
{
ATermMap normalForms;
@@ -38,6 +41,10 @@ struct EvalState
unsigned int nrEvaluated;
unsigned int nrCached;
bool strictMode;
ATermMap parsings; /* path -> expr mapping */
EvalState();
void addPrimOps();

View File

@@ -35,53 +35,6 @@ string DrvInfo::queryOutPath(EvalState & state) const
return outPath;
}
string DrvInfo::queryStateIdentifier(EvalState & state) const
{
if (stateIdentifier == "") {
ATermMap attrs2 = *attrs;
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
string key = (string)aterm2String(i->key); //cast because aterm2String returns a char*
//printMsg(lvlError, format("ATTR2: '%1%'") % key);
if(key == "stateIdentifier"){
PathSet context;
string value = coerceToString(state, i->value, context);
(string &) stateIdentifier = value;
}
}
//If still empty
if (trim(stateIdentifier) == "")
(string &) stateIdentifier = "__NOSTATE__";
}
return stateIdentifier;
}
string DrvInfo::queryRuntimeStateArgs(EvalState & state) const
{
if (runtimeStateArgs == "") {
ATermMap attrs2 = *attrs;
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
string key = (string)aterm2String(i->key); //cast because aterm2String returns a char*
if(key == "runtimeStateArgs"){
PathSet context;
string value = coerceToString(state, i->value, context);
(string &) runtimeStateArgs = value;
}
}
//If still empty
if (trim(runtimeStateArgs) == "")
(string &) runtimeStateArgs = "__NOARGS__";
}
return runtimeStateArgs;
}
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
{
@@ -149,23 +102,20 @@ static bool getDerivation(EvalState & state, Expr e,
boost::shared_ptr<ATermMap> attrs(new ATermMap());
queryAllAttrs(e, *attrs, false);
Expr a = attrs->get(toATerm("type"));
if (!a || evalStringNoCtx(state, a) != "derivation")
return true;
if (!a || evalStringNoCtx(state, a) != "derivation") return true;
/* Remove spurious duplicates (e.g., an attribute set like
`rec { x = derivation {...}; y = x;}'. */
if (doneExprs.find(e) != doneExprs.end())
return false;
if (doneExprs.find(e) != doneExprs.end()) return false;
doneExprs.insert(e);
DrvInfo drv;
a = attrs->get(toATerm("name"));
/* !!! We really would like to have a decent back trace here. */
if (!a)
throw TypeError("derivation name missing");
if (!a) throw TypeError("derivation name missing");
drv.name = evalStringNoCtx(state, a);
a = attrs->get(toATerm("system"));
@@ -175,9 +125,7 @@ static bool getDerivation(EvalState & state, Expr e,
drv.system = evalStringNoCtx(state, a);
drv.attrs = attrs;
//printMsg(lvlError, format("TEST '%1%'") % evalStringNoCtx(state, a) );
drv.attrPath = attrPath;
drvs.push_back(drv);
@@ -194,8 +142,7 @@ bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
Exprs doneExprs;
DrvInfos drvs;
getDerivation(state, e, "", drvs, doneExprs);
if (drvs.size() != 1)
return false;
if (drvs.size() != 1) return false;
drv = drvs.front();
return true;
}

View File

@@ -20,8 +20,6 @@ struct DrvInfo
private:
string drvPath;
string outPath;
string stateIdentifier;
string runtimeStateArgs;
public:
string name;
@@ -35,8 +33,6 @@ public:
string queryDrvPath(EvalState & state) const;
string queryOutPath(EvalState & state) const;
string queryStateIdentifier(EvalState & state) const;
string queryRuntimeStateArgs(EvalState & state) const;
MetaInfo queryMetaInfo(EvalState & state) const;
string queryMetaInfo(EvalState & state, const string & name) const;
@@ -49,17 +45,7 @@ public:
{
outPath = s;
}
void setStateIdentifier(const string & s)
{
stateIdentifier = s;
}
void setRuntimeStateArgs(const string & s)
{
runtimeStateArgs = s;
}
void setMetaInfo(const MetaInfo & meta);
};

View File

@@ -4,7 +4,6 @@
%x STRING
%x IND_STRING
%{
@@ -123,30 +122,6 @@ inherit { return INHERIT; }
<STRING>\" { BEGIN(INITIAL); return '"'; }
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->t = makeIndStr(toATerm(yytext));
return IND_STR;
}
<IND_STRING>\'\'\$ {
yylval->t = makeIndStr(toATerm("$"));
return IND_STR;
}
<IND_STRING>\'\'\' {
yylval->t = makeIndStr(toATerm("''"));
return IND_STR;
}
<IND_STRING>\'\'\\. {
yylval->t = unescapeStr(yytext + 2);
return IND_STR;
}
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->t = makeIndStr(toATerm("'"));
return IND_STR;
}
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
@@ -173,10 +148,4 @@ void backToString(yyscan_t scanner)
BEGIN(STRING);
}
void backToIndString(yyscan_t scanner)
{
struct yyguts_t * yyg = (struct yyguts_t *) scanner;
BEGIN(IND_STRING);
}
}

View File

@@ -46,9 +46,6 @@ Int | int | Expr |
Str | string ATermList | Expr |
Str | string | Expr | ObsoleteStr
# Internal to the parser, doesn't occur in ASTs.
IndStr | string | Expr |
# A path is a reference to a file system object that is to be copied
# to the Nix store when used as a derivation attribute. When it is
# concatenated to a string (i.e., `str + path'), it is also copied and
@@ -76,6 +73,8 @@ Inherit | Expr ATermList Pos | ATerm |
Scope | | Expr |
Cell | int Expr | Expr |
Formal | string ValidValues DefaultValue | ATerm |
ValidValues | ATermList | ValidValues |

View File

@@ -2,6 +2,7 @@
#include "derivations.hh"
#include "util.hh"
#include "aterm.hh"
#include "eval.hh" // !!! urgh
#include "nixexpr-ast.hh"
#include "nixexpr-ast.cc"
@@ -108,7 +109,16 @@ Expr makeAttrs(const ATermMap & attrs)
}
Expr substitute(const Substitution & subs, Expr e)
extern unsigned int substs;
extern unsigned int substsCached;
extern bool closedTerms;
extern bool substCache;
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e);
static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
{
checkInterrupt();
@@ -116,19 +126,20 @@ Expr substitute(const Substitution & subs, Expr e)
ATerm name, pos, e2;
substs++;
/* As an optimisation, don't substitute in subterms known to be
closed. */
if (matchClosed(e, e2)) return e;
if (closedTerms && matchClosed(e, e2)) return e;
if (matchVar(e, name)) {
Expr sub = subs.lookup(name);
if (sub == makeRemoved()) sub = 0;
Expr wrapped;
/* Add a "closed" wrapper around terms that aren't already
closed. The check is necessary to prevent repeated
wrapping, e.g., closed(closed(closed(...))), which kills
caching. */
return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
return sub ? ((!closedTerms || matchClosed(sub, wrapped)) ? sub : makeClosed(sub)) : e;
}
/* In case of a function, filter out all variables bound by this
@@ -140,18 +151,30 @@ Expr substitute(const Substitution & subs, Expr e)
for (ATermIterator i(formals); i; ++i) {
ATerm d1, d2;
if (!matchFormal(*i, name, d1, d2)) abort();
map.set(name, makeRemoved());
if (subs.lookup(name))
map.set(name, constRemoved);
}
if (map.size() == 0)
return makeFunction(
(ATermList) substitute(done, subs, (ATerm) formals),
substitute(done, subs, body), pos);
else {
Substitution subs2(&subs, &map);
ATermMap done2(128);
return makeFunction(
(ATermList) substitute(done2, subs2, (ATerm) formals),
substitute(done2, subs2, body), pos);
}
Substitution subs2(&subs, &map);
return makeFunction(
(ATermList) substitute(subs2, (ATerm) formals),
substitute(subs2, body), pos);
}
if (matchFunction1(e, name, body, pos)) {
ATermMap map(1);
map.set(name, makeRemoved());
return makeFunction1(name, substitute(Substitution(&subs, &map), body), pos);
if (subs.lookup(name)) {
ATermMap map(1);
map.set(name, constRemoved);
ATermMap done2(128);
return makeFunction1(name, substitute(done2, Substitution(&subs, &map), body), pos);
} else
return makeFunction1(name, substitute(done, subs, body), pos);
}
/* Idem for a mutually recursive attribute set. */
@@ -159,14 +182,21 @@ Expr substitute(const Substitution & subs, Expr e)
if (matchRec(e, rbnds, nrbnds)) {
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
for (ATermIterator i(rbnds); i; ++i)
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
else abort(); /* can't happen */
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
map.set(name, constRemoved);
for (ATermIterator i(nrbnds); i; ++i)
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
else abort(); /* can't happen */
return makeRec(
(ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
(ATermList) substitute(subs, (ATerm) nrbnds));
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
map.set(name, constRemoved);
if (map.size() == 0)
return makeRec(
(ATermList) substitute(done, subs, (ATerm) rbnds),
(ATermList) substitute(done, subs, (ATerm) nrbnds));
else {
ATermMap done2(128);
return makeRec(
(ATermList) substitute(done2, Substitution(&subs, &map), (ATerm) rbnds),
(ATermList) substitute(done, subs, (ATerm) nrbnds));
}
}
if (ATgetType(e) == AT_APPL) {
@@ -177,7 +207,73 @@ Expr substitute(const Substitution & subs, Expr e)
for (int i = 0; i < arity; ++i) {
ATerm arg = ATgetArgument(e, i);
args[i] = substitute(subs, arg);
args[i] = substitute(done, subs, arg);
if (args[i] != arg) changed = true;
}
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
}
if (ATgetType(e) == AT_LIST) {
unsigned int len = ATgetLength((ATermList) e);
ATerm es[len];
ATermIterator i((ATermList) e);
bool changed = false;
for (unsigned int j = 0; i; ++i, ++j) {
es[j] = substitute(done, subs, *i);
if (es[j] != *i) changed = true;
}
if (!changed) return e;
ATermList out = ATempty;
for (unsigned int j = len; j; --j)
out = ATinsert(out, es[j - 1]);
return (ATerm) out;
}
return e;
}
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e)
{
Expr res = done[e];
if (substCache && res) {
substsCached++;
return res;
}
res = substitute2(done, subs, e);
done.set(e, res);
return res;
}
Expr substitute(const Substitution & subs, Expr e)
{
ATermMap done(256);
return substitute(done, subs, e);
}
Expr allocCells(Expr e)
{
checkInterrupt();
ATerm e2;
if (matchClosed(e, e2)) return e;
int i;
if (matchCell(e, i, e2))
return allocCell(allocCells(e2));
if (ATgetType(e) == AT_APPL) {
AFun fun = ATgetAFun(e);
int arity = ATgetArity(fun);
ATerm args[arity];
bool changed = false;
for (int i = 0; i < arity; ++i) {
ATerm arg = ATgetArgument(e, i);
args[i] = allocCells(arg);
if (args[i] != arg) changed = true;
}
@@ -189,7 +285,7 @@ Expr substitute(const Substitution & subs, Expr e)
ATerm es[len];
ATermIterator i((ATermList) e);
for (unsigned int j = 0; i; ++i, ++j)
es[j] = substitute(subs, *i);
es[j] = allocCells(*i);
ATermList out = ATempty;
for (unsigned int j = len; j; --j)
out = ATinsert(out, es[j - 1]);
@@ -215,11 +311,7 @@ static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
ATerm with, body;
ATermList rbnds, nrbnds;
/* Closed terms don't have free variables, so we don't have to
check by definition. */
if (matchClosed(e, value)) return;
else if (matchVar(e, name)) {
if (matchVar(e, name)) {
if (!defs.get(name))
throw EvalError(format("undefined variable `%1%'")
% aterm2String(name));
@@ -403,5 +495,18 @@ string showValue(Expr e)
return "<unknown>";
}
static unsigned int cellCount = 0;
Expr allocCell(Expr e)
{
if (cacheTerms != 2) return e;
int i;
Expr e2;
if (matchCell(e, i, e2)) return e;
return makeCell(cellCount++, e);
}
}

View File

@@ -34,6 +34,9 @@ typedef ATerm Pos;
typedef vector<ATerm> ATermVector;
extern Expr constRemoved;
/* A substitution is a linked list of ATermMaps that map names to
identifiers. We use a list of ATermMaps rather than a single to
make it easy to grow or shrink a substitution when entering a
@@ -53,7 +56,8 @@ struct Substitution
{
Expr x;
for (const Substitution * s(this); s; s = s->prev)
if ((x = s->map->get(name))) return x;
if ((x = s->map->get(name)))
return x == constRemoved ? 0 : x;
return 0;
}
};
@@ -116,6 +120,11 @@ string showType(Expr e);
string showValue(Expr e);
Expr allocCell(Expr e); // make an updateable cell (for simulating conventional laziness)
Expr allocCells(Expr e); // re-allocate all cells in e
}

View File

@@ -57,7 +57,7 @@ static Expr fixAttrs(int recursive, ATermList as)
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));
*is = ATinsert(*is, makeBind(*j, allocCell(rhs), pos));
}
} else bs = ATinsert(bs, *i);
}
@@ -68,104 +68,15 @@ static Expr fixAttrs(int recursive, ATermList as)
}
static Expr stripIndentation(ATermList es)
{
if (es == ATempty) return makeStr("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
bool atStartOfLine = true; /* = seen only whitespace in the current line */
unsigned int minIndent = 1000000;
unsigned int curIndent = 0;
ATerm e;
for (ATermIterator i(es); i; ++i) {
if (!matchIndStr(*i, e)) {
/* Anti-quotations end the current start-of-line whitespace. */
if (atStartOfLine) {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
continue;
}
string s = aterm2String(e);
for (unsigned int j = 0; j < s.size(); ++j) {
if (atStartOfLine) {
if (s[j] == ' ')
curIndent++;
else if (s[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
} else {
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
} else if (s[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
}
}
/* Strip spaces from each line. */
ATermList es2 = ATempty;
atStartOfLine = true;
unsigned int curDropped = 0;
unsigned int n = ATgetLength(es);
for (ATermIterator i(es); i; ++i, --n) {
if (!matchIndStr(*i, e)) {
atStartOfLine = false;
curDropped = 0;
es2 = ATinsert(es2, *i);
continue;
}
string s = aterm2String(e);
string s2;
for (unsigned int j = 0; j < s.size(); ++j) {
if (atStartOfLine) {
if (s[j] == ' ') {
if (curDropped++ >= minIndent)
s2 += s[j];
}
else if (s[j] == '\n') {
curDropped = 0;
s2 += s[j];
} else {
atStartOfLine = false;
curDropped = 0;
s2 += s[j];
}
} else {
s2 += s[j];
if (s[j] == '\n') atStartOfLine = true;
}
}
/* Remove the last line if it is empty and consists only of
spaces. */
if (n == 1) {
unsigned int 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);
}
es2 = ATinsert(es2, makeStr(s2));
}
return makeConcatStrings(ATreverse(es2));
}
void backToString(yyscan_t scanner);
void backToIndString(yyscan_t scanner);
extern bool posInfo;
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
{
return makePos(toATerm(data->path),
loc->first_line, loc->first_column);
return posInfo ? makePos(toATerm(data->path),
loc->first_line, loc->first_column) : makeNoPos();
}
#define CUR_POS makeCurPos(yylocp, data)
@@ -212,11 +123,10 @@ 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 <ts> binds ids expr_list formals string_parts ind_string_parts
%token <t> ID INT STR IND_STR PATH URI
%type <ts> binds ids expr_list formals string_parts
%token <t> ID INT STR PATH URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
%token DOLLAR_CURLY /* == ${ */
%token IND_STRING_OPEN IND_STRING_CLOSE
%nonassoc IMPL
%left OR
@@ -291,9 +201,6 @@ expr_simple
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
else $$ = makeConcatStrings(ATreverse($2));
}
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(ATreverse($2));
}
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
| URI { $$ = makeStr($1, ATempty); }
| '(' expr ')' { $$ = $2; }
@@ -314,12 +221,6 @@ string_parts
| { $$ = ATempty; }
;
ind_string_parts
: ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
| { $$ = ATempty; }
;
binds
: binds bind { $$ = ATinsert($1, $2); }
| { $$ = ATempty; }
@@ -327,7 +228,7 @@ binds
bind
: ID '=' expr ';'
{ $$ = makeBind($1, $3, CUR_POS); }
{ $$ = makeBind($1, allocCell($3), CUR_POS); }
| INHERIT inheritsrc ids ';'
{ $$ = makeInherit($2, $3, CUR_POS); }
;
@@ -484,8 +385,13 @@ Expr parseExprFromFile(EvalState & state, Path path)
if (S_ISDIR(st.st_mode))
path = canonPath(path + "/default.nix");
Expr cached = state.parsings.get(toATerm(path));
if (cached) return cached;
/* Read and parse the input file. */
return parse(state, readFile(path).c_str(), path, dirOf(path));
cached = parse(state, readFile(path).c_str(), path, dirOf(path));
state.parsings.set(toATerm(path), cached);
return cached;
}

View File

@@ -1,14 +1,11 @@
#include "misc.hh"
#include "eval.hh"
#include "db.hh"
#include "globals.hh"
#include "store-api.hh"
#include "util.hh"
#include "archive.hh"
#include "expr-to-xml.hh"
#include "nixexpr-ast.hh"
#include "local-store.hh"
#include "parser.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -299,19 +296,18 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args)
return makeStr(getEnv(name));
}
/* Evaluate the first expression, and print its abstract syntax tree
on standard error. Then return the second expression. Useful for
debugging.
/* for debugging purposes. print the first arg on stdout (perhaps stderr should be used?)
* and return the second
*/
static Expr prim_trace(EvalState & state, const ATermVector & args)
{
Expr e = evalExpr(state, args[0]);
printMsg(lvlError, format("trace: %1%") % e);
//string str = evalStringNoCtx(state, args[0]);
Expr a = evalExpr(state, args[0]);
printf("traced value: %s\n", atPrint(a).c_str());
return evalExpr(state, args[1]);
}
static Expr prim_relativise(EvalState & state, const ATermVector & args)
{
PathSet context; /* !!! what to do? */
@@ -348,67 +344,39 @@ static Expr prim_relativise(EvalState & state, const ATermVector & args)
(basically) its outputHash. */
static Hash hashDerivationModulo(EvalState & state, Derivation drv)
{
//printMsg(lvlError, format("DRV: %1% %2%") % drv.env.find("name")->second % (drv.outputs.size() == 1));
/* Return a fixed hash for fixed-output derivations.
* E.g. derivations that have a hash
*/
/* Return a fixed hash for fixed-output derivations. */
if (drv.outputs.size() == 1) {
DerivationOutputs::const_iterator i = drv.outputs.begin();
if (i->first == "out" &&
i->second.hash != "")
{
//printMsg(lvlError, format("FIXED OUTPUT: %1% - %2% - %3%") % i->second.hashAlgo % i->second.hash % i->second.path);
return hashString(htSHA256, "fixed:out:"
+ i->second.hashAlgo + ":"
+ i->second.hash + ":"
+ i->second.path);
}
}
/* If we have a state derivation, we clear state paramters because they (sometimes) can affect the outPath:
* If this drv has runtime paramters: The state indentifier and statepath may change, but the componentPath (outPath) can stay the same
* If this drv doesnt have runtime paramters: The state indentifier and statepath may change, but the componentPath changes since it is build with another identifier
* In both cases: Other runtime state parameters like stateDirs, synchronisation and shareState never change the out or statepath so always need to be out of the hash
*/
if(isStateDrv(drv)){
if(drv.stateOutputs.size() != 1)
throw EvalError(format("There are more then one stateOutputs in the derviation....."));
DerivationStateOutput drvso = drv.stateOutputs["state"];
if(drvso.runtimeStateArgs != ""){ //Has runtime parameters --> Clear all state parameters
drv.stateOutputs.clear();
drv.stateOutputDirs.clear();
drv.env["statePath"] = "";
}
else{ //Has NO runtime parameters --> Clear state parameters selectively
drvso.clearAllRuntimeParamters();
drv.stateOutputDirs.clear();
}
}
/* 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)
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i)
{
Hash h = state.drvHashes[i->first];
if (h.type == htUnknown) {
Derivation drv2 = derivationFromPathTxn(noTxn, i->first);
Derivation drv2 = derivationFromPath(i->first);
h = hashDerivationModulo(state, drv2);
state.drvHashes[i->first] = h;
}
inputs2[printHash(h)] = i->second;
}
drv.inputDrvs = inputs2;
//printMsg(lvlError, format("%1% with %2% --> %3%") % drv.env.find("name")->second % printHash(hashTerm(unparseDerivation(drv))) % unparseDerivation(drv));
return hashTerm(unparseDerivation(drv));
return hashTerm(unparseDerivation(drv));
}
/* Construct (as a unobservable side effect) a Nix derivation
expression that performs the derivation described by the argument
set. Returns the original set extended with the following
@@ -447,18 +415,6 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
string outputHashAlgo;
bool outputHashRecursive = false;
//state vars
bool enableState = false; //We dont do state by default, but if a user defines stateDirs for example, than this becomes true.
bool disableState = false; //Becomes true if the user explicitly says: no state
string shareType = "none";
string syncState = "none";
string stateIdentifier = "";
bool createDirsBeforeInstall = false;
string runtimeStateArgs = "";
string sharedState = "";
string externalState = "";
vector<DerivationStateOutputDir> stateDirs;
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
string key = aterm2String(i->key);
ATerm value;
@@ -484,73 +440,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
drv.args.push_back(s);
}
}
//Add specific state variables
else if(key == "stateDirs") {
enableState = true;
value = evalExpr(state, value);
ATermList list = evalList(state, value);
for (ATermIterator j(list); j; ++j){
Expr v = evalExpr(state, *j);
ATermMap stateDirattrs;
queryAllAttrs(evalExpr(state, v), stateDirattrs, true);
DerivationStateOutputDir dir = DerivationStateOutputDir();
for (ATermMap::const_iterator k = stateDirattrs.begin(); k != stateDirattrs.end(); ++k) {
string statekey = aterm2String(k->key);
ATerm statevalue;
Expr statepos;
ATerm staterhs = k->value;
if (!matchAttrRHS(staterhs, statevalue, statepos)) abort();
startNest(nest, lvlVomit, format("processing statedir attribute `%1%'") % statekey);
try {
string s = trim(coerceToString(state, statevalue, context, true));
if (statekey == "dir") {
//Add a / to the end if it's not there
if(s[s.length() - 1] != '/')
s = s + "/";
//Remove the / at the beginning if it's there
if(s[0] == '/' && s.length() != 1)
s = s.substr(1, s.length());
dir.path = s;
}
else if (statekey == "file") { dir.path = s; }
else if (statekey == "type") { dir.type = s; }
else if (statekey == "interval") {
if(s == "")
continue;
int n;
if (!string2Int(s, n)) throw Error("interval is not a number");
if(n == 0) throw Error("Interval cannot be 0");
dir.interval = s;
}
else throw EvalError(format("invalid sub-attirbute `%1%' for attribute dirs") % statekey);
}
catch (Error & e) {
e.addPrefix(format("while evaluating the state derivation attribute `%1%' at %2%:\n") % key % showPos(statepos));
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n") % drvName % showPos(posDrvName));
throw;
}
}
stateDirs.push_back(dir);
}
}
else if(key == "solidStateDependency"){ externalState = coerceToString(state, value, context, true); }
else if(key == "shareType") { shareType = coerceToString(state, value, context, true); }
else if(key == "synchronization") { syncState = coerceToString(state, value, context, true); }
else if(key == "disableState") { disableState = evalBool(state, value); }
else if(key == "identifier"){ stateIdentifier = coerceToString(state, value, context, true); }
else if(key == "createDirsBeforeInstall"){ createDirsBeforeInstall = evalBool(state, value); }
else if(key == "runtimeStateArgs"){ runtimeStateArgs = coerceToString(state, value, context, true); }
else if(key == "shareStateFrom"){ sharedState = coerceToString(state, value, context, true); }
/* All other attributes are passed to the builder through
the environment. */
else {
@@ -575,6 +465,7 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
% drvName % showPos(posDrvName));
throw;
}
}
/* Everything in the context of the strings in the derivation
@@ -629,55 +520,24 @@ 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);
/* If there are no runtime paratermers, then we need to take the the stateIdentifier into account for the hash calcaulation below: hashDerivationModulo(...)
* We also add enableState to make it parse the drv to a state-drv
* We also add runtimeStateArgs for the hash calc in hashDerivationModulo(...) to check if its needs to take the stateIdentiefier into account in the hash
*/
if(enableState && !disableState){
if(runtimeStateArgs == ""){
string enableStateS = bool2string("true");
drv.stateOutputs["state"] = DerivationStateOutput("", "", "", "", stateIdentifier, enableStateS, "", "", "", runtimeStateArgs, queryCurrentUsername(), "", "", false);
}
}
drv.outputs["out"] =
DerivationOutput("", outputHashAlgo, outputHash);
/* Use the masked derivation expression to compute the output
path. */
Hash componentHash = hashDerivationModulo(state, drv);
Path outPath = makeStorePath("output:out", componentHash, drvName);
Path outPath = makeStorePath("output:out",
hashDerivationModulo(state, drv), drvName);
/* Construct the final derivation store expression. */
drv.env["out"] = outPath;
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, outputHash);
//printMsg(lvlError, format("DerivationOutput %1% %2% %3%") % outPath % outputHashAlgo % outputHash);
//only add state when we have to to keep compitibilty with the 'old' format.
//We add state when it's enbaled by the keywords, and not excplicitly disabled by the user
Path stateOutPath;
if(enableState && !disableState){
/* Add the state path based on the outPath
*
* NOTE: we do not include the username into the hash calculation of the statepath yet, multiple different users can use the same dervation
* but need different state paths. Thats why we keep a 'dummy' value e.g. global hash for everyone, and later at build time recalculate the real state path
*/
stateOutPath = makeStatePath(printHash(componentHash), drvName, stateIdentifier, queryCurrentUsername()); //State path
drv.env["statePath"] = stateOutPath;
string enableStateS = bool2string("true");
string createDirsBeforeInstallS = bool2string(createDirsBeforeInstall);
drv.stateOutputs["state"] = DerivationStateOutput(stateOutPath, printHash(componentHash), outputHashAlgo, outputHash, stateIdentifier, enableStateS,
shareType, syncState, createDirsBeforeInstallS, runtimeStateArgs, queryCurrentUsername(), sharedState, externalState);
for(vector<DerivationStateOutputDir>::iterator i = stateDirs.begin(); i != stateDirs.end(); ++i)
drv.stateOutputDirs[(*i).path] = *(i);
}
drv.outputs["out"] =
DerivationOutput(outPath, outputHashAlgo, outputHash);
/* Write the resulting term into the Nix store directory. */
Path drvPath = writeDerivation(drv, drvName);
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'") % drvName % drvPath);
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
% drvName % drvPath);
/* Optimisation, but required in read-only mode! because in that
case we don't actually write store expressions, so we can't
@@ -686,17 +546,10 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
/* !!! assumes a single output */
ATermMap outAttrs(2);
outAttrs.set(toATerm("outPath"), makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
outAttrs.set(toATerm("drvPath"), makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
/* TODO !!!!!!!!!!!!!!!!!!!! Recheck this !!!!!!!!!!!!!!!!!!!!
* We now always set the statePath since someone might 'convert' and old non-state expression into a state expression like this:
* let oldDrv = import ../../applications/networking/mailreaders/thunderbird-2.x;
* newDrv = stdenv.mkDerivation( oldDrv { ... } // { name = "x-state-v"; stateDirs = [ ... ]; } );
* in newDrv
*/
//if(enableState && !disableState)
outAttrs.set(toATerm("statePath"), makeAttrRHS(makeStr(stateOutPath, singleton<PathSet>(drvPath)), makeNoPos()));
outAttrs.set(toATerm("outPath"),
makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
outAttrs.set(toATerm("drvPath"),
makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
return makeAttrs(outAttrs);
}
@@ -711,14 +564,12 @@ static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
attrs.set(toATerm("type"),
makeAttrRHS(makeStr("derivation"), makeNoPos()));
Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
Expr drvStrict = allocCell(makeCall(makeVar(toATerm("derivation!")), eAttrs));
attrs.set(toATerm("outPath"),
makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
attrs.set(toATerm("drvPath"),
makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos()));
attrs.set(toATerm("statePath"),
makeAttrRHS(makeSelect(drvStrict, toATerm("statePath")), makeNoPos()));
return makeAttrs(attrs);
}
@@ -754,9 +605,9 @@ static Expr prim_baseNameOf(EvalState & state, const ATermVector & args)
{
PathSet context;
return makeStr(baseNameOf(coerceToString(state, args[0], context)), context);
}
/* Return the directory of the given path, i.e., everything before the
last slash. Return either a path or a string depending on the type
of the argument. */
@@ -770,17 +621,6 @@ static Expr prim_dirOf(EvalState & state, const ATermVector & args)
}
/* Return the contents of a file as a string. */
static Expr prim_readFile(EvalState & state, const ATermVector & args)
{
PathSet context;
Path path = coerceToPath(state, args[0], context);
if (!context.empty())
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
return makeStr(readFile(path));
}
/*************************************************************
* Creating files
*************************************************************/
@@ -807,7 +647,6 @@ static Expr prim_toFile(EvalState & state, const ATermVector & args)
string contents = evalString(state, args[1], context);
PathSet refs;
PathSet stateRefs; //refs refers to the file references, there are no state references in this case.
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
if (isDerivation(*i))
@@ -934,7 +773,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
string attr = evalStringNoCtx(state,e);
Expr r = makeSelect(evaledExpr, toATerm("value"));
res.set(toATerm(attr), makeAttrRHS(r, makeNoPos()));
res.set(toATerm(attr), makeAttrRHS(allocCell(r), makeNoPos()));
}
else
throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
@@ -949,7 +788,6 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
}
}
static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
{
ATermMap attrs;
@@ -964,7 +802,6 @@ static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
return makeAttrs(attrs);
}
/* Determine whether the argument is a list. */
static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
{
@@ -972,7 +809,6 @@ static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
return makeBool(matchAttrs(evalExpr(state, args[0]), list));
}
/*************************************************************
* Lists
*************************************************************/
@@ -1066,8 +902,8 @@ static Expr prim_toString(EvalState & state, const ATermVector & args)
}
/* `substring start len str' returns the substring of `str' starting
at character position `min(start, stringLength str)' inclusive and
/* `substr start len str' returns the substring of `str' starting at
character position `min(start, stringLength str)' inclusive and
ending at `min(start + len, stringLength str)'. `start' must be
non-negative. */
static Expr prim_substring(EvalState & state, const ATermVector & args)
@@ -1091,29 +927,16 @@ static Expr prim_stringLength(EvalState & state, const ATermVector & args)
}
static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector & args)
/*************************************************************
* Strictness
*************************************************************/
static Expr prim_strict(EvalState & state, const ATermVector & args)
{
PathSet context;
string s = coerceToString(state, args[0], context);
return makeStr(s, PathSet());
return evalExpr(state, makeCall(args[0], evalExpr(state, args[1])));
}
/* Expression serialization/deserialization */
static Expr prim_ExprToString ( EvalState & state, const ATermVector & args)
{
return makeStr ( atPrint ( evalExpr ( state, args [ 0 ] ) ) );
}
static Expr prim_StringToExpr ( EvalState & state, const ATermVector & args)
{
string s;
PathSet l;
if (! matchStr ( evalExpr ( state, args[0] ), s, l )) {
throw EvalError("__stringToExpr needs string argument!");
}
return ATreadFromString(s.c_str());
}
/*************************************************************
* Primop registration
@@ -1140,10 +963,6 @@ void EvalState::addPrimOps()
addPrimOp("throw", 1, prim_throw);
addPrimOp("__getEnv", 1, prim_getEnv);
addPrimOp("__trace", 2, prim_trace);
// Expr <-> String
addPrimOp("__exprToString", 1, prim_ExprToString);
addPrimOp("__stringToExpr", 1, prim_StringToExpr);
addPrimOp("relativise", 2, prim_relativise);
@@ -1156,7 +975,6 @@ void EvalState::addPrimOps()
addPrimOp("__pathExists", 1, prim_pathExists);
addPrimOp("baseNameOf", 1, prim_baseNameOf);
addPrimOp("dirOf", 1, prim_dirOf);
addPrimOp("__readFile", 1, prim_readFile);
// Creating files
addPrimOp("__toXML", 1, prim_toXML);
@@ -1186,8 +1004,9 @@ void EvalState::addPrimOps()
addPrimOp("toString", 1, prim_toString);
addPrimOp("__substring", 3, prim_substring);
addPrimOp("__stringLength", 1, prim_stringLength);
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
// Strictness
addPrimOp("strict", 2, prim_strict);
}

View File

@@ -1,17 +0,0 @@
pkglib_LTLIBRARIES = libext3cow.la
libext3cow_la_SOURCES = epoch2date.c snapshot.cc tt.c ext3cow_tools.h ext3cow_fs.h
pkginclude_HEADERS = snapshot.hh
#TODO linux kernel header
libext3cow_la_LIBADD = ../libutil/libutil.la \
../boost/format/libformat.la
AM_CXXFLAGS = -Wall \
-I$(srcdir)/.. ${aterm_include} \
-I$(srcdir)/../libutil
AM_CFLAGS = \
${aterm_include}

View File

@@ -1,24 +0,0 @@
/* epoch2date.c - an ext3cow tool for turning epoch numbers into dates.
* Copyright (C) 2003-2007 Zachary N. J. Peterson
*/
#include "ext3cow_tools.h"
int main(int argc, char** argv){
time_t time;
if(argc < 2){
fprintf(stderr, "usage: %s seconds\n", argv[0]);
exit(1);
}
time = atoi(argv[1]);
printf("%s", ctime(&time));
return 0;
}

View File

@@ -1,10 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <time.h>
#include <sys/time.h>
#include "ext3cow_fs.h"

View File

@@ -1,97 +0,0 @@
/* snapshot.c - an ext3cow tool for taking file system snapshots
* Copyright (C) 2003-2007 Zachary N. J. Peterson
*/
#include "ext3cow_tools.h"
#include "snapshot.hh"
#include "types.hh"
//using namespace nix;
namespace nix {
/////////////////////Original functions commented out:
/*
void snapshot_usage(void){
fprintf(stderr, "usage: snapshot <mountpoint>\n");
}
int snapshot_main(int argc, char** argv){
int fd;
int ret;
unsigned int epoch = 0;
char path[256] = ".\0";
if(argc > 1)
strcpy(path, argv[1]);
printf("Snapshot on %s: ", path);
fd = open(path, O_RDONLY);
// test for ext3cow fs type
if(fd < 0){
printf("failed.\n");
perror(argv[0]);
exit(1);
}
epoch = (int)ioctl(fd, EXT3COW_IOC_TAKESNAPSHOT, &epoch);
if((int)epoch < 0){
printf("failed.\n");
perror(argv[0]);
exit(1);
}
printf("%u\n", (unsigned int)epoch);
return 0;
}
*/
/////////////////////End original functions
unsigned int take_snapshot(const string & dir2) //const string & file_or_dir)
{
const char* dir = dir2.c_str();
int fd;
unsigned int epoch = 0;
//char path[256] = ".\0";
//TODO 256 length check ???
//strcpy(path, dir);
fd = open(dir, O_RDONLY);
/* test for ext3cow fs type */
if(fd < 0){
printf("failed.\n");
perror(dir);
exit(1);
}
epoch = (int)ioctl(fd, EXT3COW_IOC_TAKESNAPSHOT, &epoch);
if((int)epoch < 0){
printf("failed.\n");
perror(dir);
exit(1);
}
//printf("%u\n", (unsigned int)epoch);
return epoch;
}
}

View File

@@ -1,13 +0,0 @@
#ifndef SNAPSHOT_H_
#define SNAPSHOT_H_
#include "types.hh"
namespace nix {
//unsigned int take_snapshot(const string & file_or_dir);
unsigned int take_snapshot(const string & dir);
}
#endif /*SNAPSHOT_H_*/

View File

@@ -1,46 +0,0 @@
/* tt.c - an ext3cow tool for retreiving the current file system epoch
* Copyright (C) 2003-2007 Zachary N. J. Peterson
*/
#include "ext3cow_tools.h"
void tt_usage(void){
fprintf(stderr, "usage: tt <mountpoint>\n");
}
int tt_main(int argc, char** argv){
int fd;
int ret;
unsigned int epoch = 0;
char path[255] = ".\0";
if(argc > 1)
strcpy(path, argv[1]);
fd = open(path, O_RDONLY);
/* test for ext3cow fs type */
if(fd < 0){
fprintf(stderr, "Couldn't open %s\n", path);
exit(1);
}
ret = ioctl(fd, EXT3COW_IOC_GETEPOCH, &epoch);
if(ret < 0){
printf("tt on %s failed.\n", path);
perror(argv[0]);
exit(1);
}
printf("Epoch: %d\n", ret);
close(fd);
return 0;
}

View File

@@ -4,9 +4,6 @@ libmain_la_SOURCES = shared.cc shared.hh
AM_CXXFLAGS = \
-DNIX_STORE_DIR=\"$(storedir)\" \
-DNIX_STORE_STATE_DIR=\"$(storestatedir)\" \
-DNIX_EXT3_COW_HEADER=\"$(ext3cowheader)\" \
-DNIX_RSYNC=\"$(rsync)\" \
-DNIX_DATA_DIR=\"$(datadir)\" \
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \

View File

@@ -105,13 +105,10 @@ static void initAndRun(int argc, char * * argv)
{
/* Setup Nix paths. */
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
nixStoreState = canonPath(getEnv("NIX_STORE_STATE_DIR", NIX_STORE_STATE_DIR)); //store state dir usually /nix/state
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR)); //nix global state dir
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
nixExt3CowHeader = getEnv("NIX_EXT3_COW_HEADER", NIX_EXT3_COW_HEADER);
nixRsync = getEnv("NIX_RSYNC", NIX_RSYNC);
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));
@@ -163,29 +160,23 @@ static void initAndRun(int argc, char * * argv)
while (argc--) args.push_back(*argv++);
args.erase(args.begin());
/* We dont expand for nix-state since we need to pass arguments to other
* programs that can decide for themselves if they want expansion or not
*/
if(programId != "nix-state")
{
/* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'), and
ignore options for the ATerm library. */
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
string arg = *i;
if (string(arg, 0, 4) == "-at-") ;
else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
for (unsigned int j = 1; j < arg.length(); j++)
if (isalpha(arg[j]))
remaining.push_back((string) "-" + arg[j]);
else {
remaining.push_back(string(arg, j));
break;
}
} else remaining.push_back(arg);
}
args = remaining;
remaining.clear();
/* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'), and
ignore options for the ATerm library. */
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
string arg = *i;
if (string(arg, 0, 4) == "-at-") ;
else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
for (unsigned int j = 1; j < arg.length(); j++)
if (isalpha(arg[j]))
remaining.push_back((string) "-" + arg[j]);
else {
remaining.push_back(string(arg, j));
break;
}
} else remaining.push_back(arg);
}
args = remaining;
remaining.clear();
/* Process default options. */
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
@@ -201,15 +192,13 @@ static void initAndRun(int argc, char * * argv)
; /* !!! obsolete - remove eventually */
else if (arg == "--no-build-output" || arg == "-Q")
buildVerbosity = lvlVomit;
//we need to push back since arguments need to be passed on in the state wrapper script
else if (arg == "--help" && programId != "nix-state") {
else if (arg == "--help") {
printHelp();
remaining.push_back(arg);
return;
}
else if (arg == "--version") {
std::cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << std::endl;
remaining.push_back(arg);
return;
}
else if (arg == "--keep-failed" || arg == "-K")
keepFailed = true;
@@ -223,8 +212,6 @@ static void initAndRun(int argc, char * * argv)
readOnlyMode = true;
else if (arg == "--max-silent-time")
maxSilentTime = getIntArg(arg, i, args.end());
else if (arg == "--no-build-hook")
useBuildHook = false;
else remaining.push_back(arg);
}

View File

@@ -2,23 +2,21 @@ pkglib_LTLIBRARIES = libstore.la
libstore_la_SOURCES = \
store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \
globals.cc db.cc references.cc pathlocks.cc gc.cc store-state.cc
globals.cc db.cc references.cc pathlocks.cc gc.cc
pkginclude_HEADERS = \
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \
globals.hh db.hh references.hh pathlocks.hh \
worker-protocol.hh store-state.hh build.hh
worker-protocol.hh
libstore_la_LIBADD = ../libutil/libutil.la \
../libext3cow/libext3cow.la \
../boost/format/libformat.la
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la
BUILT_SOURCES = derivations-ast.cc derivations-ast.hh
EXTRA_DIST = derivations-ast.def derivations-ast.cc
AM_CXXFLAGS = -Wall \
-I$(srcdir)/.. ${bdb_include} ${aterm_include} -I$(srcdir)/../libutil -I$(srcdir)/../nix-state -I$(srcdir)/../libext3cow
-I$(srcdir)/.. ${bdb_include} ${aterm_include} -I$(srcdir)/../libutil
derivations-ast.cc derivations-ast.hh: ../aterm-helper.pl derivations-ast.def
$(perl) $(srcdir)/../aterm-helper.pl derivations-ast.hh derivations-ast.cc < $(srcdir)/derivations-ast.def

View File

@@ -5,8 +5,6 @@
#include "local-store.hh"
#include "db.hh"
#include "util.hh"
#include "store-state.hh"
#include "build.hh"
#include <map>
#include <iostream>
@@ -27,19 +25,6 @@
#include <grp.h>
/* Includes required for chroot support. */
#include "config.h"
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND)
namespace nix {
using std::map;
@@ -102,7 +87,6 @@ protected:
{
nrFailed = 0;
exitCode = ecBusy;
forceInputs = false;
}
virtual ~Goal()
@@ -110,8 +94,6 @@ protected:
trace("goal destroyed");
}
bool forceInputs;
public:
virtual void work() = 0;
@@ -146,11 +128,6 @@ public:
(important!), etc. */
virtual void cancel() = 0;
void setForceInputs(bool x)
{
forceInputs = x;
}
protected:
void amDone(ExitCode result);
};
@@ -606,89 +583,6 @@ void deletePathWrapped(const Path & path)
//////////////////////////////////////////////////////////////////////
/* Helper RAII class for automatically unmounting bind-mounts in
chroots. */
struct BindMount
{
Path source, target;
Paths created;
BindMount()
{
}
BindMount(const Path & source, const Path & target)
{
bind(source, target);
}
~BindMount()
{
try {
unbind();
} catch (...) {
ignoreException();
}
}
void bind(const Path & source, const Path & target)
{
#if CHROOT_ENABLED
debug(format("bind mounting `%1%' to `%2%'") % source % target);
this->source = source;
this->target = target;
created = createDirs(target);
if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
#endif
}
void unbind()
{
#if CHROOT_ENABLED
if (source == "") return;
debug(format("unmount bind-mount `%1%'") % target);
/* Urgh. Unmount sometimes doesn't succeed right away because
the mount point is still busy. It shouldn't be, because
we've killed all the build processes by now (at least when
using a build user; see the check in killUser()). But
maybe this is because those processes are still zombies and
are keeping some kernel structures busy (open files,
current directories, etc.). So retry a few times
(actually, a 1 second sleep is almost certainly enough for
the zombies to be cleaned up). */
unsigned int tries = 0;
while (umount(target.c_str()) == -1) {
if (errno == EBUSY && ++tries < 10) {
printMsg(lvlError, format("unmounting `%1%' failed, retrying after 1 second...") % target);
sleep(1);
}
else
throw SysError(format("unmounting bind-mount `%1%' failed") % target);
}
/* Get rid of the directories for the mount point created in
bind(). */
for (Paths::reverse_iterator i = created.rbegin(); i != created.rend(); ++i) {
debug(format("deleting `%1%'") % *i);
if (remove(i->c_str()) == -1)
throw SysError(format("cannot unlink `%1%'") % *i);
}
source = "";
#endif
}
};
//////////////////////////////////////////////////////////////////////
class DerivationGoal : public Goal
{
private:
@@ -706,17 +600,10 @@ private:
/* All input paths (that is, the union of FS closures of the
immediate input paths). */
PathSet inputPaths;
/* All input-state-paths (that is, the union of FS closures of the
immediate input paths). */
PathSet inputStatePaths;
/* Referenceable paths (i.e., input and output paths). */
PathSet allPaths;
/* Referenceable paths (i.e., input and output paths). */
PathSet allStatePaths;
/* User selected for running the builder. */
UserLock buildUser;
@@ -725,9 +612,6 @@ private:
/* The temporary directory. */
Path tmpDir;
/* The state directory. */
Path stateDir;
/* File descriptor for the log file. */
AutoCloseFD fdLogFile;
@@ -739,17 +623,6 @@ private:
Pipe toHook;
Pipe fromHook;
/* Whether we're currently doing a chroot build. */
bool useChroot;
/* A RAII object to delete the chroot directory. */
boost::shared_ptr<AutoDelete> autoDelChroot;
/* In chroot builds, the list of bind mounts currently active.
The destructor of BindMount will cause the binds to be
unmounted. */
list<boost::shared_ptr<BindMount> > bindMounts;
typedef void (DerivationGoal::*GoalState)();
GoalState state;
@@ -765,7 +638,7 @@ public:
{
return drvPath;
}
private:
/* The states. */
void init();
@@ -805,7 +678,7 @@ private:
void openLogFile();
/* Common initialisation to be performed in child processes (i.e.,
both in builders and in build hooks). */
both in builders and in build hooks. */
void initChild();
/* Delete the temporary directory, if we have one. */
@@ -832,6 +705,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, Worker & worker)
trace("created");
}
DerivationGoal::~DerivationGoal()
{
/* Careful: we should never ever throw an exception from a
@@ -844,6 +718,7 @@ DerivationGoal::~DerivationGoal()
}
}
void DerivationGoal::killChild()
{
if (pid != -1) {
@@ -912,23 +787,19 @@ void DerivationGoal::haveDerivation()
assert(store->isValidPath(drvPath));
/* Get the derivation. */
drv = derivationFromPathTxn(noTxn, drvPath);
drv = derivationFromPath(drvPath);
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
store->addTempRoot(i->second.path);
//TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Also add a temproot for state ????????????
/* Check what outputs paths are not already valid. */
PathSet invalidOutputs = checkPathValidity(false);
/* If they are all valid, then we're done. */
if (invalidOutputs.size() == 0) {
if(! forceInputs) {
amDone(ecSuccess);
return;
}
amDone(ecSuccess);
return;
}
/* If this is a fixed-output derivation, it is possible that some
@@ -972,10 +843,8 @@ void DerivationGoal::outputsSubstituted()
nrFailed = 0;
if (checkPathValidity(false).size() == 0) {
if (! forceInputs){
amDone(ecSuccess);
return;
}
amDone(ecSuccess);
return;
}
/* Otherwise, at least one of the output paths could not be
@@ -984,43 +853,13 @@ void DerivationGoal::outputsSubstituted()
/* The inputs must be built before we can build this goal. */
/* !!! but if possible, only install the paths that we need */
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i){
GoalPtr newGoal = worker.makeDerivationGoal(i->first);
newGoal->setForceInputs(forceInputs);
addWaitee(newGoal);
}
i != drv.inputDrvs.end(); ++i)
addWaitee(worker.makeDerivationGoal(i->first));
for (PathSet::iterator i = drv.inputSrcs.begin();
i != drv.inputSrcs.end(); ++i)
addWaitee(worker.makeSubstitutionGoal(*i));
/* Actually, I do some work twice just to be on the safe side */
string s = drv.env["exportBuildReferencesGraph"];
Strings ss = tokenizeString(s);
if (ss.size() % 2 !=0)
throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
string fileName = *i++;
Path storePath=*i++;
if (!isInStore(storePath))
throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
% storePath);
storePath = toStorePath(storePath);
if (!store->isValidPath(storePath))
throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
% storePath);
/* Build-time closure should be in dependencies
* We really want just derivation, its closure
* and outputs. Looks like we should build it.
* */
GoalPtr newGoal = worker.makeDerivationGoal(storePath);
newGoal->setForceInputs(true);
addWaitee(newGoal);
}
state = &DerivationGoal::inputsRealised;
}
@@ -1038,12 +877,6 @@ void DerivationGoal::inputsRealised()
return;
}
/* Maybe we just wanted to force build of inputs */
if (checkPathValidity(false).size() == 0) {
amDone(ecSuccess);
return;
}
/* Okay, try to build. Note that here we don't wait for a build
slot to become available, since we don't need one if there is a
build hook. */
@@ -1055,7 +888,7 @@ void DerivationGoal::inputsRealised()
void DerivationGoal::tryToBuild()
{
trace("trying to build");
try {
/* Is the build hook willing to accept this job? */
@@ -1191,9 +1024,6 @@ void DerivationGoal::buildDone()
deleteTmpDir(true);
/* In chroot builds, unmount the bind mounts ASAP. */
bindMounts.clear(); /* the destructors will do the rest */
/* Compute the FS closure of the outputs and register them as
being valid. */
computeClosure();
@@ -1288,46 +1118,25 @@ static string makeValidityRegistration(const PathSet & paths,
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
s += *i + "\n";
Path deriver = showDerivers ? store->queryDeriver(*i) : ""; //TODO HOW ABOUT MULTIPLE STATE-STORE DERIVERS? take last one ??!!
Path deriver = showDerivers ? store->queryDeriver(*i) : "";
s += deriver + "\n";
//store references
PathSet references;
store->queryStoreReferences(*i, references, 0);
store->queryReferences(*i, references);
s += (format("%1%\n") % references.size()).str();
for (PathSet::iterator j = references.begin();
j != references.end(); ++j)
s += *j + "\n";
if(store->isStateComponent(*i)){
//state references
PathSet stateReferences;
store->queryStateReferences(*i, stateReferences, 0);
s += (format("%1%\n") % stateReferences.size()).str();
for (PathSet::iterator j = stateReferences.begin();
j != stateReferences.end(); ++j)
s += *j + "\n";
//revision
string revision = unsignedInt2String(0);
s += revision + "\n";
}
}
return s;
}
DerivationGoal::HookReply DerivationGoal::tryBuildHook()
{
if (!useBuildHook) return rpDecline;
Path buildHook = getEnv("NIX_BUILD_HOOK");
if (buildHook == "") return rpDecline;
buildHook = absPath(buildHook);
@@ -1364,7 +1173,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
throw SysError(format("executing `%1%'") % buildHook);
} catch (std::exception & e) {
std::cerr << format("build hook error: %1%") % e.what() << std::endl;
std::cerr << format("build hook error: %1%\n") % e.what();
}
quickExit(1);
}
@@ -1431,7 +1240,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
*probably* already has it.) */
PathSet allInputs;
allInputs.insert(inputPaths.begin(), inputPaths.end());
computeFSClosure(drvPath, allInputs, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE
computeFSClosure(drvPath, allInputs);
string s;
for (PathSet::iterator i = allInputs.begin();
@@ -1451,7 +1260,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
/* The `references' file has exactly the format accepted by
`nix-store --register-validity'. */
writeStringToFile(referencesFN,
makeValidityRegistration(allInputs, true, false));
makeValidityRegistration(allInputs, true));
/* Tell the hook to proceed. */
writeLine(toHook.writeSide, "okay");
@@ -1543,53 +1352,47 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild()
deletePathWrapped(path);
}
}
/* Gather information necessary for computing the closure and/or
running the build hook. */
/* The outputs are referenceable paths. */
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
{
debug(format("building path `%1%'") % i->second.path);
allPaths.insert(i->second.path);
}
/* The state output is a referenceable path */
if(isStateDrv(drv))
allStatePaths.insert(drv.stateOutputs.find("state")->second.statepath);
/* Determine the full set of input paths. */
/* First, the input derivations. */
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i)
{
/* Add the relevant output closures of the input derivation
`*i' as input paths. Only add the closures of output paths
that are specified as inputs. */
assert(store->isValidPath(i->first));
Derivation inDrv = derivationFromPathTxn(noTxn, i->first);
for (StringSet::iterator j = i->second.begin(); j != i->second.end(); ++j)
if (inDrv.outputs.find(*j) != inDrv.outputs.end()){
computeFSClosure(inDrv.outputs[*j].path, inputPaths, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE (done?)
computeFSClosure(inDrv.outputs[*j].path, inputStatePaths, false, true, 0);
}
Derivation inDrv = derivationFromPath(i->first);
for (StringSet::iterator j = i->second.begin();
j != i->second.end(); ++j)
if (inDrv.outputs.find(*j) != inDrv.outputs.end())
computeFSClosure(inDrv.outputs[*j].path, inputPaths);
else
throw BuildError(
format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
% drvPath % *j % i->first);
}
/* Second, the input sources. */
for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i){
computeFSClosure(*i, inputPaths, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE (done?)
computeFSClosure(*i, inputStatePaths, false, true, 0);
}
for (PathSet::iterator i = drv.inputSrcs.begin();
i != drv.inputSrcs.end(); ++i)
computeFSClosure(*i, inputPaths);
debug(format("added input paths %1%") % showPaths(inputPaths));
debug(format("added input state paths %1%") % showPaths(inputStatePaths));
allPaths.insert(inputPaths.begin(), inputPaths.end());
allStatePaths.insert(inputStatePaths.begin(), inputStatePaths.end());
return prProceed;
}
@@ -1630,24 +1433,13 @@ void DerivationGoal::startBuilder()
env["NIX_STORE"] = nixStore;
/* Add all bindings specified in the derivation. */
for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i){
env[i->first] = i->second;
}
for (StringPairs::iterator i = drv.env.begin();
i != drv.env.end(); ++i)
env[i->first] = i->second;
/* Create a temporary directory where the build will take
place. */
tmpDir = createTempDir();
/* Create the state directory where the component can store it's state files place */
//We only create state dirs when state is enabled and when the dirs need to be created before the installation
if(drv.stateOutputs.size() != 0){
/* we check the recalculated state path at build time with the correct user for securiyt */
checkStatePath(drv);
if(drv.stateOutputs.find("state")->second.getCreateDirsBeforeInstall())
createSubStateDirsTxn(noTxn, drv.stateOutputDirs, drv.stateOutputs);
}
/* For convenience, set an environment pointing to the top build
directory. */
@@ -1709,52 +1501,13 @@ void DerivationGoal::startBuilder()
/* Write closure info to `fileName'. */
PathSet refs;
computeFSClosure(storePath, refs, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE
computeFSClosure(storePath, refs);
/* !!! in secure Nix, the writing should be done on the
build uid for security (maybe). */
writeStringToFile(tmpDir + "/" + fileName,
makeValidityRegistration(refs, false, false));
makeValidityRegistration(refs, false));
}
// The same for derivations
s = drv.env["exportBuildReferencesGraph"];
ss = tokenizeString(s);
if (ss.size() % 2 != 0)
throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
string fileName = *i++;
checkStoreName(fileName); /* !!! abuse of this function */
/* Check that the store path is valid. */
Path storePath = *i++;
if (!isInStore(storePath))
throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
% storePath);
storePath = toStorePath(storePath);
if (!store->isValidPath(storePath))
throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
% storePath);
/* Write closure info to `fileName'. */
PathSet refs1,refs;
computeFSClosure(storePath, refs1, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE
for (PathSet::iterator j = refs1.begin(); j != refs1.end() ; j++) {
refs.insert (*j);
if (isDerivation (*j)) {
Derivation deriv = derivationFromPath (*j);
for (DerivationOutputs::iterator k=deriv.outputs.begin();
k != deriv.outputs.end(); k++) {
refs.insert(k->second.path);
}
}
}
/* !!! in secure Nix, the writing should be done on the
build uid for security (maybe). */
writeStringToFile(tmpDir + "/" + fileName,
makeValidityRegistration(refs, false, false));
}
/* If `build-users-group' is not empty, then we have to build as
one of the members of that group. */
@@ -1791,56 +1544,6 @@ void DerivationGoal::startBuilder()
% buildUser.getGID() % nixStore);
}
/* Are we doing a chroot build? */
useChroot = queryBoolSetting("build-use-chroot", false);
Path tmpRootDir;
if (useChroot) {
#if CHROOT_ENABLED
/* Create a temporary directory in which we set up the chroot
environment using bind-mounts.
!!! Big danger here: since we're doing this in /tmp, there
is a risk that the admin does something like "rm -rf
/tmp/chroot-nix-*" to clean up aborted builds, and if some
of the bind-mounts are still active, then "rm -rf" will
happily recurse into those mount points (thereby deleting,
say, /nix/store). Ideally, tmpRootDir should be created in
some special location (maybe in /nix/var/nix) where Nix
takes care of unmounting / deleting old chroots
automatically. */
tmpRootDir = createTempDir("", "chroot-nix");
/* Clean up the chroot directory automatically, but don't
recurse; that would be very very bad if the unmount of a
bind-mount fails. Instead BindMount::unbind() unmounts and
deletes exactly those directories that it created to
produce the mount point, so that after all the BindMount
destructors have run, tmpRootDir should be empty. */
autoDelChroot = boost::shared_ptr<AutoDelete>(new AutoDelete(tmpRootDir, false));
printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % tmpRootDir);
Paths defaultDirs;
defaultDirs.push_back("/dev");
defaultDirs.push_back("/proc");
Paths dirsInChroot = querySetting("build-chroot-dirs", defaultDirs);
dirsInChroot.push_front(nixStore);
dirsInChroot.push_front(tmpDir);
/* Push BindMounts at the front of the list so that they get
unmounted in LIFO order. (!!! Does the C++ standard
guarantee that list elements are destroyed in order?) */
for (Paths::iterator i = dirsInChroot.begin(); i != dirsInChroot.end(); ++i)
bindMounts.push_front(boost::shared_ptr<BindMount>(new BindMount(*i, tmpRootDir + *i)));
#else
throw Error("chroot builds are not supported on this platform");
#endif
}
/* Run the builder. */
printMsg(lvlChatty, format("executing builder `%1%'") %
@@ -1866,17 +1569,6 @@ void DerivationGoal::startBuilder()
try { /* child */
#if CHROOT_ENABLED
/* If building in a chroot, do the chroot right away.
initChild() will do a chdir() to the temporary build
directory to make sure the current directory is in the
chroot. (Actually the order doesn't matter, since due
to the bind mount tmpDir and tmpRootDit/tmpDir are the
same directories.) */
if (useChroot && chroot(tmpRootDir.c_str()) == -1)
throw SysError(format("cannot change root directory to `%1%'") % tmpRootDir);
#endif
initChild();
/* Fill in the environment. */
@@ -1940,7 +1632,7 @@ void DerivationGoal::startBuilder()
% drv.builder);
} catch (std::exception & e) {
std::cerr << format("build error: %1%") % e.what() << std::endl;
std::cerr << format("build error: %1%\n") % e.what();
}
quickExit(1);
}
@@ -1976,28 +1668,14 @@ PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
void DerivationGoal::computeClosure()
{
map<Path, PathSet> allReferences; //all component references in componentpath (one drv can have multiple output paths)
map<Path, PathSet> allStateReferences; //all state references in componentpath
PathSet state_references; //all component references in statepath (one drv can have only one state path)
PathSet state_stateReferences; //all state references in statepath
map<Path, PathSet> allReferences;
map<Path, Hash> contentHashes;
//We create state dirs only when state is enabled and when the dirs need to be created after the installation
if(drv.stateOutputs.size() != 0)
if(!drv.stateOutputs.find("state")->second.getCreateDirsBeforeInstall())
createSubStateDirsTxn(noTxn, drv.stateOutputDirs, drv.stateOutputs);
/*
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
printMsg(lvlError, format("scanning for allPaths: %1%") % *i);
for (PathSet::iterator i = allStatePaths.begin(); i != allStatePaths.end(); ++i)
printMsg(lvlError, format("scanning for allStatePaths: %1%") % *i);
*/
/* Check whether the output paths were created, and grep each
output path to determine what other paths it references. Also make all
output paths read-only. */
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
{
Path path = i->second.path;
if (!pathExists(path)) {
@@ -2010,7 +1688,8 @@ void DerivationGoal::computeClosure()
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("getting attributes of path `%1%'") % path);
startNest(nest, lvlTalkative, format("scanning for component and state references inside `%1%'") % path);
startNest(nest, lvlTalkative,
format("scanning for references inside `%1%'") % path);
/* Check that fixed-output derivations produced the right
outputs (i.e., the content hash should match the specified
@@ -2045,30 +1724,14 @@ void DerivationGoal::computeClosure()
format("output path `%1%' should have %2% hash `%3%', instead has `%4%'")
% path % algo % printHash(h) % printHash(h2));
}
/* Get rid of all weird permissions. */
canonicalisePathMetaData(path);
/* In case we have an externalState:
* Just before the very first scanForReferences, we insert the solid state references
* in its table so its references will show up in the scan
*/
if(isStateDrv(drv)){
if(drv.stateOutputs.find("state")->second.externalState != ""){
PathSet singStatePath;
singStatePath.insert(drv.stateOutputs.find("state")->second.statepath);
setSolidStateReferencesTxn(noTxn, path, singStatePath);
}
}
/* For this output path, find the component references to other paths contained in it. */
/* Get rid of all weird permissions. */
canonicalisePathMetaData(path);
/* For this output path, find the references to other paths contained
in it. */
PathSet references = scanForReferences(path, allPaths);
allReferences[path] = references;
/* For this output path, find the state references to other paths contained in it. */
PathSet output_state_references = scanForStateReferences(path, allStatePaths);
allStateReferences[path] = output_state_references;
/* For debugging, print out the referenced and unreferenced
paths. */
for (PathSet::iterator i = inputPaths.begin();
@@ -2080,8 +1743,9 @@ void DerivationGoal::computeClosure()
else
debug(format("referenced input: `%1%'") % *i);
}
allReferences[path] = references;
/* If the derivation specifies an `allowedReferences'
attribute (containing a list of paths that the output may
refer to), check that all references are in that list. !!!
@@ -2093,39 +1757,13 @@ void DerivationGoal::computeClosure()
throw BuildError(format("output is not allowed to refer to path `%1%'") % *i);
}
//TODO !!!!!!!!!!!!!!!!!!!!!!!! AllowedStateReferences??
/* Hash the contents of the path. The hash is stored in the
database so that we can verify later on whether nobody has
messed with the store. !!! inefficient: it would be nice
if we could combine this with filterReferences(). */
contentHashes[path] = hashPath(htSHA256, path);
}
/* We already scanned for [Component references in Component paths] //1
* and [Component paths for state references] //2
*
* If state is enabled for the path we:
* [scan for and state references and component references in the state path] //3,4
*/
if(isStateDrv(drv)){
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
if(sharedState != ""){
if (!store->isValidStatePath(sharedState))
throw BuildError(format("sharedState path `%1%', is not a valid state path") % sharedState);
//We dont need to scan for state references since at the query to the state path we give the results of the linked-to path
}
else{
Path statePath = drv.stateOutputs.find("state")->second.statepath;
printMsg(lvlTalkative, format("scanning for component and state references inside `%1%'") % statePath);
state_references = scanForReferences(statePath, allPaths);
state_stateReferences = scanForStateReferences(statePath, allStatePaths);
}
}
/* Register each output path as valid, and register the sets of
paths referenced by each of them. This is wrapped in one
database transaction to ensure that if we crash, either
@@ -2137,93 +1775,18 @@ void DerivationGoal::computeClosure()
The reason that we do the transaction here and not on the fly
while we are scanning (above) is so that we don't hold database
locks for too long. */
Transaction txn;
createStoreTransaction(txn);
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
{
if(isStateDrv(drv)){ //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CHECK IF THE BUILDER ALSO COMES HERE TWICE WITH A RUNTIME STATEPATH DRV
Path statePath = drv.stateOutputs.find("state")->second.statepath;
string identifier = drv.stateOutputs.find("state")->second.stateIdentifier;
string user = drv.stateOutputs.find("state")->second.username;
setStateComponentTxn(txn, i->second.path, statePath, identifier , user); //Register the path as a store-state path
}
registerValidPath(txn,
i->second.path, //component path
registerValidPath(txn, i->second.path,
contentHashes[i->second.path],
allReferences[i->second.path], //set of component-references
allStateReferences[i->second.path], //set of state-references
drvPath, 0);
allReferences[i->second.path],
drvPath);
}
//Register the state path valid
if(isStateDrv(drv))
{
Path statePath = drv.stateOutputs.find("state")->second.statepath;
registerValidPath(txn,
statePath,
Hash(), //emtpy hash
state_references,
state_stateReferences,
drvPath, 0);
//Convert stateInfo from drv to DB format
//And set all interval-ed paths to zero to begin with
DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs;
StateInfos infos;
CommitIntervals intervals;
for (DerivationStateOutputDirs::const_reverse_iterator j = stateOutputDirs.rbegin(); j != stateOutputDirs.rend(); ++j){
DerivationStateOutputDir d = j->second;
StateInfo si;
si.path = d.path;
si.type = d.type;
if(d.interval != ""){
bool succeed = string2UnsignedInt(d.interval, si.interval);
if(!succeed)
throw Error(format("Malformed interval value '%1%' in drv '%2%'") % d.interval % drvPath);
}
else
si.interval = 0;
infos.push_back(si);
if(d.type == "interval")
intervals[d.path] = 0;
}
setStatePathsIntervalTxn(txn, statePath, intervals); //Set intervals to zero
setVersionedStateEntriesTxn(txn, statePath, infos); //register subdirs/files and their types of versioning
//register state options that may change
DerivationStateOutput drvso = drv.stateOutputs["state"];
setStateOptionsTxn(txn, statePath, queryCallingUsername(), "nixbld", 700, drvso.runtimeStateArgs);
//Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO)
RevisionClosure rivisionMapping;
rivisionMapping[statePath] = commitStatePathTxn(txn, statePath);
//Save the new revision
setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision.");
//Shared state
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
if(sharedState != ""){
shareStateTxn(txn, sharedState, statePath, false);
}
//If not shared: create the dir and set the rights
else{
ensureStateDir(statePath, queryCallingUsername(), "nixbld", "700");
}
}
txn.commit();
/* It is now safe to delete the lock files, since all future
lockers will see that the output paths are valid; they will not
create new lock files with the same names as the old (unlinked)
@@ -2292,8 +1855,7 @@ void DerivationGoal::deleteTmpDir(bool force)
getOwnership(tmpDir);
}
else
deletePathWrapped(tmpDir);
deletePathWrapped(tmpDir);
tmpDir = "";
}
}
@@ -2341,7 +1903,7 @@ class SubstitutionGoal : public Goal
private:
/* The store path that should be realised through a substitute. */
Path storePath; //TODO !!!!!!!!!!!!!!!!!!!!! add statePath?
Path storePath;
/* The remaining substituters. */
Paths subs;
@@ -2352,7 +1914,6 @@ private:
/* Path info returned by the substituter's --query-info operation. */
bool infoOkay;
PathSet references;
PathSet stateReferences; /* Outgoing state references for this path. */
Path deriver;
/* Pipe for the substitute's standard output/error. */
@@ -2554,12 +2115,6 @@ void SubstitutionGoal::tryToRun()
if (pathExists(storePath))
deletePathWrapped(storePath);
/* TODO Remove the (stale) state path if it exists. ????????????????? !!!!!!!!!!!!!!!!!!!! */
//if(isStatePath .... ??)
// if (pathExists(statePath))
// deletePathWrapped(statePath);
/* Fork the substitute program. */
pid = fork();
switch (pid) {
@@ -2588,7 +2143,7 @@ void SubstitutionGoal::tryToRun()
throw SysError(format("executing `%1%'") % sub);
} catch (std::exception & e) {
std::cerr << format("substitute error: %1%") % e.what() << std::endl;
std::cerr << format("substitute error: %1%\n") % e.what();
}
quickExit(1);
}
@@ -2652,8 +2207,8 @@ void SubstitutionGoal::finished()
Transaction txn;
createStoreTransaction(txn);
registerValidPath(txn, storePath, contentHash, //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! how about substituing a state path ?????
references, stateReferences, deriver, 0);
registerValidPath(txn, storePath, contentHash,
references, deriver);
txn.commit();
outputLock->setDeletion(true);
@@ -2691,8 +2246,7 @@ static bool working = false;
Worker::Worker()
{
/* Debugging: prevent recursive workers. */
if (working)
abort();
if (working) abort();
working = true;
nrChildren = 0;
}
@@ -2889,11 +2443,10 @@ void Worker::getInfo()
args.push_back("--query-info");
args.insert(args.end(), paths2.begin(), paths2.end());
string res = runProgram(sub, false, args);
//printMsg(lvlError, format("run: '%1%' res: '%2%'") % sub % res);
std::istringstream str(res);
while (true) {
ValidPathInfo info = decodeValidPathInfo(str); //TODO Test is this is ok with the state-brand extension to struct ValidPath
ValidPathInfo info = decodeValidPathInfo(str);
if (info.path == "") break;
/* !!! inefficient */
@@ -2904,10 +2457,8 @@ void Worker::getInfo()
if (goal) {
SubstitutionGoal * goal2 = dynamic_cast<SubstitutionGoal *>(goal.get());
if (goal2->storePath == info.path) {
goal2->references = info.references;
goal2->references = info.references;
goal2->deriver = info.deriver;
//goal2->stateReferences = info.stateReferences; //TODO !!!!!!!!!!!!!!!!!!!!!!!!!! STATE REFERENCES FOR A SubstitutionGoal
//goal2->revision = info.revision;
goal2->infoOkay = true;
wakeUp(goal);
}
@@ -3091,11 +2642,11 @@ void LocalStore::buildDerivations(const PathSet & drvPaths)
format("building %1%") % showPaths(drvPaths));
Worker worker;
Goals goals;
for (PathSet::const_iterator i = drvPaths.begin(); i != drvPaths.end(); ++i){
printMsg(lvlError, format("BUILD: '%1%'") % *i);
for (PathSet::const_iterator i = drvPaths.begin();
i != drvPaths.end(); ++i)
goals.insert(worker.makeDerivationGoal(*i));
}
worker.run(goals);
@@ -3112,14 +2663,10 @@ void LocalStore::buildDerivations(const PathSet & drvPaths)
}
void ensurePathTxn(const Transaction & txn, const Path & path)
void LocalStore::ensurePath(const Path & path)
{
/* If the path is already valid, we're done. */
if (isValidPathTxn(txn, path))
return;
printMsg(lvlError, format("ensurePathTxn(%1%)") % path);
if (store->isValidPath(path)) return;
Worker worker;
GoalPtr goal = worker.makeSubstitutionGoal(path);
@@ -3131,10 +2678,5 @@ void ensurePathTxn(const Transaction & txn, const Path & path)
throw Error(format("path `%1%' does not exist and cannot be created") % path);
}
void LocalStore::ensurePath(const Path & path)
{
ensurePathTxn(noTxn, path);
}
}

View File

@@ -1,12 +0,0 @@
#ifndef BUILD_HH_
#define BUILD_HH_
#include "db.hh"
namespace nix {
void ensurePathTxn(const Transaction & txn, const Path & path);
}
#endif /*BUILD_HH_*/

View File

@@ -1,9 +1,6 @@
#include "db.hh"
#include "util.hh"
#include "pathlocks.hh"
#include "derivations.hh"
#include "local-store.hh"
#include "misc.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -71,7 +68,7 @@ Transaction::~Transaction()
void Transaction::begin(Database & db)
{
assert(txn == 0);
db.requireEnv("begin transaction");
db.requireEnv();
try {
db.env->txn_begin(0, &txn, 0);
} catch (DbException e) { rethrow(e); }
@@ -110,11 +107,11 @@ void Transaction::moveTo(Transaction & t)
}
void Database::requireEnv(string debug)
void Database::requireEnv()
{
checkInterrupt();
if (!env) throw Error(format("database environment is not open while trying '%1%'"
"(maybe you don't have sufficient permission?)") % debug);
if (!env) throw Error("database environment is not open "
"(maybe you don't have sufficient permission?)");
}
@@ -216,9 +213,9 @@ void Database::open2(const string & path, bool removeOldEnv)
run db_recover on the database to remove the existing DB
environment (since changes only take effect on new
environments). */
env->set_lk_max_locks(100000);
env->set_lk_max_lockers(100000);
env->set_lk_max_objects(100000);
env->set_lk_max_locks(10000);
env->set_lk_max_lockers(10000);
env->set_lk_max_objects(10000);
env->set_lk_detect(DB_LOCK_DEFAULT);
/* Dangerous, probably, but from the docs it *seems* that BDB
@@ -310,7 +307,7 @@ void Database::close()
TableId Database::openTable(const string & tableName, bool sorted)
{
requireEnv(tableName);
requireEnv();
TableId table = nextId++;
try {
@@ -457,7 +454,6 @@ void Database::enumTable(const Transaction & txn, TableId table,
} catch (DbException e) { rethrow(e); }
}
void Database::clearTable(const Transaction & txn, TableId table)
{

View File

@@ -54,14 +54,12 @@ private:
TableId nextId;
std::map<TableId, Db *> tables;
void requireEnv(string debug);
void requireEnv();
Db * getDb(TableId table);
void open2(const string & path, bool removeOldEnv);
public:
Database();
~Database();

View File

@@ -1,22 +1,10 @@
init initDerivationsHelpers
Derive | ATermList ATermList ATermList ATermList ATermList string string ATermList ATermList | ATerm |
Derive | ATermList ATermList ATermList string string ATermList ATermList | ATerm |
| string string | ATerm | EnvBinding |
| string ATermList | ATerm | DerivationInput |
| string string string string | ATerm | DerivationOutput |
| string string string string string string string string string string string string string string | ATerm | DerivationStateOutput |
| string string string | ATerm | DerivationStateOutputDir |
#We use DeriveWithOutState to create derivations that dont use state, and thus dont have the stateDerivationStateOutput and DerivationStateOutputDir in their derivation
#Ive put this in becuase its easy to stay backwards compatible, but maybe it should be removed somtime to prevent confusion & duplication
#The function will be called matchDerivateWithOutState, but it will match the Derive term to remain backwards compatible
Derive | ATermList ATermList ATermList string string ATermList ATermList | ATerm | DeriveWithOutState
| string string | ATerm | EnvBindingWithOutState |
| string ATermList | ATerm | DerivationInputWithOutState |
| string string string string | ATerm | DerivationOutputWithOutState |
#end drv without state
Closure | ATermList ATermList | ATerm | OldClosure |
| string ATermList | ATerm | OldClosureElem |

View File

@@ -2,7 +2,6 @@
#include "store-api.hh"
#include "aterm.hh"
#include "globals.hh"
#include "util.hh"
#include "derivations-ast.hh"
#include "derivations-ast.cc"
@@ -21,12 +20,12 @@ 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)
for (DerivationInputs::const_iterator i = drv.inputDrvs.begin();
i != drv.inputDrvs.end(); ++i)
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
held during a garbage collection). */
string suffix = name + drvExtension;
string contents = atPrint(unparseDerivation(drv));
return readOnlyMode
@@ -67,14 +66,9 @@ Derivation parseDerivation(ATerm t)
{
Derivation drv;
ATermList outs, inDrvs, inSrcs, args, bnds;
ATermList stateOuts = ATempty, stateOutDirs = ATempty;
ATerm builder, platform;
bool withState;
if (matchDerive(t, outs, stateOuts, stateOutDirs, inDrvs, inSrcs, platform, builder, args, bnds) ) { withState = true; }
else if (matchDeriveWithOutState(t, outs, inDrvs, inSrcs, platform, builder, args, bnds) ) { withState = false; }
else
if (!matchDerive(t, outs, inDrvs, inSrcs, platform, builder, args, bnds))
throwBadDrv(t);
for (ATermIterator i(outs); i; ++i) {
@@ -88,46 +82,6 @@ Derivation parseDerivation(ATerm t)
out.hash = aterm2String(hash);
drv.outputs[aterm2String(id)] = out;
}
if(withState)
{
//parse state part
for (ATermIterator i(stateOuts); i; ++i) {
ATerm id, statepath, componentHash, hashAlgo, hash, stateIdentifier, enabled, shareType, synchronization, createDirsBeforeInstall, runtimeStateArgs, username, sharedState, externalState; //TODO unitialized warning
if (!matchDerivationStateOutput(*i, id, statepath, componentHash, hashAlgo, hash, stateIdentifier, enabled, shareType, synchronization, createDirsBeforeInstall, runtimeStateArgs, username, sharedState, externalState))
throwBadDrv(t);
DerivationStateOutput stateOut;
stateOut.statepath = aterm2String(statepath);
stateOut.componentHash = aterm2String(componentHash);
//checkPath(stateOut.path); //should we check the statpath .... ???
stateOut.hashAlgo = aterm2String(hashAlgo);
stateOut.hash = aterm2String(hash);
stateOut.stateIdentifier = aterm2String(stateIdentifier);
stateOut.enabled = aterm2String(enabled);
stateOut.shareType = aterm2String(shareType);
stateOut.synchronization = aterm2String(synchronization);
stateOut.createDirsBeforeInstall = aterm2String(createDirsBeforeInstall);
stateOut.runtimeStateArgs = aterm2String(runtimeStateArgs);
stateOut.username = aterm2String(username);
stateOut.sharedState = aterm2String(sharedState);
stateOut.externalState = aterm2String(externalState);
drv.stateOutputs[aterm2String(id)] = stateOut;
}
//parse state dirs part
for (ATermIterator i(stateOutDirs); i; ++i) {
ATerm id, path, type, interval;
if (!matchDerivationStateOutputDir(*i, id, type, interval))
throwBadDrv(t);
path = id; //We prevent duplication since the key is also the path
DerivationStateOutputDir stateOutDirs;
stateOutDirs.path = aterm2String(path);
stateOutDirs.type = aterm2String(type);
stateOutDirs.interval = aterm2String(interval);
drv.stateOutputDirs[aterm2String(id)] = stateOutDirs;
}
}
for (ATermIterator i(inDrvs); i; ++i) {
ATerm drvPath;
@@ -166,7 +120,8 @@ Derivation parseDerivation(ATerm t)
ATerm unparseDerivation(const Derivation & drv)
{
ATermList outputs = ATempty;
for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin(); i != drv.outputs.rend(); ++i)
for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin();
i != drv.outputs.rend(); ++i)
outputs = ATinsert(outputs,
makeDerivationOutput(
toATerm(i->first),
@@ -174,42 +129,6 @@ ATerm unparseDerivation(const Derivation & drv)
toATerm(i->second.hashAlgo),
toATerm(i->second.hash)));
//decide if we need to add state to the derivation
bool createState = false;
ATermList stateOutputs = ATempty;
for (DerivationStateOutputs::const_reverse_iterator i = drv.stateOutputs.rbegin(); i != drv.stateOutputs.rend(); ++i){
if(i->second.enabled == "true"){
createState = true;
}
stateOutputs = ATinsert(stateOutputs,
makeDerivationStateOutput(
toATerm(i->first),
toATerm(i->second.statepath),
toATerm(i->second.componentHash),
toATerm(i->second.hashAlgo),
toATerm(i->second.hash),
toATerm(i->second.stateIdentifier),
toATerm(i->second.enabled),
toATerm(i->second.shareType),
toATerm(i->second.synchronization),
toATerm(i->second.createDirsBeforeInstall),
toATerm(i->second.runtimeStateArgs),
toATerm(i->second.username),
toATerm(i->second.sharedState),
toATerm(i->second.externalState)
));
}
ATermList stateOutputDirs = ATempty;
for (DerivationStateOutputDirs::const_reverse_iterator i = drv.stateOutputDirs.rbegin(); i != drv.stateOutputDirs.rend(); ++i)
stateOutputDirs = ATinsert(stateOutputDirs,
makeDerivationStateOutputDir(
toATerm(i->first),
toATerm(i->second.type),
toATerm(i->second.interval)
));
ATermList inDrvs = ATempty;
for (DerivationInputs::const_reverse_iterator i = drv.inputDrvs.rbegin();
i != drv.inputDrvs.rend(); ++i)
@@ -231,28 +150,14 @@ ATerm unparseDerivation(const Derivation & drv)
toATerm(i->first),
toATerm(i->second)));
if(createState){
return makeDerive(
outputs,
stateOutputs,
stateOutputDirs,
inDrvs,
toATermList(drv.inputSrcs),
toATerm(drv.platform),
toATerm(drv.builder),
args,
env);
}
else{
return makeDeriveWithOutState(
outputs,
inDrvs,
toATermList(drv.inputSrcs),
toATerm(drv.platform),
toATerm(drv.builder),
args,
env);
}
return makeDerive(
outputs,
inDrvs,
toATermList(drv.inputSrcs),
toATerm(drv.platform),
toATerm(drv.builder),
args,
env);
}

View File

@@ -4,7 +4,6 @@
typedef struct _ATerm * ATerm;
#include "hash.hh"
#include "util.hh"
#include <map>
@@ -21,8 +20,8 @@ const string drvExtension = ".drv";
struct DerivationOutput
{
Path path;
string hashAlgo; /* hash used for expected hash computation */
string hash; /* expected hash, may be null */
string hashAlgo; /* hash used for expected hash computation */
string hash; /* expected hash, may be null */
DerivationOutput()
{
}
@@ -34,147 +33,17 @@ struct DerivationOutput
}
};
struct DerivationStateOutput
{
Path statepath;
string componentHash;
string hashAlgo;
string hash;
string stateIdentifier; //the identifier
string enabled; //enable or disable state
string shareType; //none, full, group
string synchronization; //none (no locks), exclusive-lock, recursive-exclusive-lock
string commitReferences; //TODO none, direct, recursive-all
string commitBinaries; //TODO list of binaries that need (or not) to be committed when these binaries are called
string createDirsBeforeInstall; //if true: creates state dirs before installation
string runtimeStateArgs; //if not empty: these are the runtime parameters where state can be found (you can use $statepath here)
string username;
string sharedState; //Path to share state From
string externalState; //a statePath not not in the stateStore (Official hack)
DerivationStateOutput()
{
}
//TODO add const ??
DerivationStateOutput(Path statepath, string componentHash, string hashAlgo, string hash, string stateIdentifier, string enabled, string shareType, string synchronization, string createDirsBeforeInstall, string runtimeStateArgs, string username, string sharedState, string externalState, bool check=true)
{
if(check){
if(shareType != "none" && shareType != "full" && shareType != "group")
throw Error(format("shareType '%1%' is not a correct type") % shareType);
if(synchronization != "none" && synchronization != "exclusive-lock" && synchronization != "recursive-exclusive-lock")
throw Error(format("synchronization '%1%' is not a correct type") % synchronization);
if(username == "")
throw Error(format("Username cannot be empty"));
if(stateIdentifier == "__EMTPY__" || stateIdentifier == "__NOSTATE__")
throw Error(format("the stateIdenfier cannot be this value '%1%'") % stateIdentifier);
if(runtimeStateArgs == "__NOARGS__")
throw Error(format("the runtimeStateArgs cannot be this value '%1%'") % runtimeStateArgs);
if(externalState != "" && sharedState != "")
throw Error(format("You cannot have an externalState and sharedState at the same time"));
}
//TODO
//commitReferences
//commitBinaries
this->statepath = statepath;
this->componentHash = componentHash;
this->hashAlgo = hashAlgo;
this->hash = hash;
this->stateIdentifier = stateIdentifier;
this->enabled = enabled;
this->shareType = shareType;
this->synchronization = synchronization;
this->createDirsBeforeInstall = createDirsBeforeInstall;
this->runtimeStateArgs = runtimeStateArgs;
this->username = username;
this->sharedState = sharedState;
this->externalState = externalState;
}
bool getEnabled(){
return string2bool(enabled);
}
bool getCreateDirsBeforeInstall(){
return string2bool(createDirsBeforeInstall);
}
/*
* This sets all the paramters to "" to ensure they're not taken into account for the hash calculation in primops.cc
*/
void clearAllRuntimeParamters(){
this->statepath = "";
this->componentHash = "";
//this->hashAlgo; //Clear this one?
//this->hash; //Clear this one?
//this->stateIdentifier; //Changes the statepath directly
this->enabled = "";
this->shareType = "";
this->synchronization = "";
this->createDirsBeforeInstall = "";
this->runtimeStateArgs = "";
//this->username; //Changes the statepath directly
this->sharedState = "";
this->externalState = "";
}
};
struct DerivationStateOutputDir
{
string path;
string type; //none, manual, interval, full
string interval; //type int
DerivationStateOutputDir()
{
}
DerivationStateOutputDir(string path, string type, string interval)
{
if(type != "none" && type != "manual" && type != "interval" && type != "full")
throw Error(format("interval '%1%' is not a correct type") % type);
this->path = path;
this->type = type;
this->interval = interval;
}
int getInterval(){
if(interval == "")
return 0;
else{
int i;
if (!string2Int(interval, i)) throw Error("interval is not a number");
return i;
}
}
//sort function
bool operator<(const DerivationStateOutputDir& a) const { return path < a.path; }
};
typedef std::map<string, DerivationOutput> DerivationOutputs;
typedef std::map<string, DerivationStateOutput> DerivationStateOutputs;
typedef std::map<string, DerivationStateOutputDir> DerivationStateOutputDirs;
/* For inputs that are sub-derivations, we specify exactly which
output IDs we are interested in. */
typedef std::map<Path, StringSet> DerivationInputs;
typedef std::map<string, string> StringPairs;
struct Derivation
{
DerivationOutputs outputs; /* keyed on symbolic IDs */
DerivationStateOutputs stateOutputs; /* TODO */
DerivationStateOutputDirs stateOutputDirs; /* TODO */
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
PathSet inputSrcs; /* inputs that are sources */
string platform;

View File

@@ -26,8 +26,6 @@ static string gcLockName = "gc.lock";
static string tempRootsDir = "temproots";
static string gcRootsDir = "gcroots";
static const int defaultGcLevel = 1000;
/* Acquire the global GC lock. This is used to prevent new Nix
processes from starting after the temporary root files have been
@@ -409,13 +407,12 @@ static void addAdditionalRoots(PathSet & roots)
static void dfsVisit(const PathSet & paths, const Path & path,
PathSet & visited, Paths & sorted)
{
if (visited.find(path) != visited.end())
return;
if (visited.find(path) != visited.end()) return;
visited.insert(path);
PathSet references;
if (store->isValidPath(path))
store->queryStoreReferences(path, references, 0);
store->queryReferences(path, references);
for (PathSet::iterator i = references.begin();
i != references.end(); ++i)
@@ -427,8 +424,6 @@ static void dfsVisit(const PathSet & paths, const Path & path,
sorted.push_front(path);
}
//TODO maybe? stateTopoSortPaths(const PathSet & paths)
Paths topoSortPaths(const PathSet & paths)
{
@@ -440,7 +435,7 @@ Paths topoSortPaths(const PathSet & paths)
}
void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, //TODO ? statePathsToDelete
void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed)
{
result.clear();
@@ -450,15 +445,11 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
queryBoolSetting("gc-keep-outputs", false);
bool gcKeepDerivations =
queryBoolSetting("gc-keep-derivations", true);
int gcKeepOutputsThreshold =
queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel);
//printMsg(lvlError, format("gcKeepOutputs %1% gcKeepDerivations: %2%") % gcKeepOutputs % gcKeepDerivations);
/* Acquire the global GC root. This prevents
a) New roots from being added.
b) Processes from creating new temporary root files. */
AutoCloseFD fdGCLock = openGCLock(ltWrite); //TODO !!!!!!!!!!!!!!!!!!! GET STATE LOCKS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
AutoCloseFD fdGCLock = openGCLock(ltWrite);
/* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */
@@ -467,7 +458,6 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
PathSet roots;
for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i)
roots.insert(i->second);
/* Add additional roots returned by the program specified by the
NIX_ROOT_FINDER environment variable. This is typically used
@@ -485,85 +475,32 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
roots under the `references' relation. */
PathSet livePaths;
for (PathSet::const_iterator i = roots.begin(); i != roots.end(); ++i)
computeFSClosure(canonPath(*i), livePaths, true, true, 0);
computeFSClosure(canonPath(*i), livePaths);
/* Note that the deriver need not be valid (e.g., if we
previously ran the collector with `gcKeepDerivations'
turned off). */
if (gcKeepDerivations) {
for (PathSet::iterator i = livePaths.begin(); i != livePaths.end(); ++i)
for (PathSet::iterator i = livePaths.begin();
i != livePaths.end(); ++i)
{
if (store->isStateComponent(*i)){
//printMsg(lvlError, format("Live state store '%1%'") % *i);
PathSet derivers = store->queryDerivers(*i); //we select ALL state Derivations here
for (PathSet::const_iterator j = derivers.begin(); j != derivers.end(); ++j)
if (*j != "" && store->isValidPath(*j))
computeFSClosure(*j, livePaths, true, true, 0);
}
else if (store->isValidPath(*i)){
//printMsg(lvlError, format("Live Store '%1%'") % *i);
Path deriver = store->queryDeriver(*i);
if (deriver != "" && store->isValidPath(deriver))
computeFSClosure(deriver, livePaths, true, true, 0);
}
else if (store->isValidStatePath(*i)){
//printMsg(lvlError, format("Live State '%1%'") % *i);
Path deriver = queryStatePathDrvTxn(noTxn, *i);
if(!store->isValidPath(deriver))
throw Error(format("deriver `%1%' of state-store component `%2%' in GC is not valid") % deriver % *i);
computeFSClosure(deriver, livePaths, true, true, 0);
}
/* Note that the deriver need not be valid (e.g., if we
previously ran the collector with `gcKeepDerivations'
turned off). */
Path deriver = store->queryDeriver(*i);
if (deriver != "" && store->isValidPath(deriver))
computeFSClosure(deriver, livePaths);
}
}
if (gcKeepOutputs) {
/* Hmz, identical to storePathRequisites in nix-store. */
for (PathSet::iterator i = livePaths.begin(); i != livePaths.end(); ++i)
for (PathSet::iterator i = livePaths.begin();
i != livePaths.end(); ++i)
if (isDerivation(*i)) {
Derivation drv = derivationFromPathTxn(noTxn, *i);
/*
* TODO REMOVE
<<<<<<< .working
Derivation drv = derivationFromPath(*i);
for (DerivationOutputs::iterator j = drv.outputs.begin();
j != drv.outputs.end(); ++j)
if (store->isValidPath(j->second.path))
computeFSClosure(j->second.path, livePaths, true, true, 0);
else if (store->isValidStatePath(j->second.path))
computeFSClosure(j->second.path, livePaths, true, true, 0);
=======
string gcLevelStr = drv.env["__gcLevel"];
int gcLevel;
if (!string2Int(gcLevelStr,gcLevel)) {
gcLevel = defaultGcLevel;
}
if (gcLevel >= gcKeepOutputsThreshold)
for (DerivationOutputs::iterator j = drv.outputs.begin();
j != drv.outputs.end(); ++j)
if (store->isValidPath(j->second.path) || store->isValidStatePath(j->second.path))
computeFSClosure(j->second.path, livePaths, true, true, 0);
}
=======
*/
string gcLevelStr = drv.env["__gcLevel"];
int gcLevel;
if (!string2Int(gcLevelStr, gcLevel))
gcLevel = defaultGcLevel;
if (gcLevel >= gcKeepOutputsThreshold)
for (DerivationOutputs::iterator j = drv.outputs.begin();
j != drv.outputs.end(); ++j)
if (store->isValidPath(j->second.path) || store->isValidStatePath(j->second.path))
computeFSClosure(j->second.path, livePaths, true, true, 0);
}
computeFSClosure(j->second.path, livePaths);
}
}
if (action == gcReturnLive) {
@@ -577,7 +514,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
PathSet tempRoots;
FDs fds;
readTempRoots(tempRoots, fds);
/* Close the temporary roots. Note that we *cannot* do this in
readTempRoots(), because there we may not have all locks yet,
meaning that an invalid path can become valid (and thus add to
@@ -587,9 +524,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
PathSet tempRootsClosed;
for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i)
if (store->isValidPath(*i))
computeFSClosure(*i, tempRootsClosed, true, true, 0);
else if(store->isValidStatePath(*i))
computeFSClosure(*i, tempRootsClosed, true, true, 0);
computeFSClosure(*i, tempRootsClosed);
else
tempRootsClosed.insert(*i);
@@ -602,67 +537,29 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
that is not currently in in `livePaths' or `tempRootsClosed'
can be deleted. */
/*
* We lookup all state (also shared) paths for: livePaths, tempRootsClosed
*/
PathSet allLiveStatePaths;
for (PathSet::iterator i = livePaths.begin(); i != livePaths.end(); ++i)
if(store->isValidStatePath(*i)){
allLiveStatePaths.insert(*i);
allLiveStatePaths = pathSets_union(getSharedWithPathSetRecTxn(noTxn, *i), allLiveStatePaths);
}
for (PathSet::iterator i = tempRootsClosed.begin(); i != tempRootsClosed.end(); ++i)
if(store->isValidStatePath(*i)){
allLiveStatePaths.insert(*i);
allLiveStatePaths = pathSets_union(getSharedWithPathSetRecTxn(noTxn, *i), allLiveStatePaths);
}
/* Read the Nix store and state directory's to find all currently existing
/* Read the Nix store directory to find all currently existing
paths. */
PathSet storePathSet;
PathSet statePathSet;
if (action != gcDeleteSpecific) {
Paths entries = readDirectory(nixStore);
Paths stateEntries = readDirectory(nixStoreState);
for (Paths::iterator i = entries.begin(); i != entries.end(); ++i)
storePathSet.insert(canonPath(nixStore + "/" + *i));
for (Paths::iterator i = stateEntries.begin(); i != stateEntries.end(); ++i)
statePathSet.insert(canonPath(nixStoreState + "/" + *i));
} else {
for (PathSet::iterator i = pathsToDelete.begin();
i != pathsToDelete.end(); ++i)
{
assertStorePath(*i); //TODO ASSERTSTATEPATH, but for now we have no arg statePathsToDelete
assertStorePath(*i);
storePathSet.insert(*i);
}
}
/* Topologically sort them under the `referrers' relation. That
is, a < b if a is in referrers(b). This gives us the order in
is, a < b iff a is in referrers(b). This gives us the order in
which things can be deleted safely. */
/* !!! when we have multiple output paths per derivation, this
will not work anymore because we get cycles. */
Paths storePaths = topoSortPaths(storePathSet);
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
if(*i != "/nix/store/zg8x9wdhcs4j0hvf33vg34c7m65adrpa-env-manifest")
continue;
PathSet references;
store->queryStoreReferences(*i, references, 0);
for (PathSet::iterator j = references.begin(); j != references.end(); ++j) {
printMsg(lvlError, format("REF `%1%'") % *j);
}
}
/* Try to delete store paths in the topologically sorted order. */
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
@@ -679,7 +576,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
debug(format("temporary root `%1%'") % *i);
continue;
}
debug(format("dead path `%1%'") % *i);
result.insert(*i);
@@ -709,7 +606,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
if (!pathExists(*i)) continue;
printMsg(lvlInfo, format("deleting store path `%1%'") % *i);
printMsg(lvlInfo, format("deleting `%1%'") % *i);
/* Okay, it's safe to delete. */
try {
@@ -718,7 +615,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
bytesFreed += freed;
} catch (PathInUse & e) {
printMsg(lvlError, format("warning: %1%") % e.msg());
}
}
#ifndef __CYGWIN__
if (fdLock != -1)
@@ -727,87 +624,6 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
#endif
}
}
/* Try to delete state paths. */
/* Topologically sort them under the `referrers' relation. That
is, a < b if a is in referrers(b). This gives us the order in
which things can be deleted safely. */
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! when we have multiple output paths per derivation, this
will not work anymore because we get cycles. */
Paths statePaths;
for (PathSet::iterator i = statePathSet.begin(); i != statePathSet.end(); ++i)
statePaths.push_back(*i);
//
for (Paths::iterator i = statePaths.begin(); i != statePaths.end(); ++i) {
debug(format("considering deletion of state path `%1%'") % *i);
if (allLiveStatePaths.find(*i) != allLiveStatePaths.end()) {
debug(format("State path `%1%' is alive (possibyly shared with another live path)") % *i);
continue;
}
string lostAndFound = nixStoreState + "/lost+found";
if(*i == lostAndFound){
debug(format("Keeping `%1%' because its a special path") % lostAndFound);
continue;
}
debug(format("dead path `%1%'") % *i);
result.insert(*i);
/* If just returning the set of dead paths, we also return the
space that would be freed if we deleted them. */
if (action == gcReturnDead)
bytesFreed += computePathSize(*i);
if (action == gcDeleteDead || action == gcDeleteSpecific) {
//TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! state locks
/*
#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 (i->size() >= 5 && string(*i, i->size() - 5) == ".lock") {
fdLock = openLockFile(*i, false);
if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
debug(format("skipping active lock `%1%'") % *i);
continue;
}
}
#endif
*/
if (!pathExists(*i)) continue;
printMsg(lvlInfo, format("deleting state path `%1%'") % *i);
/* Okay, it's safe to delete. */
try {
unsigned long long freed;
deleteFromState(*i, freed);
bytesFreed += freed;
} catch (PathInUse & e) {
printMsg(lvlError, format("warning: %1%") % e.msg());
}
/*
#ifndef __CYGWIN__
if (fdLock != -1)
/* Write token to stale (deleted) lock file. * /
writeFull(fdLock, (const unsigned char *) "d", 1);
#endif
*/
}
}
}

View File

@@ -3,19 +3,16 @@
#include <map>
#include <algorithm>
#include <pwd.h>
namespace nix {
string nixStore = "/UNINIT";
string nixStoreState = "/UNINIT";
string nixDataDir = "/UNINIT";
string nixLogDir = "/UNINIT";
string nixStateDir = "/UNINIT";
string nixDBPath = "/UNINIT";
string nixExt3CowHeader = "/UNINIT";
string nixRsync = "/UNINIT";
string nixConfDir = "/UNINIT";
string nixLibexecDir = "/UNINIT";
string nixBinDir = "/UNINIT";
@@ -29,16 +26,10 @@ bool readOnlyMode = false;
string thisSystem = "unset";
unsigned int maxSilentTime = 0;
Paths substituters;
bool useBuildHook = true;
static bool settingsRead = false;
uid_t callingUID = 0; //A root user will not set this value, so the default uid is 0
bool singleThreaded = false; //TODO Gives an error: cannot start worker (environment already open) / waiting for process X: No child processes
bool sendOutput = true;
bool sleepForGDB = false;
static std::map<string, Strings> settings;
@@ -124,39 +115,5 @@ unsigned int queryIntSetting(const string & name, unsigned int def)
return n;
}
uid_t queryCallingUID()
{
/* A root user will not even bother calling the daemon, so there is no way to check
* If the uid is not yet set...
*/
return callingUID;
}
void setCallingUID(uid_t uid, bool reset)
{
if(callingUID != 0 && !reset)
throw Error(format("The UID of the caller aleady set! at this point"));
callingUID = uid;
}
string uidToUsername(uid_t uid)
{
passwd *pwd = getpwuid(uid);
char *pw_name = pwd->pw_name;
return (string)pw_name;
}
string queryCallingUsername()
{
uid_t uid = queryCallingUID();
return uidToUsername(uid);
}
string queryCurrentUsername()
{
return uidToUsername(geteuid());
}
}

View File

@@ -18,21 +18,12 @@ extern string nixDataDir; /* !!! fix */
/* nixLogDir is the directory where we log various operations. */
extern string nixLogDir;
/* nixStoreState is the directory where the state dirs of the components are stored. */
extern string nixStoreState;
/* nixStateDir is the directory where state is stored. */
extern string nixStateDir;
/* nixDBPath is the path name of our Berkeley DB environment. */
extern string nixDBPath;
/* nixExt3CowHeader is the header file used to communicate with ext3cow. */
extern string nixExt3CowHeader;
/* nixRsync is used to copy from one statedir to the other. */
extern string nixRsync;
/* nixConfDir is the directory where configuration files are
stored. */
extern string nixConfDir;
@@ -44,6 +35,7 @@ extern string nixLibexecDir;
/* nixBinDir is the directory where the main programs are stored. */
extern string nixBinDir;
/* Misc. global flags. */
/* Whether to keep temporary directories of failed builds. */
@@ -80,9 +72,6 @@ extern unsigned int maxSilentTime;
from a CD. */
extern Paths substituters;
/* Whether to use build hooks (for distributed builds). Sometimes
users want to disable this from the command-line. */
extern bool useBuildHook;
Strings querySetting(const string & name, const Strings & def);
@@ -92,27 +81,7 @@ bool queryBoolSetting(const string & name, bool def);
unsigned int queryIntSetting(const string & name, unsigned int def);
/* TODO PRIVATE: UID of the user that calls the nix-worker daemon */
extern uid_t callingUID;
/* get/set the UID of the user that calls the nix-worker daemon */
uid_t queryCallingUID();
void setCallingUID(uid_t uid, bool reset = false);
/* Convert a uid to a username: Watch it! this segfaults when given a wrong uid !! */
string uidToUsername(uid_t uid);
/* get the username based on the UID of the user that calls the nix-worker daemon */
string queryCallingUsername();
/* get the username based on the UID of the user currently runs the process */
string queryCurrentUsername();
/* Debugging variables */
extern bool singleThreaded;
extern bool sendOutput;
extern bool sleepForGDB;
}

File diff suppressed because it is too large Load Diff

View File

@@ -58,24 +58,12 @@ public:
/* Implementations of abstract store API methods. */
bool isValidPath(const Path & path);
bool isValidStatePath(const Path & path);
bool isValidComponentOrStatePath(const Path & path);
PathSet queryValidPaths();
Hash queryPathHash(const Path & path);
Path queryStatePathDrv(const Path & statePath);
void queryStoreReferences(const Path & path, PathSet & references, const unsigned int revision);
void queryStateReferences(const Path & storePath, PathSet & stateReferences, const unsigned int revision);
void queryStoreReferrers(const Path & path, PathSet & referrers, const unsigned int revision);
void queryStateReferrers(const Path & path, PathSet & stateReferrers, const unsigned int revision);
Hash queryPathHash(const Path & path);
void queryReferences(const Path & path, PathSet & references);
void queryReferrers(const Path & path, PathSet & referrers);
Path queryDeriver(const Path & path);
@@ -109,45 +97,6 @@ public:
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
/////////////////////////////
void setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals);
CommitIntervals getStatePathsInterval(const Path & statePath);
bool isStateComponent(const Path & storePath);
void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision);
void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision);
bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions);
Snapshots commitStatePath(const Path & statePath);
PathSet queryDerivers(const Path & storePath); //should these be in here ????
void scanAndUpdateAllReferences(const Path & statePath, const bool recursive);
bool getSharedWith(const Path & statePath1, Path & statePath2);
PathSet toNonSharedPathSet(const PathSet & statePaths);
void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive);
void shareState(const Path & from, const Path & to, const bool snapshot);
void unShareState(const Path & path, const bool branch, const bool restoreOld);
Path lookupStatePath(const Path & storePath, const string & identifier, const string & user);
void setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
void getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
/* Optimise the disk space usage of the Nix store by hard-linking
files with the same contents. */
@@ -168,9 +117,8 @@ void copyPath(const Path & src, const Path & dst);
of the file system contents of the path. The hash must be a
SHA-256 hash. */
void registerValidPath(const Transaction & txn,
const Path & component_or_state_path, const Hash & hash,
const PathSet & references, const PathSet & stateReferences,
const Path & deriver, const unsigned int revision);
const Path & path, const Hash & hash, const PathSet & references,
const Path & deriver);
typedef list<ValidPathInfo> ValidPathInfos;
@@ -190,27 +138,18 @@ void canonicalisePathMetaData(const Path & path);
/* Checks whether a path is valid. */
bool isValidPathTxn(const Transaction & txn, const Path & path);
/* Sets the set of outgoing FS (also state) references for a store path. Use with
care!
0 for revision means overwrite the last revision
*/
void setReferences(const Transaction & txn, const Path & store_or_statePath,
const PathSet & references, const PathSet & stateReferences, const unsigned int revision);
/* Sets the set of outgoing FS references for a store path. Use with
care! */
void setReferences(const Transaction & txn, const Path & path,
const PathSet & references);
/* Sets the deriver of a store path. Use with care! */
void setDeriver(const Transaction & txn, const Path & path,
const Path & deriver);
/* Query the derivers of a state-store path. */
PathSet queryDerivers(const Transaction & txn, const Path & storePath);
/* Delete a value from the nixStore directory. */
void deleteFromStore(const Path & path, unsigned long long & bytesFreed);
/* Delete a value from the nixState directory. */
void deleteFromState(const Path & _path, unsigned long long & bytesFreed);
MakeError(PathInUse, Error);
void verifyStore(bool checkContents);
@@ -230,67 +169,7 @@ void deletePathWrapped(const Path & path,
unsigned long long & bytesFreed);
void deletePathWrapped(const Path & path);
/* Adds a new deriver based on storePath to the dbDerivers table */
void addStateDeriver(const Transaction & txn, const Path & storePath, const Path & deriver);
bool isStateComponentTxn(const Transaction & txn, const Path & path);
bool isStateDrvPathTxn(const Transaction & txn, const Path & drvPath);
bool isStateDrv(const Derivation & drv);
//TODO CHECK IF THESE DONT BELONG HERE, REFACTOR CODE, EG MOVE FUNCTIONS AROUND
void queryAllValidPathsTxn(const Transaction & txn, PathSet & allComponentPaths, PathSet & allStatePaths);
bool isValidStatePathTxn(const Transaction & txn, const Path & path);
void queryXReferencesTxn(const Transaction & txn, const Path & path, PathSet & references, const bool component_or_state, const unsigned int revision, const unsigned int timestamp = 0);
void setStateComponentReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp);
void setStateStateReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp);
void queryStoreReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & referrers, const unsigned int revision);
void queryStateReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & stateReferrers, const unsigned int revision);
Path queryStatePathDrvTxn(const Transaction & txn, const Path & statePath);
void storePathRequisitesTxn(const Transaction & txn, const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision);
void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
bool isValidPathTxn(const Transaction & txn, const Path & path);
bool isValidStatePathTxn(const Transaction & txn, const Path & path);
void setSolidStateReferencesTxn(const Transaction & txn, const Path & statePath, const PathSet & paths);
bool querySolidStateReferencesTxn(const Transaction & txn, const Path & statePath, PathSet & paths);
void shareStateTxn(const Transaction & txn, const Path & from, const Path & to, const bool snapshot);
void unShareStateTxn(const Transaction & txn, const Path & path, const bool branch, const bool restoreOld);
PathSet toNonSharedPathSetTxn(const Transaction & txn, const PathSet & statePaths);
Path toNonSharedPathTxn(const Transaction & txn, const Path & statePath);
/*
* Returns a pathset with all paths (including itself) that eventually share the same statePath
*/
PathSet getSharedWithPathSetRecTxn(const Transaction & txn, const Path & statePath);
void ensurePathTxn(const Transaction & txn, const Path & path);
CommitIntervals getStatePathsIntervalTxn(const Transaction & txn, const Path & statePath);
void setStatePathsIntervalTxn(const Transaction & txn, const Path & statePath, const CommitIntervals & intervals);
bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision);
bool querySharedStateTxn(const Transaction & txn, const Path & statePath, Path & shared_with);
void setStateComponentTxn(const Transaction & txn, const Path & storePath, const Path & statePath, const string & identifier, const string & user);
void setVersionedStateEntriesTxn(const Transaction & txn, const Path & statePath, const StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
bool getVersionedStateEntriesTxn(const Transaction & txn, const Path & statePath, StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
void setStateOptionsTxn(const Transaction & txn, const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
void getStateOptionsTxn(const Transaction & txn, const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
}

View File

@@ -1,7 +1,6 @@
#include "misc.hh"
#include "store-api.hh"
#include "db.hh"
#include "local-store.hh"
#include <aterm2.h>
@@ -9,92 +8,31 @@
namespace nix {
Derivation derivationFromPathPrivate(const bool dotxn, const Transaction & txn, const Path & drvPath)
Derivation derivationFromPath(const Path & drvPath)
{
assertStorePath(drvPath);
if(dotxn)
ensurePathTxn(txn, drvPath);
else
store->ensurePath(drvPath);
store->ensurePath(drvPath);
ATerm t = ATreadFromNamedFile(drvPath.c_str());
if (!t)
throw Error(format("cannot read aterm from `%1%'") % drvPath);
if (!t) throw Error(format("cannot read aterm from `%1%'") % drvPath);
return parseDerivation(t);
}
//Wrappers
Derivation derivationFromPath(const Path & drvPath)
{
return derivationFromPathPrivate(false, noTxn, drvPath);
}
Derivation derivationFromPathTxn(const Transaction & txn, const Path & drvPath)
{
return derivationFromPathPrivate(true, txn, drvPath);
}
void computeFSClosure(const Path & path, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection)
void computeFSClosure(const Path & storePath,
PathSet & paths, bool flipDirection)
{
computeFSClosureTxn(noTxn, path, paths, withComponents, withState, revision, flipDirection);
}
/*
* Notice that the incoming 'paths' parameter can already be filled and only needs to be expanded
* Notice that we CANNOT change the order of the existing paths
*/
void computeFSClosureTxn(const Transaction & txn, const Path & path, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection)
{
PathSet allPaths;
computeFSClosureRecTxn(txn, path, allPaths, revision, flipDirection);
if(!withComponents && !withState)
throw Error(format("Useless call to computeFSClosure, at leat withComponents or withState must be true"));
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
if ( !isValidPathTxn(txn, *i) && !isValidStatePathTxn(txn, *i) )
throw Error(format("Not a state or store path: ") % *i);
//if withComponents, we add all component paths
if( withComponents ){
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
if ( isValidPathTxn(txn, *i) )
paths.insert(*i);
}
//if withState, we add all state paths
if( withState ){
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
if ( isValidStatePathTxn(txn, *i) )
paths.insert(*i);
}
}
void computeFSClosureRecTxn(const Transaction & txn, const Path & path, PathSet & paths, const int revision, const bool & flipDirection)
{
if (paths.find(path) != paths.end()) //takes care of double entries
return;
//printMsg(lvlError, format("ComputeFSClosureRec '%1%'") % path);
paths.insert(path);
if (paths.find(storePath) != paths.end()) return;
paths.insert(storePath);
PathSet references;
PathSet stateReferences;
if (flipDirection){
queryStoreReferrersTxn(txn, path, references, revision);
queryStateReferrersTxn(txn, path, stateReferences, revision);
}
else{
queryXReferencesTxn(txn, path, references, true, revision);
queryXReferencesTxn(txn, path, stateReferences, false, revision);
}
PathSet allReferences;
allReferences = pathSets_union(references, stateReferences);
if (flipDirection)
store->queryReferrers(storePath, references);
else
store->queryReferences(storePath, references);
for (PathSet::iterator i = allReferences.begin(); i != allReferences.end(); ++i)
computeFSClosureRecTxn(txn, *i, paths, revision, flipDirection);
for (PathSet::iterator i = references.begin();
i != references.end(); ++i)
computeFSClosure(*i, paths, flipDirection);
}
@@ -120,7 +58,7 @@ void queryMissing(const PathSet & targets,
if (isDerivation(p)) {
if (!store->isValidPath(p)) continue;
Derivation drv = derivationFromPathTxn(noTxn, p);
Derivation drv = derivationFromPath(p);
bool mustBuild = false;
for (DerivationOutputs::iterator i = drv.outputs.begin();

View File

@@ -2,7 +2,6 @@
#define __MISC_H
#include "derivations.hh"
#include "db.hh"
namespace nix {
@@ -10,9 +9,6 @@ namespace nix {
/* Read a derivation, after ensuring its existence through
ensurePath(). */
Derivation derivationFromPathTxn(const Transaction & txn, const Path & drvPath);
/* Same as above, but wihouth a txn now. This function can be called from the user side */
Derivation derivationFromPath(const Path & drvPath);
/* Place in `paths' the set of all store paths in the file system
@@ -21,18 +17,9 @@ Derivation derivationFromPath(const Path & drvPath);
`flipDirection' is true, the set of paths that can reach
`storePath' is returned; that is, the closures under the
`referrers' relation instead of the `references' relation is
withState = include the state paths into the closure
withComponents = include the component paths into the closure
We can currentky only compute the closure of the latsest revision!
returned. */
void computeFSClosure(const Path & storePath, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection = false);
void computeFSClosureTxn(const Transaction & txn, const Path & storePath, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection = false);
void computeFSClosureRecTxn(const Transaction & txn, const Path & path, PathSet & paths, const int revision, const bool & flipDirection); //TODO private
void computeFSClosure(const Path & storePath,
PathSet & paths, bool flipDirection = false);
/* Return the path corresponding to the output identifier `id' in the
given derivation. */

View File

@@ -1,7 +1,6 @@
#include "references.hh"
#include "hash.hh"
#include "util.hh"
#include "local-store.hh"
#include <cerrno>
#include <map>
@@ -118,36 +117,8 @@ void checkPath(const string & path,
else throw Error(format("unknown file type: %1%") % path);
}
PathSet scanForReferences(const string & path, const PathSet & paths)
{
return scanForReferencesTxn(noTxn, path, paths);
}
PathSet scanForReferencesTxn(const Transaction & txn, const Path & path, const PathSet & paths)
{
return scanForReferencesTxn_(txn, path, paths);
}
PathSet scanForStateReferences(const string & path, const PathSet & paths)
{
return scanForStateReferencesTxn(noTxn, path, paths);
}
PathSet scanForStateReferencesTxn(const Transaction & txn, const Path & path, const PathSet & paths)
{
//Get the references from the scan
PathSet org_references = scanForReferencesTxn_(txn, path, paths);
//Get the solid state dependencies, and also insert them
PathSet solidStateDeps;
querySolidStateReferencesTxn(txn, path, solidStateDeps);
org_references.insert(solidStateDeps.begin(), solidStateDeps.end());
return org_references;
}
PathSet scanForReferencesTxn_(const Transaction & txn, const Path & path, const PathSet & paths)
{
std::map<string, Path> backMap;
StringSet ids;
@@ -163,7 +134,6 @@ PathSet scanForReferencesTxn_(const Transaction & txn, const Path & path, const
throw Error(format("bad reference `%1%'") % *i);
string s = string(baseName, 0, pos);
assert(s.size() == refLength);
//printMsg(lvlError, format("BACKMAP[%1%] = '%2%'") % s % *i);
assert(backMap.find(s) == backMap.end());
// parseHash(htSHA256, s);
ids.insert(s);
@@ -182,5 +152,5 @@ PathSet scanForReferencesTxn_(const Transaction & txn, const Path & path, const
return found;
}
}

View File

@@ -2,21 +2,11 @@
#define __REFERENCES_H
#include "types.hh"
#include "db.hh"
namespace nix {
/* Scans for Component References (currently doesnt add solid dependencys) */
PathSet scanForReferences(const Path & path, const PathSet & refs);
PathSet scanForReferencesTxn(const Transaction & txn, const Path & path, const PathSet & refs);
/* Scans for State References and adds solid state dependencys*/
PathSet scanForStateReferences(const Path & path, const PathSet & refs);
PathSet scanForStateReferencesTxn(const Transaction & txn, const Path & path, const PathSet & refs);
/* The original scanForReferences */
PathSet scanForReferencesTxn_(const Transaction & txn, const Path & path, const PathSet & refs);
}
#endif /* !__REFERENCES_H */

View File

@@ -18,61 +18,27 @@
namespace nix {
Path readXPath(Source & from, const bool canBeStore, const bool canBeState)
{
Path path = readString(from);
if(canBeStore && canBeState)
assertStoreOrStatePath(path);
else if(canBeStore)
assertStorePath(path);
else if(canBeState)
assertStatePath(path);
return path;
}
Path readStorePath(Source & from)
{
return readXPath(from, true, false);
}
Path readStatePath(Source & from)
{
return readXPath(from, false, true);
}
Path readStoreOrStatePath(Source & from)
{
return readXPath(from, true, true);
Path path = readString(from);
assertStorePath(path);
return path;
}
PathSet readXPaths(Source & from, const bool canBeStore, const bool canBeState)
{
PathSet paths = readStringSet(from);
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i){
if(canBeStore && canBeState)
assertStoreOrStatePath(*i);
else if(canBeStore)
assertStorePath(*i);
else if(canBeState)
assertStatePath(*i);
}
return paths;
}
PathSet readStorePaths(Source & from)
{
return readXPaths(from, true, false);
PathSet paths = readStringSet(from);
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
assertStorePath(*i);
return paths;
}
PathSet readStatePaths(Source & from)
{
return readXPaths(from, false, true);
}
RemoteStore::RemoteStore()
{
string remoteMode = getEnv("NIX_REMOTE");
debug(format("Client remoteMode: '%1%'") % remoteMode);
if (remoteMode == "slave")
/* Fork off a setuid worker to do the privileged work. */
forkSlave();
@@ -93,7 +59,7 @@ RemoteStore::RemoteStore()
unsigned int magic = readInt(from);
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
daemonVersion = readInt(from);
unsigned int daemonVersion = readInt(from);
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
throw Error("Nix daemon protocol version not supported");
writeInt(PROTOCOL_VERSION, to);
@@ -203,8 +169,6 @@ void RemoteStore::setOptions()
writeInt(verbosity, to);
writeInt(maxBuildJobs, to);
writeInt(maxSilentTime, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
writeInt(useBuildHook, to);
processStderr();
}
@@ -218,29 +182,6 @@ bool RemoteStore::isValidPath(const Path & path)
return reply != 0;
}
bool RemoteStore::isValidStatePath(const Path & path)
{
writeInt(wopIsValidStatePath, to);
writeString(path, to);
processStderr();
unsigned int reply = readInt(from);
return reply != 0;
}
bool RemoteStore::isValidComponentOrStatePath(const Path & path)
{
writeInt(wopIsValidComponentOrStatePath, to);
writeString(path, to);
processStderr();
unsigned int reply = readInt(from);
return reply != 0;
}
PathSet RemoteStore::queryValidPaths()
{
throw Error("not implemented");
}
bool RemoteStore::hasSubstitutes(const Path & path)
{
@@ -261,68 +202,35 @@ Hash RemoteStore::queryPathHash(const Path & path)
return parseHash(htSHA256, hash);
}
Path RemoteStore::queryStatePathDrv(const Path & statePath)
{
writeInt(wopQueryStatePathDrv, to);
writeString(statePath, to);
processStderr();
return readString(from);
}
void RemoteStore::queryStoreReferences(const Path & path,
PathSet & references, const unsigned int revision)
void RemoteStore::queryReferences(const Path & path,
PathSet & references)
{
writeInt(wopQueryStoreReferences, to);
writeInt(wopQueryReferences, to);
writeString(path, to);
writeBigUnsignedInt(revision, to);
processStderr();
PathSet references2 = readStorePaths(from);
references.insert(references2.begin(), references2.end());
}
void RemoteStore::queryStateReferences(const Path & path,
PathSet & stateReferences, const unsigned int revision)
{
writeInt(wopQueryStateReferences, to);
writeString(path, to);
writeBigUnsignedInt(revision, to);
processStderr();
PathSet stateReferences2 = readStatePaths(from);
stateReferences.insert(stateReferences2.begin(), stateReferences2.end());
}
void RemoteStore::queryStoreReferrers(const Path & path,
PathSet & referrers, const unsigned int revision)
void RemoteStore::queryReferrers(const Path & path,
PathSet & referrers)
{
writeInt(wopQueryStoreReferrers, to);
writeInt(wopQueryReferrers, to);
writeString(path, to);
writeBigUnsignedInt(revision, to);
processStderr();
PathSet referrers2 = readStorePaths(from);
referrers.insert(referrers2.begin(), referrers2.end());
}
void RemoteStore::queryStateReferrers(const Path & path,
PathSet & stateReferrers, const unsigned int revision)
{
writeInt(wopQueryStateReferrers, to);
writeString(path, to);
writeBigUnsignedInt(revision, to);
processStderr();
PathSet stateReferrers2 = readStatePaths(from);
stateReferrers.insert(stateReferrers2.begin(), stateReferrers2.end());
}
Path RemoteStore::queryDeriver(const Path & path)
{
writeInt(wopQueryDeriver, to);
writeString(path, to);
processStderr();
Path drvPath = readString(from);
if (drvPath != "") assertStorePath(drvPath);
return drvPath;
return readStorePath(from);
}
@@ -355,6 +263,7 @@ Path RemoteStore::addTextToStore(const string & suffix, const string & s,
writeString(suffix, to);
writeString(s, to);
writeStringSet(references, to);
processStderr();
return readStorePath(from);
}
@@ -461,187 +370,6 @@ void RemoteStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
bytesFreed = (((unsigned long long) hi) << 32) | lo;
}
PathSet RemoteStore::queryDerivers(const Path & storePath)
{
writeInt(wopQueryDerivers, to);
writeString(storePath, to);
processStderr();
return readStorePaths(from);
}
void RemoteStore::setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals)
{
writeInt(wopSetStatePathsInterval, to);
writeString(statePath, to);
writeCommitIntervals(intervals, to);
processStderr();
readInt(from);
}
CommitIntervals RemoteStore::getStatePathsInterval(const Path & statePath)
{
writeInt(wopGetStatePathsInterval, to);
writeString(statePath, to);
processStderr();
return readCommitIntervals(from);
}
bool RemoteStore::isStateComponent(const Path & path)
{
writeInt(wopIsStateComponent, to);
writeString(path, to);
processStderr();
unsigned int reply = readInt(from);
return reply != 0;
}
void RemoteStore::storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision)
{
writeInt(wopStorePathRequisites, to);
writeString(storeOrstatePath, to);
writeInt(includeOutputs ? 1 : 0, to);
writeStringSet(paths, to);
writeInt(withComponents ? 1 : 0, to);
writeInt(withState ? 1 : 0, to);
writeBigUnsignedInt(revision, to);
processStderr();
readInt(from);
}
void RemoteStore::setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment)
{
writeInt(wopSetStateRevisions, to);
writeRevisionClosure(revisions, to);
writeString(rootStatePath, to);
writeString(comment, to);
processStderr();
readInt(from);
}
bool RemoteStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision)
{
writeInt(wopQueryStateRevisions, to);
writeString(statePath, to);
writeBigUnsignedInt(revision, to);
processStderr();
RevisionClosure revisions2 = readRevisionClosure(from);
RevisionClosureTS timestamps2 = readRevisionClosureTS(from);
revisions = revisions2;
timestamps = timestamps2; //TODO Is this ok?? Maybe COPY BY VALUE ??
unsigned int reply = readInt(from);
return reply != 0;
}
bool RemoteStore::queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions)
{
writeInt(wopQueryAvailableStateRevisions, to);
writeString(statePath, to);
processStderr();
RevisionInfos revisions2 = readRevisionInfos(from);
revisions = revisions2;
unsigned int reply = readInt(from);
return reply != 0;
}
Snapshots RemoteStore::commitStatePath(const Path & statePath)
{
writeInt(wopCommitStatePath, to);
writeString(statePath, to);
processStderr();
return readSnapshots(from);
}
void RemoteStore::scanAndUpdateAllReferences(const Path & statePath, const bool recursive)
{
writeInt(wopScanAndUpdateAllReferences, to);
writeString(statePath, to);
writeInt(recursive ? 1 : 0, to);
processStderr();
readInt(from);
}
bool RemoteStore::getSharedWith(const Path & statePath1, Path & statePath2)
{
writeInt(wopGetSharedWith, to);
writeString(statePath1, to);
processStderr();
statePath2 = readString(from);
unsigned int reply = readInt(from);
return reply != 0;
}
PathSet RemoteStore::toNonSharedPathSet(const PathSet & statePaths)
{
writeInt(wopToNonSharedPathSet, to);
writeStringSet(statePaths, to);
processStderr();
return readStatePaths(from);
}
void RemoteStore::revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive)
{
writeInt(wopRevertToRevision, to);
writeString(statePath, to);
writeBigUnsignedInt(revision_arg, to);
writeInt(recursive ? 1 : 0, to);
processStderr();
readInt(from);
}
void RemoteStore::shareState(const Path & from_arg, const Path & to_arg, const bool snapshot)
{
writeInt(wopShareState, to);
writeString(from_arg, to);
writeString(to_arg, to);
writeInt(snapshot ? 1 : 0, to);
processStderr();
readInt(from);
}
void RemoteStore::unShareState(const Path & path, const bool branch, const bool restoreOld)
{
writeInt(wopUnShareState, to);
writeString(path, to);
writeInt(branch ? 1 : 0, to);
writeInt(restoreOld ? 1 : 0, to);
processStderr();
readInt(from);
}
Path RemoteStore::lookupStatePath(const Path & storePath, const string & identifier, const string & user)
{
writeInt(wopLookupStatePath, to);
writeString(storePath, to);
writeString(identifier, to);
writeString(user, to);
processStderr();
return readStatePath(from);
}
void RemoteStore::setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs)
{
writeInt(wopSetStateOptions, to);
writeString(statePath, to);
writeString(user, to);
writeString(group, to);
writeInt(chmod, to);
writeString(runtimeArgs, to);
processStderr();
readInt(from);
}
void RemoteStore::getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs)
{
writeInt(wopGetStateOptions, to);
writeString(statePath, to);
processStderr();
user = readString(from);
group = readString(from);
chmod = readInt(from);
runtimeArgs = readString(from);
readInt(from);
}
void RemoteStore::processStderr(Sink * sink, Source * source)
{

View File

@@ -26,24 +26,12 @@ public:
/* Implementations of abstract store API methods. */
bool isValidPath(const Path & path);
bool isValidStatePath(const Path & path);
bool isValidComponentOrStatePath(const Path & path);
PathSet queryValidPaths();
Hash queryPathHash(const Path & path);
Path queryStatePathDrv(const Path & statePath);
void queryStoreReferences(const Path & path, PathSet & references, const unsigned int revision);
void queryReferences(const Path & path, PathSet & references);
void queryStateReferences(const Path & storePath, PathSet & stateReferences, const unsigned int revision);
void queryStoreReferrers(const Path & path, PathSet & referrers, const unsigned int revision);
void queryStateReferrers(const Path & path, PathSet & stateReferrers, const unsigned int revision);
void queryReferrers(const Path & path, PathSet & referrers);
Path queryDeriver(const Path & path);
@@ -77,49 +65,12 @@ public:
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
void setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals);
CommitIntervals getStatePathsInterval(const Path & statePath);
bool isStateComponent(const Path & path);
void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision);
void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision);
bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions);
Snapshots commitStatePath(const Path & statePath);
PathSet queryDerivers(const Path & storePath);
void scanAndUpdateAllReferences(const Path & statePath, const bool recursive);
bool getSharedWith(const Path & statePath1, Path & statePath2);
PathSet toNonSharedPathSet(const PathSet & statePaths);
void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive);
void shareState(const Path & from, const Path & to, const bool snapshot);
void unShareState(const Path & path, const bool branch, const bool restoreOld);
Path lookupStatePath(const Path & storePath, const string & identifier, const string & user);
void setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
void getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
private:
AutoCloseFD fdSocket;
FdSink to;
FdSource from;
Pid child;
unsigned int daemonVersion;
void processStderr(Sink * sink = 0, Source * source = 0);

View File

@@ -21,13 +21,6 @@ bool isInStore(const Path & path)
&& path[nixStore.size()] == '/';
}
bool isInStateStore(const Path & path)
{
return path[0] == '/'
&& string(path, 0, nixStoreState.size()) == nixStoreState
&& path.size() >= nixStoreState.size() + 2
&& path[nixStoreState.size()] == '/';
}
bool isStorePath(const Path & path)
{
@@ -35,36 +28,18 @@ bool isStorePath(const Path & path)
&& path.find('/', nixStore.size() + 1) == Path::npos;
}
bool isStatePath(const Path & path)
{
return isInStateStore(path)
&& path.find('/', nixStoreState.size() + 1) == Path::npos;
}
void assertStorePath(const Path & path)
{
if (!isStorePath(path))
throw Error(format("component path `%1%' is not in the Nix store") % path);
throw Error(format("path `%1%' is not in the Nix store") % path);
}
void assertStatePath(const Path & path)
{
if (!isStatePath(path))
throw Error(format("state path `%1%' is not in the Nix state-store") % path);
}
void assertStoreOrStatePath(const Path & path)
{
if (!isStorePath(path) && !isStatePath(path))
throw Error(format("path `%1%' is not a store or state path") % path);
}
Path toStorePath(const Path & path)
{
if (!isInStore(path))
throw Error(format("path `%1%' is not in the Nix store (2)") % path);
throw Error(format("path `%1%' is not in the Nix store") % path);
Path::size_type slash = path.find('/', nixStore.size() + 1);
if (slash == Path::npos)
return path;
@@ -72,45 +47,6 @@ Path toStorePath(const Path & path)
return Path(path, 0, slash);
}
Path toStoreOrStatePath(const Path & path)
{
bool isStorePath = isInStore(path);
bool isStateStore = isInStateStore(path);
if (!isStorePath && !isStateStore)
throw Error(format("path `%1%' is not in the Nix store or Nix state store") % path);
Path::size_type slash;
if(isStorePath)
slash = path.find('/', nixStore.size() + 1);
else
slash = path.find('/', nixStoreState.size() + 1);
if (slash == Path::npos)
return path;
else
return Path(path, 0, slash);
}
Path followLinksToStore(const Path & _path)
{
Path path = absPath(_path);
while (!isInStore(path)) {
if (!isLink(path)) break;
string target = readLink(path);
path = absPath(target, dirOf(path));
}
if (!isInStore(path))
throw Error(format("path `%1%' is not in the Nix store") % path);
return path;
}
Path followLinksToStorePath(const Path & path)
{
return toStorePath(followLinksToStore(path));
}
void checkStoreName(const string & name)
{
@@ -131,7 +67,8 @@ void checkStoreName(const string & name)
}
Path makeStorePath(const string & type, const Hash & hash, const string & suffix)
Path makeStorePath(const string & type,
const Hash & hash, const string & suffix)
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":sha256:" + printHash(hash) + ":"
@@ -140,50 +77,10 @@ Path makeStorePath(const string & type, const Hash & hash, const string & suffix
checkStoreName(suffix);
return nixStore + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20)) //TODO maybe also add a suffix_stateIdentifier when: there is state & no runtime state args & ... ?
+ printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + suffix;
}
Path makeStatePath(const string & componentHash, const string & suffix, const string & stateIdentifier, const string & username)
{
string suffix_stateIdentifier = stateIdentifier;
suffix_stateIdentifier = "-" + suffix_stateIdentifier;
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = ":sha256:" + componentHash + ":"
+ nixStoreState + ":" + suffix + ":" + stateIdentifier + ":" + username;
checkStoreName(suffix);
checkStoreName(stateIdentifier);
return nixStoreState + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + suffix + suffix_stateIdentifier;
}
void checkStatePath(const Derivation & drv)
{
Path drvPath = drv.stateOutputs.find("state")->second.statepath;
string componentHash = drv.stateOutputs.find("state")->second.componentHash;
string suffix = drv.env.find("name")->second;
string stateIdentifier = drv.stateOutputs.find("state")->second.stateIdentifier;
string drvUser = drv.stateOutputs.find("state")->second.username;
string callingUser = queryCallingUsername();
//Check name (TODO how about sharing of drvs between users?) (user is filled in on the fly)
if(drvUser != callingUser)
throw Error(format("The calling user does not match the user specified in the drv '%1%'") % drvPath);
Path calculatedPath = makeStatePath(componentHash, suffix, stateIdentifier, callingUser);
printMsg(lvlError, format("Checking statePath validity: %1% %2%") % drvPath % calculatedPath);
//Check Calculated path
if(drvPath != calculatedPath)
throw Error(format("The statepath from the Derivation does not match the recalculated statepath, are u trying to spoof the statepath?"));
}
Path makeFixedOutputPath(bool recursive,
string hashAlgo, Hash hash, string name)
@@ -230,86 +127,24 @@ Path computeStorePathForText(const string & suffix, const string & s,
type += ":";
type += *i;
}
return makeStorePath(type, hash, suffix);
}
/* Return a string accepted by decodeValidPathInfo() that
registers the specified paths as valid. Note: it's the
responsibility of the caller to provide a closure. */
string makeValidityRegistration(const PathSet & paths, // TODO !!!!!!!!!!!!!!!!!!!!!! makeValidityRegistration CHECK CALLS FOR STATE PASSINGS?
bool showDerivers, bool showHash)
{
string s = "";
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
s += *i + "\n";
if (showHash)
s += printHash(store->queryPathHash(*i)) + "\n";
Path deriver = showDerivers ? store->queryDeriver(*i) : "";
s += deriver + "\n";
PathSet references;
store->queryStoreReferences(*i, references, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WE MAY NEED STATE???
s += (format("%1%\n") % references.size()).str();
for (PathSet::iterator j = references.begin();
j != references.end(); ++j)
s += *j + "\n";
}
return s;
}
ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
ValidPathInfo decodeValidPathInfo(std::istream & str)
{
ValidPathInfo info;
getline(str, info.path);
if (str.eof()) { info.path = ""; return info; }
if (hashGiven) {
string s;
getline(str, s);
info.hash = parseHash(htSHA256, s);
}
getline(str, info.deriver);
string s; int n;
getline(str, s);
if (!string2Int(s, n))
throw Error("number expected");
if (!string2Int(s, n)) throw Error("number expected");
while (n--) {
getline(str, s);
info.references.insert(s);
}
if(store->isStateComponent(info.path)){
getline(str, s);
if (!string2Int(s, n))
throw Error("number expected");
while (n--) {
getline(str, s);
info.stateReferences.insert(s);
}
unsigned int u;
getline(str, s);
if (!string2UnsignedInt(s, u))
throw Error("number expected");
info.revision = u;
}
if (!str || str.eof())
throw Error("missing input");
if (!str || str.eof()) throw Error("missing input");
return info;
}

View File

@@ -8,8 +8,6 @@
#include "hash.hh"
#include "serialise.hh"
#include "derivations.hh"
#include "db.hh"
namespace nix {
@@ -36,40 +34,19 @@ public:
/* Checks whether a path is valid. */
virtual bool isValidPath(const Path & path) = 0;
/* Checks whether a state-path is valid. */
virtual bool isValidStatePath(const Path & path) = 0;
/* TODO */
virtual bool isValidComponentOrStatePath(const Path & path) = 0;
/* Query the set of valid paths. */
virtual PathSet queryValidPaths() = 0;
/* Queries the hash of a valid path. */
virtual Hash queryPathHash(const Path & path) = 0;
/* Queries the derivation Path of a valid state path. */
virtual Path queryStatePathDrv(const Path & statePath) = 0;
/* Queries the set of outgoing FS references for a store path.
The result is not cleared. */
virtual void queryStoreReferences(const Path & path,
PathSet & references, const unsigned int revision) = 0;
/* Queries the set of outgoing FS state-references for a store path.
The result is not cleared. */
virtual void queryStateReferences(const Path & path,
PathSet & stateReferences, const unsigned int revision) = 0;
virtual void queryReferences(const Path & path,
PathSet & references) = 0;
/* Queries the set of incoming FS references for a store path.
The result is not cleared. */
virtual void queryStoreReferrers(const Path & path,
PathSet & referrers, const unsigned int revision) = 0;
/* Queries the set of incoming FS state-references for a store path.
The result is not cleared. */
virtual void queryStateReferrers(const Path & path, PathSet & stateReferrers, const unsigned int revision) = 0;
virtual void queryReferrers(const Path & path,
PathSet & referrers) = 0;
/* Query the deriver of a store path. Return the empty string if
no deriver has been set. */
@@ -184,65 +161,6 @@ public:
`bytesFreed'. */
virtual void collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) = 0;
/* TODO */
virtual void setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals) = 0;
/* TODO */
virtual CommitIntervals getStatePathsInterval(const Path & statePath) = 0;
/* Checks whether a path is a component path that has a statePath. */
virtual bool isStateComponent(const Path & path) = 0;
/* TODO */
virtual void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision) = 0;
/* TODO */
virtual void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) = 0;
/*
* TODO Not sure wheter this comment is 100% correct
* Querys all available state revision closures.
* (e.g. a references scan can change this)
*/
virtual bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision) = 0;
/* TODO */
virtual bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions) = 0;
/* TODO */
virtual Snapshots commitStatePath(const Path & statePath) = 0;
/* TODO */
virtual PathSet queryDerivers(const Path & storePath) = 0;
/* TODO */
virtual void scanAndUpdateAllReferences(const Path & statePath, const bool recursive) = 0;
/* TODO */
virtual bool getSharedWith(const Path & statePath1, Path & statePath2) = 0;
/* TODO */
virtual PathSet toNonSharedPathSet(const PathSet & statePaths) = 0;
/* TODO */
virtual void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive) = 0;
/* TODO */
virtual void shareState(const Path & from, const Path & to, const bool snapshot) = 0;
/* TODO */
virtual void unShareState(const Path & path, const bool branch, const bool restoreOld) = 0;
/* TODO */
virtual Path lookupStatePath(const Path & storePath, const string & identifier, const string & user) = 0;
/* TODO */
virtual void setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs) = 0;
/* TODO */
virtual void getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs) = 0;
};
@@ -250,31 +168,15 @@ public:
/* Throw an exception if `path' is not directly in the Nix store. */
void assertStorePath(const Path & path);
void assertStatePath(const Path & path);
void assertStoreOrStatePath(const Path & path);
bool isInStore(const Path & path);
bool isStorePath(const Path & path);
bool isInStateStore(const Path & path);
bool isStatePath(const Path & path);
void checkStoreName(const string & name);
/* Chop off the parts after the top-level store name, e.g.,
/nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
Path toStorePath(const Path & path);
Path toStoreOrStatePath(const Path & path);
/* Follow symlinks until we end up with a path in the Nix store. */
Path followLinksToStore(const Path & path);
/* Same as followLinksToStore(), but apply toStorePath() to the
result. */
Path followLinksToStorePath(const Path & path);
/* Constructs a unique store path name. */
@@ -284,11 +186,6 @@ Path makeStorePath(const string & type,
Path makeFixedOutputPath(bool recursive,
string hashAlgo, Hash hash, string name);
/* TODO ... */
Path makeStatePath(const string & componentHash, const string & suffix, const string & stateIdentifier, const string & user);
/* TODO ... */
void checkStatePath(const Derivation & drv);
/* This is the preparatory part of addToStore() and addToStoreFixed();
it computes the store path to which srcPath is to be copied.
@@ -342,11 +239,6 @@ extern boost::shared_ptr<StoreAPI> store;
boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
string makeValidityRegistration(const PathSet & paths,
bool showDerivers, bool showHash);
/* OLD TODO REMOVE
struct ValidPathInfo
{
Path path;
@@ -354,19 +246,8 @@ struct ValidPathInfo
Hash hash;
PathSet references;
};
*/
struct ValidPathInfo
{
Path path;
Path deriver;
Hash hash;
PathSet references;
PathSet stateReferences;
int unsigned revision;
};
ValidPathInfo decodeValidPathInfo(std::istream & str,
bool hashGiven = false);
ValidPathInfo decodeValidPathInfo(std::istream & str);
}

View File

@@ -1,845 +0,0 @@
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fstream>
#include <iostream>
#include "store-state.hh"
#include "globals.hh"
#include "util.hh"
#include "derivations.hh"
#include "store-api.hh"
#include "local-store.hh"
#include "misc.hh"
#include "archive.hh"
#include "snapshot.hh"
#include "references.hh"
//for nixDB
namespace nix {
/*
* This gets called before a stateDir might be valid, so we get the data
* from the drv and not from the database
*/
void createSubStateDirsTxn(const Transaction & txn, const DerivationStateOutputDirs & stateOutputDirs, const DerivationStateOutputs & stateOutputs)
{
Path statePath = stateOutputs.find("state")->second.statepath;
string stateDir = statePath;
PathSet intervalPaths;
for (DerivationStateOutputDirs::const_reverse_iterator i = stateOutputDirs.rbegin(); i != stateOutputDirs.rend(); ++i){
DerivationStateOutputDir d = i->second;
string thisdir = d.path;
Path fullstatedir = stateDir + "/" + thisdir;
//If it is a file: continue
if(thisdir.substr(thisdir.length() -1 , thisdir.length()) != "/"){
continue;
}
ensureDirExists(fullstatedir);
if(d.type == "interval"){
intervalPaths.insert(fullstatedir);
}
}
setChown(statePath, queryCallingUsername(), "nixbld", true); //Set all dirs in the statePath recursively to their owners
printMsg(lvlTalkative, format("Set CHOWN '%1%'") % (statePath + "-" + queryCallingUsername()));
}
/*
* TODO
*/
void updatePaths()
{
//set in db
//update setStatePathsIntervalTxn(txn, intervalPaths, empty, true);
}
/*
* Input: store (or statePath?)
* Returns all the drv's of the statePaths (in)directly referenced.
*/
//TODO TXN
PathSet getAllStateDerivationsRecursivelyTxn(const Transaction & txn, const Path & storePath, const int revision)
{
//Get recursively all state paths
PathSet statePaths;
storePathRequisitesTxn(noTxn, storePath, false, statePaths, false, true, revision);
//Find the matching drv with the statePath
PathSet derivations;
for (PathSet::iterator i = statePaths.begin(); i != statePaths.end(); ++i)
derivations.insert(queryStatePathDrvTxn(txn,*i));
return derivations;
}
void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const int revision_arg, const bool recursive)
{
//Unshare the path
Path statePath_ns = toNonSharedPathTxn(txn, statePath);
//get a new timestamp for the references update
unsigned int newTimestamp = getTimeStamp();
//Get the revisions recursively to also roll them back
RevisionClosure getRivisions;
RevisionClosureTS getTimestamps;
queryStateRevisionsTxn(txn, statePath_ns, getRivisions, getTimestamps, revision_arg);
//If not recursive: filter out all paths execpt statePath_ns
if(!recursive)
{
Snapshots currentSS = getRivisions[statePath_ns]; //save SS of statePath_ns
getRivisions.clear(); //clear all
getRivisions[statePath_ns] = currentSS; //insert
unsigned int currentTS = getTimestamps[statePath_ns]; //same as above
getTimestamps.clear();
getTimestamps[statePath_ns] = currentTS;
}
//Revert each statePath in the list
for (RevisionClosure::iterator i = getRivisions.begin(); i != getRivisions.end(); ++i){
Path statePath = (*i).first;
Snapshots revisioned_paths = (*i).second;
unsigned int timestamp = getTimestamps[statePath];
for (Snapshots::iterator j = revisioned_paths.begin(); j != revisioned_paths.end(); ++j){
Path revertPathOrFile = (*j).first;
unsigned int epoch = (*j).second;
if(epoch == 0) //Path was deleted so were done
continue;
printMsg(lvlError, format("Reverting '%1%@%2%'") % revertPathOrFile % unsignedInt2String(epoch));
//Dir: Remove a slash at the end, we create /nix/state/...../cachedir@1234/
//File: Or create /nix/state/..../logfile.txt@1234
string revertPathOrFile_e;
if(revertPathOrFile.substr(revertPathOrFile.length() -1 , revertPathOrFile.length()) == "/")
revertPathOrFile_e = revertPathOrFile.substr(0 , revertPathOrFile.length() -1) + "@" + unsignedInt2String(epoch) + "/";
else
revertPathOrFile_e = revertPathOrFile + "@" + unsignedInt2String(epoch);
rsyncPaths(revertPathOrFile_e, revertPathOrFile, false);
}
//*** Now also revert state _references_ to the specific revision (the revision is already converted to a timestamp here)
//Query the references of the old revision (already converted to a timestamp)
PathSet state_references;
queryXReferencesTxn(txn, statePath, state_references, true, 0, timestamp);
PathSet state_stateReferences;
queryXReferencesTxn(txn, statePath, state_stateReferences, false, 0, timestamp);
//Now set these old references as the new references at the new (just created) Timestamp
setStateComponentReferencesTxn(txn, statePath, Strings(state_references.begin(), state_references.end()), 0, newTimestamp);
setStateStateReferencesTxn(txn, statePath, Strings(state_stateReferences.begin(), state_stateReferences.end()), 0, newTimestamp);
//Now set these old state-items as the new state-items at the new (just created) Timestamp
StateInfos infos;
getVersionedStateEntriesTxn(txn, statePath, infos, 0, timestamp);
setVersionedStateEntriesTxn(txn, statePath, infos, 0, newTimestamp);
//Now set these old staterights as the new staterights at the new (just created) Timestamp
//void getStateOptionsTxn(const Transaction & txn, const Path & statePath, string & user, string & group, int & chmod);
//void setStateOptionsTxn(const Transaction & txn, const Path & statePath, const string & user, const string & group, int chmod);
//TODO !!!!!!!!!??
if(revision_arg == 0)
printMsg(lvlError, format("Reverted state of '%1%' to the latest revision") % statePath); //TODO lookup the number
else
printMsg(lvlError, format("Reverted state of '%1%' to revision '%2%'") % statePath % revision_arg);
}
}
Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath)
{
if(!isValidStatePathTxn(txn, statePath))
throw Error(format("path `%1%' is not a valid state path") % statePath);
printMsg(lvlError, format("Snapshotting statePath: %1%") % statePath);
//Get all paths from the db
StateInfos infos;
getVersionedStateEntriesTxn(txn, statePath, infos);
//Get all interval counters
CommitIntervals intervals = getStatePathsIntervalTxn(txn, statePath);
Snapshots revisions_list;
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i){
string thisdir = (*i).path;
string type = (*i).type;
unsigned int interval = (*i).interval;
//printMsg(lvlError, format("maybe ssing %1% %2%") % thisdir % type);
if(type == "none"){
continue;
}
if(type == "interval"){
unsigned int interval_counter = intervals[thisdir];
//update the interval
intervals[thisdir] = (interval_counter + 1);
//We continue if we dont have to commit now
if(interval_counter % interval != 0)
continue;
}
else if(type == "full"){ }
else if(type == "manual"){ continue; } //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
else
throw Error(format("Type '%1%' is not handled in nix-state") % type);
/////////// We got here so we need to commit
//We get all info from the orignal path, but we snapshot on the shared path
Path sharedWith = toNonSharedPathTxn(txn, statePath);
string fullstatedir = sharedWith + "/" + thisdir;
if(thisdir == "/") //exception for the root dir
fullstatedir = sharedWith + "/";
unsigned int revision_number;
if(pathExists(fullstatedir) || FileExist(fullstatedir)){
revision_number = take_snapshot(fullstatedir);
printMsg(lvlError, format("Snapshotted '%1%@%2%'") % fullstatedir % revision_number);
}
else
revision_number = 0; //deleted, so we assign 0 to indicate that
revisions_list[fullstatedir] = revision_number;
//printMsg(lvlError, format("FSD %1% RN %2%") % fullstatedir % revision_number);
}
//Update the intervals again
setStatePathsIntervalTxn(txn, statePath, intervals);
return revisions_list;
}
//TODO include this call in the validate function
void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath
, PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences) //only for recursion
{
//Check if is a state Path
if(! isValidStatePathTxn(txn, statePath))
throw Error(format("This path '%1%' is not a state path") % statePath);
//printMsg(lvlError, format("scanAndUpdateAllReferencesTxn: '%1%' - %2%") % statePath % revision);
//Check if path is not a shared path
Path statePath_ns = toNonSharedPathTxn(txn, statePath);
//get all possible state and component references
PathSet allComponentPaths;
PathSet allStatePaths;
queryAllValidPathsTxn(txn, allComponentPaths, allStatePaths);
//Remove derivation paths
PathSet allComponentPaths2; //without derivations
for (PathSet::iterator i = allComponentPaths.begin(); i != allComponentPaths.end(); ++i){
string path = *i;
if(path.substr(path.length() - 4,path.length()) != drvExtension) //TODO HACK: we should have a typed table or a seperate table .... drvExtension == ".drv"
allComponentPaths2.insert(path);
}
//TODO maybe only scan in the changeset (patch) for new references? (this will be difficult and depending on the underlying versioning system)
//Scan in for (new) component and state references
PathSet state_references = scanForReferences(statePath_ns, allComponentPaths2);
PathSet state_stateReferences = scanForStateReferences(statePath_ns, allStatePaths);
//Retrieve old references
PathSet old_references;
PathSet old_state_references;
queryXReferencesTxn(txn, statePath_ns, old_references, true, 0); //get the latsest references
queryXReferencesTxn(txn, statePath_ns, old_state_references, false, 0);
//Check for added and removed paths
PathSet diff_references_removed;
PathSet diff_references_added;
pathSets_difference(state_references, old_references, diff_references_removed, diff_references_added);
PathSet diff_state_references_removed;
PathSet diff_state_references_added;
pathSets_difference(state_stateReferences, old_state_references, diff_state_references_removed, diff_state_references_added);
//Set PathSet's for the caller of this function
newFoundComponentReferences = diff_references_added;
newFoundStateReferences = diff_state_references_added;
//Print error, but we could also throw an error.
if(diff_references_added.size() != 0)
for (PathSet::iterator i = diff_references_added.begin(); i != diff_references_added.end(); ++i)
printMsg(lvlError, format("Added component reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
if(diff_references_removed.size() != 0)
for (PathSet::iterator i = diff_references_removed.begin(); i != diff_references_removed.end(); ++i)
printMsg(lvlError, format("Removed component reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
if(diff_state_references_added.size() != 0)
for (PathSet::iterator i = diff_state_references_added.begin(); i != diff_state_references_added.end(); ++i)
printMsg(lvlError, format("Added state reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
if(diff_state_references_removed.size() != 0)
for (PathSet::iterator i = diff_state_references_removed.begin(); i != diff_state_references_removed.end(); ++i)
printMsg(lvlError, format("Removed state reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
//We always set the referernces so we know they were scanned (maybe the same) at a certain time
printMsg(lvlError, format("Updating new references for statepath: '%1%'") % statePath_ns);
Path drvPath = queryStatePathDrvTxn(txn, statePath_ns);
registerValidPath(txn,
statePath_ns,
Hash(), //emtpy hash
state_references,
state_stateReferences,
drvPath,
0); //Set at a new timestamp
}
void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path & statePath)
{
if(! isValidStatePathTxn(txn, statePath))
throw Error(format("This path '%1%' is not a state path") % statePath);
//get all state current state references recursively
PathSet statePaths;
storePathRequisitesTxn(txn, statePath, false, statePaths, false, true, 0); //Get all current state dependencies
//Add own statePath (may already be in there, but its a set, so no doubles)
statePaths.insert(statePath);
//We dont need to sort since the db does that
//call scanForAllReferences again on all statePaths
for (PathSet::iterator i = statePaths.begin(); i != statePaths.end(); ++i){
//Scan, update, call recursively
PathSet newFoundComponentReferences;
PathSet newFoundStateReferences;
scanAndUpdateAllReferencesTxn(txn, *i, newFoundComponentReferences, newFoundStateReferences);
//Call the function recursively again on all newly found references //TODO test if this works
PathSet allNewReferences = pathSets_union(newFoundComponentReferences, newFoundStateReferences);
for (PathSet::iterator j = allNewReferences.begin(); j != allNewReferences.end(); ++j)
scanAndUpdateAllReferencesRecusivelyTxn(txn, *j);
}
}
void rsyncPaths(const Path & from, const Path & to, const bool addSlashes) //TODO bool shellexpansion, bool failure for nix-env
{
//TODO Could be a symlink (to a non-existing dir)
/*
if(!DirectoryExist(from))
throw Error(format("Path `%1%' doenst exist ...") % from);
if(!DirectoryExist(to))
throw Error(format("Path `%1%' doenst exist ...") % to);
*/
//TODO IF IS FILE: REMOVE THE FILE todo MOVE THIS INTO RSYNCPATHS???
//TODO IF IS DIR: REMOVE THE DIR
Path from2 = from;
Path to2 = to;
if(addSlashes){
//We add a slash / to the end to ensure the contents is copyed
if(from2[from2.length() - 1] != '/')
from2 = from2 + "/";
if(to2[to2.length() - 1] != '/')
to2 = to2 + "/";
}
printMsg(lvlError, format("Rsync from: '%1%' to: '%2%'") % from2 % to2);
//Rsync from --> to and also with '-avHx --delete'
//This makes the paths completely equal (also deletes) and retains times ownership etc.
Strings p_args;
p_args.push_back("-avHx");
p_args.push_back("--delete");
p_args.push_back(from2);
p_args.push_back(to2);
runProgram_AndPrintOutput(nixRsync, true, p_args, "rsync");
}
// ******************************************************* DB FUNCTIONS ********************************************************************
/* State specific db functions */
Path mergeToDBKey(const string & s1, const string & s2)
{
string prefix = "-KEY-";
return s1 + prefix + s2;
}
Path mergeToDBRevKey(const Path & statePath, const unsigned int intvalue)
{
return mergeToDBKey(statePath, unsignedInt2String(intvalue));
}
void splitDBKey(const string & s, string & s1, string & s2)
{
string prefix = "-KEY-";
size_t getPos = s.find_last_of(prefix);
if(getPos == string::npos)
throw Error(format("No prefx '%1%' found in '%2%' so we cannot split") % prefix % s);
int pos = getPos;
s1 = s.substr(0, pos - prefix.length() + 1);
s2 = s.substr(pos+1, s.length());
}
void splitDBRevKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue)
{
string s2;
splitDBKey(revisionedStatePath, statePath, s2);
bool succeed = string2UnsignedInt(s2, intvalue);
if(!succeed)
throw Error(format("Malformed revision value of path '%1%'") % revisionedStatePath);
}
unsigned int getNewRevisionNumber(Database & nixDB, const Transaction & txn, TableId table,
const Path & statePath)
{
//query
string data;
bool notEmpty = nixDB.queryString(txn, table, statePath, data);
if(!notEmpty){
nixDB.setString(txn, table, statePath, int2String(1));
return 1; //we begin counting from 1 since 0 is a special value representing the last revision
}
unsigned int revision;
bool succeed = string2UnsignedInt(data, revision);
if(!succeed)
throw Error(format("Malformed revision counter value of path '%1%'") % statePath);
revision++;
nixDB.setString(txn, table, statePath, unsignedInt2String(revision));
return revision;
}
bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan)
{
unsigned int highestRev = 0;
//Lookup which key we need
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) {
if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys
continue;
//printMsg(lvlError, format("'%1%' - '%2%'") % *i % statePath);
Path getStatePath;
unsigned int getRevision;
splitDBRevKey(*i, getStatePath, getRevision);
if(getRevision > highestRev){
if(lowerthan != 0){
if(getRevision <= lowerthan) //if we have an uppper limit, see to it that we downt go over it
highestRev = getRevision;
}
else
highestRev = getRevision;
}
}
if(highestRev == 0) //no records found
return false;
key = mergeToDBRevKey(statePath, highestRev); //final key that matches revision + statePath
return true;
}
bool revisionToTimeStamp(Database & nixDB, const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp)
{
string key = mergeToDBRevKey(statePath, revision);
Strings references;
bool notempty = nixDB.queryStrings(txn, revisions_table, key, references);
if(notempty){
Path empty;
splitDBRevKey(*(references.begin()), empty, timestamp); //extract the timestamp
//printMsg(lvlError, format("PRINT '%1%'") % timestamp);
return true;
}
else
return false;
}
void setStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp)
{
//printMsg(lvlError, format("setStateReferences '%1%' for '%2%'") % references_table % statePath);
//Find the timestamp if we need
unsigned int timestamp2 = timestamp;
if(revision == 0 && timestamp == 0)
timestamp2 = getTimeStamp();
else if(revision != 0 && timestamp == 0){
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
if(!found)
throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision);
}
//for (Strings::const_iterator i = references.begin(); i != references.end(); ++i)
// printMsg(lvlError, format("setStateReferences::: '%1%'") % *i);
//Warning if it already exists
Strings empty;
if( nixDB.queryStrings(txn, references_table, mergeToDBRevKey(statePath, timestamp2), empty) )
printMsg(lvlError, format("Warning: The timestamp '%1%' (now: '%5%') / revision '%4%' already exists for set-references of path '%2%' with db '%3%'")
% timestamp2 % statePath % references_table % revision % getTimeStamp());
//Create the key
string key = mergeToDBRevKey(statePath, timestamp2);
//Insert
nixDB.setStrings(txn, references_table, key, references, false); //The false makes sure also empty references are set
}
bool queryStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
const Path & statePath, Strings & references, const unsigned int revision, const unsigned int timestamp)
{
//printMsg(lvlError, format("queryStateReferences '%1%' with revision '%2%'") % references_table % revision);
//Convert revision to timestamp number useing the revisions_table
unsigned int timestamp2 = timestamp;
if(timestamp == 0 && revision != 0){
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false;
return false;
}
Strings keys;
nixDB.enumTable(txn, references_table, keys);
//Mabye we need the latest timestamp?
string key = "";
if(timestamp2 == 0){
bool foundsomething = lookupHighestRevivison(keys, statePath, key);
if(!foundsomething)
return false;
else
return nixDB.queryStrings(txn, references_table, key, references);
}
//If a specific key is given: check if this timestamp exists key in the table
key = mergeToDBRevKey(statePath, timestamp2);
bool found = false;
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) {
if(key == *i){
found = true;
key = mergeToDBRevKey(statePath, timestamp2);
}
}
//If it doesn't exist in the table then find the highest key lower than it
if(!found){
bool foundsomething = lookupHighestRevivison(keys, statePath, key, 0);
if(!foundsomething)
return false;
//printMsg(lvlError, format("Warning: References for timestamp '%1%' not was not found, so taking the highest rev-key possible for statePath '%2%'") % timestamp2 % statePath);
}
return nixDB.queryStrings(txn, references_table, key, references); //now that we have the key, we can query the references
}
void invalidateAllStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, const Path & statePath)
{
//Remove all references
Strings keys;
nixDB.enumTable(txn, references_table, keys);
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){
if((*i).substr(0, statePath.length()) == statePath)
nixDB.delPair(txn, references_table, *i);
}
}
void removeAllStatePathRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table,
TableId revisions_comments, TableId snapshots_table, TableId statecounters, const Path & statePath)
{
//Remove all revisions
nixDB.delPair(txn, revisions_table, statePath);
RevisionInfos revisions;
queryAvailableStateRevisions(nixDB, txn, revisions_table, revisions_comments, statePath, revisions);
for (RevisionInfos::iterator i = revisions.begin(); i != revisions.end(); ++i){
unsigned int rev = (*i).first;
nixDB.delPair(txn, revisions_table, mergeToDBRevKey(statePath, rev));
//Remove all comments
nixDB.delPair(txn, revisions_comments, mergeToDBRevKey(statePath, rev));
}
//Remove all snapshots
Strings keys;
nixDB.enumTable(txn, snapshots_table, keys);
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){
if((*i).substr(0, statePath.length()) == statePath)
nixDB.delPair(txn, snapshots_table, *i);
}
//Remove all state-counters
keys.clear();
nixDB.enumTable(txn, statecounters, keys);
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){
if((*i).substr(0, statePath.length()) == statePath)
nixDB.delPair(txn, statecounters, *i);
}
}
void setStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment)
{
if( !isStatePath(rootStatePath) ) //weak check on statePath
throw Error(format("StatePath '%1%' is not a statepath") % rootStatePath);
unsigned int timestamp = getTimeStamp();
//Insert all ss_epochs into snapshots_table with the current ts.
for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){
string key = mergeToDBRevKey((*i).first, timestamp);
Strings data;
//the map<> takes care of the sorting on the Path
for (Snapshots::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j)
data.push_back(mergeToDBRevKey((*j).first, (*j).second));
nixDB.setStrings(txn, snapshots_table, key, data);
}
//Insert for each statePath a new revision record linked to the ss_epochs
for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){
Path statePath = (*i).first;
unsigned int revision = getNewRevisionNumber(nixDB, txn, revisions_table, statePath); //get a new revision number
string key = mergeToDBRevKey(statePath, revision);
//get all its requisites
PathSet statePath_references;
storePathRequisitesTxn(txn, statePath, false, statePath_references, false, true, 0);
statePath_references.insert(statePath);
//save in db
Strings data;
for (PathSet::const_iterator j = statePath_references.begin(); j != statePath_references.end(); ++j)
data.push_back(mergeToDBRevKey(*j, timestamp));
nixDB.setStrings(txn, revisions_table, key, data, false); //The false makes sure also empty revisions are set
//save the date and comments
Strings metadata;
metadata.push_back(unsignedInt2String(timestamp));
//get all paths that point to the same state (using shareing) and check if one of them equals the rootStatePath
PathSet sharedWith = getSharedWithPathSetRecTxn(txn, statePath);
if(statePath == rootStatePath || sharedWith.find(rootStatePath) != sharedWith.end())
metadata.push_back(comment);
else
metadata.push_back("Part of the snashot closure for " + rootStatePath);
nixDB.setStrings(txn, revisions_comments, key, metadata);
}
}
bool queryStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId snapshots_table,
const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision)
{
string key;
if(root_revision == 0){
Strings keys;
nixDB.enumTable(txn, revisions_table, keys); //get all revisions
bool foundsomething = lookupHighestRevivison(keys, statePath, key);
if(!foundsomething)
return false;
}
else
key = mergeToDBRevKey(statePath, root_revision);
//Get references pointing to snapshots_table from revisions_table with root_revision
Strings statePaths;
bool notempty = nixDB.queryStrings(txn, revisions_table, key, statePaths);
if(!notempty)
throw Error(format("Root revision '%1%' not found of statePath '%2%'") % unsignedInt2String(root_revision) % statePath);
//For each statePath add the revisions
for (Strings::iterator i = statePaths.begin(); i != statePaths.end(); ++i){
Path getStatePath;
unsigned int getTimestamp;
splitDBRevKey(*i, getStatePath, getTimestamp);
Strings snapshots_s;
Snapshots snapshots;
nixDB.queryStrings(txn, snapshots_table, *i, snapshots_s);
int counter=0;
for (Strings::iterator j = snapshots_s.begin(); j != snapshots_s.end(); ++j){
Path snapshottedPath;
unsigned int revision;
splitDBRevKey(*j, snapshottedPath, revision);
snapshots[snapshottedPath] = revision;
counter++;
}
revisions[getStatePath] = snapshots;
timestamps[getStatePath] = getTimestamp;
}
return notempty;
}
//TODO include comments into revisions?
bool queryAvailableStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
const Path & statePath, RevisionInfos & revisions)
{
Strings keys;
nixDB.enumTable(txn, revisions_table, keys); //get all revisions
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) {
if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys
continue;
Path getStatePath;
unsigned int getRevision;
splitDBRevKey(*i, getStatePath, getRevision);
//save the date and comments
RevisionInfo rev;
Strings metadata;
nixDB.queryStrings(txn, revisions_comments, *i, metadata);
unsigned int ts;
bool succeed = string2UnsignedInt(*(metadata.begin()), ts);
if(!succeed)
throw Error(format("Malformed timestamp in the revisions table of path '%1%'") % *i);
rev.timestamp = ts;
metadata.pop_front();
rev.comment = *(metadata.begin());
revisions[getRevision] = rev;
}
if(revisions.empty())
return false;
else
return true;
}
void setVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
const Path & statePath, const StateInfos & infos, const unsigned int revision, const unsigned int timestamp)
{
if( !isValidStatePathTxn(txn, statePath) )
throw Error(format("path '%1%' is not a statepath") % statePath);
//TODO THIS IS THE SAME CODE AS IN SETSTATEREFERENCES !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//Find the timestamp if we need
unsigned int timestamp2 = timestamp;
if(revision == 0 && timestamp == 0)
timestamp2 = getTimeStamp();
else if(revision != 0 && timestamp == 0){
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
if(!found)
throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision);
}
Strings ss;
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i) {
StateInfo si = *i;
ss.push_back(mergeToDBKey(si.path, mergeToDBKey(si.type, unsignedInt2String(si.interval))));
}
nixDB.setStrings(txn, versionItems, statePath, ss);
}
bool getVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
const Path & statePath, StateInfos & infos, const unsigned int revision, const unsigned int timestamp)
{
if( !isValidStatePathTxn(txn, statePath) )
throw Error(format("path '%1%' is not a statepath") % statePath);
//TODO THIS IS THE SAME CODE AS IN QUERY STATEREFERRERS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
//Convert revision to timestamp number useing the revisions_table
unsigned int timestamp2 = timestamp;
if(timestamp == 0 && revision != 0){
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false;
return false;
}
Strings ss;
bool found = nixDB.queryStrings(txn, versionItems, statePath, ss);
if(!found)
return false;
for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i) {
StateInfo si;
string s1;
string interval;
splitDBKey(*i, s1, interval);
bool succeed = string2UnsignedInt(interval, si.interval);
if(!succeed)
throw Error(format("Malformed TS value of record '%1%'") % *i);
string path;
string type;
splitDBKey(s1, si.path, si.type);
infos.push_back(si);
}
return true;
}
void setStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath,
const string & user, const string & group, int chmod, const string & runtimeArgs)
{
string value = mergeToDBKey(user,mergeToDBKey(group, mergeToDBKey(int2String(chmod), runtimeArgs)));
nixDB.setString(txn, stateOptions, statePath, value);
}
void getStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath,
string & user, string & group, int & chmod, string & runtimeArgs)
{
string value;
bool notEmpty = nixDB.queryString(txn, stateOptions, statePath, value);
if(!notEmpty)
throw Error(format("No rights found for path '%1%'") % statePath);
string s1;
splitDBKey(value, s1, runtimeArgs);
string s2;
string chmod_s;
splitDBKey(s1, s2, chmod_s);
bool succeed = string2Int(chmod_s, chmod);
if(!succeed)
throw Error(format("Malformed chmod value of path '%1%'") % statePath);
splitDBKey(s2, user, group);
}
}

View File

@@ -1,102 +0,0 @@
#ifndef __STORESTATE_H
#define __STORESTATE_H
#include "derivations.hh"
#include "types.hh"
#include "db.hh"
namespace nix {
/* Create a state directory. */
void createSubStateDirsTxn(const Transaction & txn, const DerivationStateOutputDirs & stateOutputDirs, const DerivationStateOutputs & stateOutputs);
/* TODO */
Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath);
/* TODO */
//void updateRevisionsRecursivelyTxn(const Transaction & txn, const Path & statePath);
/* TODO */
//int readRevisionNumber(Path statePath);
void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath
, PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences);
void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path & statePath);
/* revision 0 == latest ????? */
void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const int revision_arg, const bool recursive);
/* Copy all files and folders recursively (also the hidden ones) from the dir from/... to the dir to/... and delete the rest in to/ (Rsync) */
void rsyncPaths(const Path & from, const Path & to, const bool addSlashes);
// **************************************** *******************************************
/* TODO */
bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan = 0);
/* TODO */
unsigned int getNewRevisionNumber(Database & nixDB, const Transaction & txn, TableId table, const Path & statePath);
/* TODO */
Path mergeToDBKey(const string & s1, const string & s2);
Path mergeToDBRevKey(const Path & statePath, const unsigned int intvalue);
/* TODO */
void splitDBKey(const string & s, string & s1, string & s2);
void splitDBRevKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue);
/* TODO */
bool revisionToTimeStamp(Database & nixDB, const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp);
/* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */
void setStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
const Path & statePath, const Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0);
/* Returns the references for a specific revision (and older until the next higher revision number in the table) */
bool queryStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
const Path & statePath, Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0);
/* Get the revision number of the statePath and the revision numbers of all state paths in the references closure */
void setStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
/* Returns all the revision numbers of the state references closure of the given state path */
bool queryStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId snapshots_table,
const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision = 0);
/* Returns all available revision numbers of the given state path */
bool queryAvailableStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
const Path & statePath, RevisionInfos & revisions);
/**/
void invalidateAllStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, const Path & statePath);
/**/
void removeAllStatePathRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table,
TableId revisions_comments, TableId snapshots_table, TableId statecounters, const Path & statePath);
/**/
void setVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
const Path & statePath, const StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
/**/
bool getVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
const Path & statePath, StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
/**/
void setStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
/**/
void getStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
}
#endif /* !__STORESTATE_H */

View File

@@ -8,54 +8,30 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION 0x102
#define PROTOCOL_VERSION 0x101
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
typedef enum {
wopQuit = 0, //0
wopIsValidPath, //1
wopHasSubstitutes = 3, //3
wopIsValidStatePath,
wopIsValidComponentOrStatePath,
wopQuit = 0,
wopIsValidPath,
wopHasSubstitutes = 3,
wopQueryPathHash,
wopQueryStatePathDrv,
wopQueryStoreReferences,
wopQueryStateReferences,
wopQueryStoreReferrers, //10
wopQueryStateReferrers,
wopAddToStore,
wopAddTextToStore, //13
wopBuildDerivations, //14
wopEnsurePath, //15
wopQueryReferences,
wopQueryReferrers,
wopAddToStore,
wopAddTextToStore,
wopBuildDerivations,
wopEnsurePath,
wopAddTempRoot,
wopAddIndirectRoot,
wopSyncWithGC,
wopFindRoots,
wopCollectGarbage, //20
wopExportPath,
wopCollectGarbage,
wopExportPath,
wopImportPath,
wopQueryDeriver,
wopQueryDerivers,
wopSetStatePathsInterval,
wopGetStatePathsInterval,
wopIsStateComponent,
wopStorePathRequisites,
wopSetStateRevisions,
wopQueryStateRevisions, //30
wopQueryAvailableStateRevisions,
wopCommitStatePath,
wopScanAndUpdateAllReferences,
wopGetSharedWith,
wopToNonSharedPathSet,
wopRevertToRevision,
wopShareState,
wopUnShareState,
wopLookupStatePath,
wopSetStateOptions,
wopGetStateOptions,
wopSetOptions, //40
wopSetOptions,
} WorkerOp;
@@ -75,11 +51,7 @@ typedef enum {
Path readStorePath(Source & from);
Path readStatePath(Source & from);
Path readStoreOrStatePath(Source & from);
PathSet readStorePaths(Source & from);
PathSet readStatePaths(Source & from);
}

View File

@@ -3,16 +3,14 @@ pkglib_LTLIBRARIES = libutil.la
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
archive.cc aterm.cc aterm-map.cc xml-writer.cc
libutil_la_LIBADD = ../boost/format/libformat.la
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
archive.hh aterm.hh aterm-map.hh xml-writer.hh types.hh
libutil_la_LIBADD = ../boost/format/libformat.la
if !HAVE_OPENSSL
libutil_la_SOURCES += \
md5.c md5.h sha1.c sha1.h sha256.c sha256.h md32_common.h
endif
AM_CXXFLAGS = -Wall \
-I$(srcdir)/.. ${aterm_include}
AM_CXXFLAGS = -Wall -I$(srcdir)/.. ${aterm_include}

View File

@@ -51,15 +51,3 @@ ATermList nix::toATermList(const StringSet & ss)
l = ATinsert(l, toATerm(*i));
return l;
}
ATermList nix::toATermList(const SetStringSet & sss)
{
ATermList l = ATempty;
for (SetStringSet::const_reverse_iterator i = sss.rbegin(); i != sss.rend(); ++i){
StringSet ss = *i;
for (StringSet::const_reverse_iterator j = ss.rbegin(); j != ss.rend(); ++j){
l = ATinsert(l, toATerm(*j));
}
}
return l;
}

View File

@@ -44,7 +44,6 @@ ATerm toATerm(const char * s);
ATerm toATerm(const string & s);
ATermList toATermList(const StringSet & ss);
ATermList toATermList(const SetStringSet & sss);
}

View File

@@ -38,11 +38,6 @@ void writeInt(unsigned int n, Sink & sink)
sink(buf, sizeof(buf));
}
void writeBigUnsignedInt(unsigned int n, Sink & sink)
{
writeString(unsignedInt2String(n), sink); //TODO better way?
}
void writeString(const string & s, Sink & sink)
{
@@ -60,61 +55,6 @@ void writeStringSet(const StringSet & ss, Sink & sink)
writeString(*i, sink);
}
void writeIntVector(const IntVector & iv, Sink & sink)
{
writeInt(iv.size(), sink);
for(unsigned int i=0;i < iv.size(); i++)
writeString(int2String(iv.at(i)), sink); //TODO !!!!!!!!!!!!!!!!!!! writeInts ???????????
}
void writeRevisionClosure(const RevisionClosure & rc, Sink & sink)
{
writeInt(rc.size(), sink);
for (RevisionClosure::const_iterator i = rc.begin(); i != rc.end(); ++i){
writeString((*i).first, sink);
writeSnapshots((*i).second, sink);
}
}
void writeSnapshots(const Snapshots & ss, Sink & sink)
{
writeInt(ss.size(), sink);
for (Snapshots::const_iterator i = ss.begin(); i != ss.end(); ++i){
writeString((*i).first, sink);
writeBigUnsignedInt((*i).second, sink);
}
}
void writeRevisionClosureTS(const RevisionClosureTS & rc, Sink & sink)
{
writeInt(rc.size(), sink);
for (RevisionClosureTS::const_iterator i = rc.begin(); i != rc.end(); ++i){
writeString((*i).first, sink);
writeBigUnsignedInt((*i).second, sink);
}
}
void writeRevisionInfos(const RevisionInfos & ri, Sink & sink)
{
writeInt(ri.size(), sink);
for (RevisionInfos::const_iterator i = ri.begin(); i != ri.end(); ++i){
writeBigUnsignedInt((*i).first, sink);
RevisionInfo rvi = (*i).second;
writeString(rvi.comment, sink);
writeBigUnsignedInt(rvi.timestamp, sink);
}
}
void writeCommitIntervals(const CommitIntervals ci, Sink & sink)
{
writeInt(ci.size(), sink);
for (CommitIntervals::const_iterator i = ci.begin(); i != ci.end(); ++i){
writeString((*i).first, sink);
writeBigUnsignedInt((*i).second, sink);
}
}
////////////////
void readPadding(unsigned int len, Source & source)
{
@@ -127,6 +67,7 @@ void readPadding(unsigned int len, Source & source)
}
}
unsigned int readInt(Source & source)
{
unsigned char buf[8];
@@ -140,15 +81,6 @@ unsigned int readInt(Source & source)
(buf[3] << 24);
}
unsigned int readBigUnsignedInt(Source & source)
{
string s = readString(source);
unsigned int i;
if(! string2UnsignedInt(s, i) )
throw Error(format("Serialize: readBigUnsignedInt cannot read int: '%1%'") % s);
return i;
}
string readString(Source & source)
{
@@ -170,82 +102,5 @@ StringSet readStringSet(Source & source)
return ss;
}
IntVector readIntVector(Source & source)
{
unsigned int count = readInt(source);
IntVector iv;
while (count--){
string s = readString(source); //TODO !!!!!!!!!!!!!!!!!!!! readInt ???????????????
int i;
if (!string2Int(s, i))
throw Error(format("`%1%' is corrupt in readIntVector") % s);
iv.push_back(i);
}
return iv;
}
RevisionClosure readRevisionClosure(Source & source)
{
unsigned int count = readInt(source);
RevisionClosure rc;
while (count--){
string path = readString(source);
Snapshots ss = readSnapshots(source);
rc[path] = ss;
}
return rc;
}
Snapshots readSnapshots(Source & source)
{
unsigned int count = readInt(source);
Snapshots ss;
while (count--){
string path = readString(source);
unsigned int ri = readBigUnsignedInt(source);
ss[path] = ri;
}
return ss;
}
RevisionClosureTS readRevisionClosureTS(Source & source)
{
unsigned int count = readInt(source);
RevisionClosureTS rc;
while (count--){
string path = readString(source);
unsigned int ri = readBigUnsignedInt(source);
rc[path] = ri;
}
return rc;
}
RevisionInfos readRevisionInfos(Source & source)
{
unsigned int count = readInt(source);
RevisionInfos ri;
while (count--){
unsigned int revision = readBigUnsignedInt(source);
RevisionInfo rvi;
rvi.comment = readString(source);
rvi.timestamp = readBigUnsignedInt(source);
ri[revision] = rvi;
}
return ri;
}
CommitIntervals readCommitIntervals(Source & source)
{
unsigned int count = readInt(source);
CommitIntervals ci;
while (count--){
string path = readString(source);
unsigned int ri = readBigUnsignedInt(source);
ci[path] = ri;
}
return ci;
}
}

View File

@@ -95,29 +95,14 @@ struct StringSource : Source
void writePadding(unsigned int len, Sink & sink);
void writeInt(unsigned int n, Sink & sink);
void writeBigUnsignedInt(unsigned int n, Sink & sink);
void writeString(const string & s, Sink & sink);
void writeStringSet(const StringSet & ss, Sink & sink);
void writeIntVector(const IntVector & iv, Sink & sink);
void writeRevisionClosure(const RevisionClosure & rc, Sink & sink);
void writeSnapshots(const Snapshots & ss, Sink & sink);
void writeRevisionClosureTS(const RevisionClosureTS & rc, Sink & sink);
void writeRevisionInfos(const RevisionInfos & ri, Sink & sink);
void writeCommitIntervals(const CommitIntervals ci, Sink & sink);
void readPadding(unsigned int len, Source & source);
unsigned int readInt(Source & source);
unsigned int readBigUnsignedInt(Source & source);
string readString(Source & source);
StringSet readStringSet(Source & source);
IntVector readIntVector(Source & source);
RevisionClosure readRevisionClosure(Source & source);
Snapshots readSnapshots(Source & source);
RevisionClosureTS readRevisionClosureTS(Source & source);
RevisionInfos readRevisionInfos(Source & source);
CommitIntervals readCommitIntervals(Source & source);
}

View File

@@ -4,7 +4,6 @@
#include <string>
#include <list>
#include <set>
#include <map>
#include <boost/format.hpp>
@@ -16,7 +15,6 @@ namespace nix {
using std::string;
using std::list;
using std::set;
using std::map;
using std::vector;
using boost::format;
@@ -53,39 +51,14 @@ public:
typedef list<string> Strings;
typedef list<Strings> StringsList;
typedef set<string> StringSet;
typedef set<StringSet> SetStringSet;
/* Paths are just strings. */
typedef string Path;
typedef list<Path> Paths;
typedef set<Path> PathSet;
//state types
typedef vector<int> IntVector; //the Strings (list) of StateReferences and this list are connected by position //TODO
typedef vector<unsigned int> UnsignedIntVector;
typedef map<Path, unsigned int> CommitIntervals;
struct RevisionInfo
{
string comment;
unsigned int timestamp;
};
typedef map<unsigned int, RevisionInfo> RevisionInfos;
typedef map<Path, unsigned int> Snapshots; // /nix/state/...../cache -> .... Automatically sorted on Path :)
typedef map<Path, Snapshots> RevisionClosure;
typedef map<Path, unsigned int> RevisionClosureTS; //Paht with a timestamp about when the revision was made.
typedef map<int, Strings> StateReferences;
struct StateInfo
{
string path;
string type;
unsigned int interval;
};
typedef list<StateInfo> StateInfos;
typedef enum {
lvlError,

View File

@@ -1,7 +1,5 @@
#include "config.h"
#include "util.hh"
#ifdef __CYGWIN__
#include <windows.h>
#endif
@@ -10,15 +8,15 @@
#include <cerrno>
#include <cstdio>
#include <sstream>
#include <fstream>
#include <iostream>
#include <cstring>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include "util.hh"
extern char * * environ;
@@ -107,7 +105,8 @@ Path canonPath(const Path & path, bool resolveSymlinks)
/* If s points to a symlink, resolve it and restart (since
the symlink target might contain new symlinks). */
if (resolveSymlinks && isLink(s)) {
if (++followCount >= maxFollow)
followCount++;
if (followCount >= maxFollow)
throw Error(format("infinite symlink recursion in path `%1%'") % path);
temp = absPath(readLink(s), dirOf(s))
+ string(i, end);
@@ -260,7 +259,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
struct stat st;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path);
throw SysError(format("getting attributes of path `%1%'") % path);
bytesFreed += st.st_size;
@@ -270,7 +269,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
/* Make the directory writable. */
if (!(st.st_mode & S_IWUSR)) {
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % path);
throw SysError(format("making `%1%' writable") % path);
}
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
@@ -319,19 +318,19 @@ void makePathReadOnly(const Path & path)
}
static Path tempName(const Path & tmpRoot, const Path & prefix)
static Path tempName(const Path & tmpRoot)
{
static int counter = 0;
Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
return (format("%1%/%2%-%3%-%4%") % tmpRoot2 % prefix % getpid() % counter++).str();
return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str();
}
Path createTempDir(const Path & tmpRoot, const Path & prefix)
Path createTempDir(const Path & tmpRoot)
{
while (1) {
checkInterrupt();
Path tmpDir = tempName(tmpRoot, prefix);
Path tmpDir = tempName(tmpRoot);
if (mkdir(tmpDir.c_str(), 0777) == 0) {
/* Explicitly set the group of the directory. This is to
work around around problems caused by BSD's group
@@ -350,16 +349,14 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix)
}
}
Paths createDirs(const Path & path)
void createDirs(const Path & path)
{
if (path == "/") return Paths();
Paths created = createDirs(dirOf(path));
if (!pathExists(path)) {
if (path == "/") return;
createDirs(dirOf(path));
if (!pathExists(path))
if (mkdir(path.c_str(), 0777) == -1)
throw SysError(format("creating directory `%1%'") % path);
created.push_back(path);
}
return created;
}
@@ -374,8 +371,7 @@ void writeStringToFile(const Path & path, const string & s)
LogType logType = ltPretty;
Verbosity verbosity = lvlInfo; //Default
//Verbosity verbosity = lvlVomit; //Debugging
Verbosity verbosity = lvlInfo;
static int nestingLevel = 0;
@@ -469,8 +465,7 @@ void readFull(int fd, unsigned char * buf, size_t count)
if (errno == EINTR) continue;
throw SysError("reading from file");
}
if (res == 0)
throw EndOfFile("unexpected end-of-file (in readFull so daemon communication)");
if (res == 0) throw EndOfFile("unexpected end-of-file");
count -= res;
buf += res;
}
@@ -514,25 +509,14 @@ string drainFD(int fd)
//////////////////////////////////////////////////////////////////////
AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
AutoDelete::AutoDelete(const string & p) : path(p)
{
del = true;
this->recursive = recursive;
}
AutoDelete::~AutoDelete()
{
try {
if (del)
if (recursive)
deletePath(path);
else {
if (remove(path.c_str()) == -1)
throw SysError(format("cannot unlink `%1%'") % path);
}
} catch (...) {
ignoreException();
}
if (del) deletePath(path);
}
void AutoDelete::cancel()
@@ -768,10 +752,10 @@ void killUser(uid_t uid)
if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
} catch (std::exception & e) {
std::cerr << format("killing processes beloging to uid `%1%': %1%")
% uid % e.what() << std::endl;
std::cerr << format("killing processes beloging to uid `%1%': %1%\n")
% uid % e.what();
quickExit(1);
}
quickExit(0);
@@ -790,6 +774,7 @@ void killUser(uid_t uid)
//////////////////////////////////////////////////////////////////////
string runProgram(Path program, bool searchPath, const Strings & args)
{
checkInterrupt();
@@ -826,7 +811,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
throw SysError(format("executing `%1%'") % program);
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl; //TODO does not give the full error message
std::cerr << "error: " << e.what() << std::endl;
}
quickExit(1);
}
@@ -941,34 +926,6 @@ Strings unpackStrings(const string & s)
return strings;
}
/*
string packRevisionNumbers(const UnsignedIntVector & revs)
{
string seperator = "|";
string d = "";
for (UnsignedIntVector::const_iterator i = revs.begin(); i != revs.end(); ++i){
d += unsignedInt2String(*i) + seperator;
}
return d;
}
UnsignedIntVector unpackRevisionNumbers(const string & packed)
{
string seperator = "|";
Strings ss = tokenizeString(packed, seperator);
UnsignedIntVector revs;
for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i){
int rev;
bool succeed = string2UnsignedInt(*i, rev);
if(!succeed)
throw Error(format("Corrupted revisions db entry: `%1%'") % packed);
revs.push_back(rev);
}
return revs;
}
*/
Strings tokenizeString(const string & s, const string & separators)
{
@@ -984,62 +941,14 @@ Strings tokenizeString(const string & s, const string & separators)
return result;
}
//Splits on a space
//Uses quotes: "...." to group multiple spaced arguments into one
//Removes the grouping quotes at the call
//TODO bug a call with args: a b "c" d will fail
//TODO bug a call with args: a bc"d " e will fail
Strings tokenizeStringWithQuotes(const string & s)
{
string separator = " ";
Strings result;
string::size_type pos = s.find_first_not_of(separator, 0);
string quote = "\"";
string inquotes = "";
while (pos != string::npos) {
string::size_type end = s.find_first_of(separator, pos + 1);
if (end == string::npos) end = s.size();
string token(s, pos, end - pos);
pos = s.find_first_not_of(separator, end);
//if first or last item is a quote, we switch inquotes
if(token.substr(0,1) == quote || token.substr(token.size()-1,token.size()) == quote){
if(inquotes == ""){ //begin
inquotes = token;
continue;
}
else{ //end
token = inquotes + token;
token = token.substr(1,token.size()-2); //remove the grouping quotes
inquotes = "";
}
}
if(inquotes != "") //middle
inquotes += separator + token;
else
result.push_back(token); //normal
}
return result;
}
string statusToString(int status)
{
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (WIFEXITED(status))
return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
else if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
#if HAVE_STRSIGNAL
const char * description = strsignal(sig);
return (format("failed due to signal %1% (%2%)") % sig % description).str();
#else
return (format("failed due to signal %1%") % sig).str();
#endif
}
else if (WIFSIGNALED(status))
return (format("failed due to signal %1%") % WTERMSIG(status)).str();
else
return "died abnormally";
} else return "succeeded";
@@ -1067,12 +976,6 @@ bool string2Int(const string & s, int & n)
return str && str.get() == EOF;
}
string unsignedInt2String(unsigned int n)
{
std::ostringstream str;
str << n;
return str.str();
}
void ignoreException()
{
@@ -1083,283 +986,5 @@ void ignoreException()
}
}
bool string2UnsignedInt(const string & s, unsigned int & n)
{
std::istringstream str(s);
str >> n;
return str && str.get() == EOF;
}
string bool2string(const bool b)
{
if(b == true)
return "true";
else
return "false";
}
bool string2bool(const string & s)
{
if(s == "true")
return true;
else if(s == "false")
return false;
else{
throw Error(format("cannot convert string: `%1%' to bool") % s);
quickExit(1);
return false;
}
}
string triml(const string & s) {
string news = s;
int pos(0);
for ( ; news[pos]==' ' || news[pos]=='\t'; ++pos );
news.erase(0, pos);
return news;
}
string trimr(const string & s) {
string news = s;
int pos(news.size());
for ( ; pos && news[pos-1]==' ' || news[pos]=='\t'; --pos );
news.erase(pos, news.size()-pos);
return news;
}
string trim(const string & s) {
return triml(trimr(s));
}
//executes a shell command, captures and prints the output.
void runProgram_AndPrintOutput(Path program, bool searchPath, const Strings & args, const string outputPrefix)
{
string program_output = runProgram(program, searchPath, args);
//Add the prefix on every line
Strings lines = tokenizeString(program_output, "\n");
for (Strings::const_iterator i = lines.begin(); i != lines.end(); ++i){
if(trim(*i) != "")
printMsg(lvlError, format("[%2%]: %1%") % *i % outputPrefix);
}
}
//executes a direct shell command (faster)
void executeShellCommand(const string & command)
{
int kidstatus, deadpid;
pid_t kidpid = fork();
switch (kidpid) {
case -1:
throw SysError("unable to fork");
case 0:
try { // child
int rv = system(command.c_str());
//int rv = execlp(svnbin.c_str(), svnbin.c_str(), ">", tempoutput.c_str(), NULL); //TODO make this work ... ?
if (rv == -1) {
throw SysError("executeShellCommand(..) error");
quickExit(99);
}
quickExit(0);
} catch (std::exception & e) {
std::cerr << format("executeShellCommand(..) child error: %1%\n") % e.what();
quickExit(1);
}
}
deadpid = waitpid(kidpid, &kidstatus, 0);
if (deadpid == -1) {
std::cerr << format("state child waitpid error\n");
quickExit(1);
}
}
unsigned int getTimeStamp()
{
const time_t now = time(0);
unsigned int i = now;
return i;
}
//TODO Does this work on windows?
bool FileExist(const string FileName)
{
const char* FileName_C = FileName.c_str();
struct stat my_stat;
if (stat(FileName_C, &my_stat) != 0) return false;
return ((my_stat.st_mode & S_IFREG) != 0);
/*
S_IFMT 0170000 bitmask for the file type bitfields
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 fifo
*/
}
//TODO Does this work on windows?
bool DirectoryExist(const string FileName)
{
const char* FileName_C = FileName.c_str();
struct stat my_stat;
if (stat(FileName_C, &my_stat) != 0) return false;
return ((my_stat.st_mode & S_IFDIR) != 0);
}
//TODO Does this work on windows?
bool IsSymlink(const string FileName)
{
const char* FileName_C = FileName.c_str();
struct stat my_stat;
if (lstat(FileName_C, &my_stat) != 0) return false;
return (S_ISLNK(my_stat.st_mode) != 0);
}
/* adds the second PathSet after the first, but removing doubles from the second (union)
* (We assume the first PathSet has no duplicates)
* UNTESTED !!!!!!!!!!!!!!
*/
void pathSets_union_ordered(PathSet & originalPaths, const PathSet & newPaths)
{
for (PathSet::iterator i = newPaths.begin(); i != newPaths.end(); ++i){
if (originalPaths.find(*i) != originalPaths.end())
continue;
originalPaths.insert(*i);
}
}
//merges two PathSets into one, removing doubles (union)
PathSet pathSets_union(const PathSet & paths1, const PathSet & paths2)
{
vector<Path> vector1(paths1.begin(), paths1.end());
vector<Path> vector2(paths2.begin(), paths2.end());
vector<Path> setResult;
set_union(vector1.begin(), vector1.end(),vector2.begin(), vector2.end(), back_inserter(setResult)); //Also available: set_symmetric_difference and set_intersection
PathSet unionPaths;
for(unsigned int i=0; i<setResult.size(); i++)
unionPaths.insert(setResult[i]);
return unionPaths;
}
void pathSets_difference(const PathSet & oldpaths, const PathSet & newpaths, PathSet & addedpaths, PathSet & removedpaths)
{
for (PathSet::iterator i = oldpaths.begin(); i != oldpaths.end(); ++i){
bool exists = false;
for (PathSet::iterator j = newpaths.begin(); j != newpaths.end(); ++j){
if(*i == *j){
exists = true;
break;
}
}
if(!exists)
removedpaths.insert(*i);
}
for (PathSet::iterator i = newpaths.begin(); i != newpaths.end(); ++i){
bool exists = false;
for (PathSet::iterator j = oldpaths.begin(); j != oldpaths.end(); ++j){
if(*i == *j){
exists = true;
break;
}
}
if(!exists)
addedpaths.insert(*i);
}
}
void ensureDirExists(const Path & path)
{
Strings p_args;
p_args.push_back("-p");
p_args.push_back(path);
runProgram_AndPrintOutput("mkdir", true, p_args, "mkdir"); //TODO ensurePath
}
void setChown(const Path & pathOrFile, const string & user, const string & group, bool recursive)
{
Strings p_args;
if(recursive)
p_args.push_back("-R");
p_args.push_back(user + "." + group);
p_args.push_back(pathOrFile);
runProgram_AndPrintOutput("chown", true, p_args, "chown");
}
void setChmod(const Path & pathOrFile, const string & chmod)
{
Strings p_args;
p_args.push_back(chmod);
p_args.push_back(pathOrFile);
runProgram_AndPrintOutput("chmod", true, p_args, "chmod");
}
string padd(const string & s, char c , unsigned int size, bool front)
{
string ss = s;
while (ss.length() < size){
if(front)
ss = c + ss;
else
ss = ss + c;
}
return ss;
}
void symlinkPath(const Path & existingDir, const Path & newLinkName) //TODO bool shellexpansion
{
/*
* Symlink link to the share path
* Usage: ln [OPTION]... [-T] TARGET LINK_NAME (1st form)
*
* we call the shell (/bin/sh -c) so it expands the ~ to a users home dir
*
* We do -snf for:
* -s : symlinking
* -f : To remove existing destination files (this does NOT always overwrite the newLinkName !!!!)
* -n : Treat destination that is a symlink to a directory as if it were a normal file:
* When the destination is an actual directory (not a symlink to one), there is no ambiguity.
* The link is created in that directory. But when the specified destination is a symlink to a directory,
* there are two ways to treat the user's request. ln can treat the destination just as it would a normal
* directory and create the link in it. On the other hand, the destination can be viewed as a non-directory
* - as the symlink itself. In that case, ln must delete or backup that symlink before creating the new link.
* The default is to treat a destination that is a symlink to a directory just like a directory.
*
* ((This makes sure that newLinkName is really overwritten)
*/
Strings p_args;
p_args.push_back("-c");
p_args.push_back("ln -snf " + existingDir + " " + newLinkName);
runProgram_AndPrintOutput("/bin/sh", true, p_args, "sh-ln");
printMsg(lvlError, format("ln -sf %1% %2%") % existingDir % newLinkName);
}
void removeSymlink(const string & path)
{
if(path[path.length() - 1] == '/')
throw Error(format("We dont want to remove the symlink, not the enitre directory, but a / is given for `%1%'") % path);
deletePath(path);
}
void ensureStateDir(const Path & statePath, const string & user, const string & group, const string & chmod)
{
ensureDirExists(statePath);
setChown(statePath, user, group);
setChmod(statePath, chmod);
}
}

View File

@@ -36,18 +36,9 @@ Path dirOf(const Path & path);
following the final `/'. */
string baseNameOf(const Path & path);
/* Return true if the given path (dir of file) exists. */
/* Return true iff the given path exists. */
bool pathExists(const Path & path);
/* Return true if the given file exists. */
bool FileExist(const string FileName);
/* Return true if the given filename is a dir. */
bool DirectoryExist(const string FileName);
/* Return true if the given filename is a symlink. */
bool IsSymlink(const string FileName);
/* Read the contents (target) of a symbolic link. The result is not
in any way canonicalised. */
Path readLink(const Path & path);
@@ -79,11 +70,10 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
void makePathReadOnly(const Path & path);
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix");
Path createTempDir(const Path & tmpRoot = "");
/* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */
Paths createDirs(const Path & path);
/* Create a directory and all its parents, if necessary. */
void createDirs(const Path & path);
/* Create a file and write the given text to it. The file is written
in binary mode (i.e., no end-of-line conversions). The path should
@@ -176,9 +166,8 @@ class AutoDelete
{
Path path;
bool del;
bool recursive;
public:
AutoDelete(const Path & p, bool recursive = true);
AutoDelete(const Path & p);
~AutoDelete();
void cancel();
};
@@ -274,11 +263,10 @@ MakeError(Interrupted, BaseError)
string packStrings(const Strings & strings);
Strings unpackStrings(const string & s);
/* String tokenizer. */
Strings tokenizeString(const string & s, const string & separators = " \t\n\r");
/* String tokenizer for commandline agruments. */
//Strings tokenizeStringWithQuotes(const string & s); //TODO maybe remove the function
/* Convert the exit status of a child as returned by wait() into an
error string. */
@@ -291,60 +279,13 @@ bool statusOk(int status);
string int2String(int n);
bool string2Int(const string & s, int & n);
/* */
bool string2UnsignedInt(const string & s, unsigned int & n);
string unsignedInt2String(unsigned int n);
/* Parse a bool to a string and back */
string bool2string(const bool b);
bool string2bool(const string & s);
//return modified string s with spaces trimmed from left
string triml(const string & s);
//return modified string s with spaces trimmed from right
string trimr(const string & s);
//return modified string s with spaces trimmed from edges
string trim(const string & s);
//excecute a shell command
void executeShellCommand(const string & command);
//
void runProgram_AndPrintOutput(Path program, bool searchPath, const Strings & args, const string outputPrefix);
unsigned int getTimeStamp();
//string getCallingUserName();
/* Merges two PathSets into one, removing doubles (union) */
PathSet pathSets_union(const PathSet & paths1, const PathSet & paths2);
/* TODO UNTESTED !!!!!!!!!!!!!! */
void pathSets_union_ordered(PathSet & originalPaths, const PathSet & newPaths);
/* TODO */
void pathSets_difference(const PathSet & oldpaths, const PathSet & newpaths, PathSet & addedpaths, PathSet & removedpaths);
/* TODO */
void ensureDirExists(const Path & path);
/* TODO */
void setChown(const Path & pathOrFile, const string & user, const string & group, bool recursive = false);
void setChmod(const Path & pathOrFile, const string & chmod);
string padd(const string & s, char c , unsigned int size, bool front = false);
/* Symlinks one path to the other */
void symlinkPath(const Path & existingDir, const Path & newLinkName);
void removeSymlink(const string & path);
void ensureStateDir(const Path & statePath, const string & user, const string & group, const string & chmod);
/* Exception handling in destructors: print an error message, then
ignore the exception. */
void ignoreException();
}
#endif /* !__UTIL_H */

View File

@@ -9,7 +9,6 @@ Operations:
--set: create a user environment containing a single derivation
--uninstall / -e: remove derivations from the user environment
--query / -q: perform a query on an environment or Nix expression
--set-flag NAME VALUE: set derivation meta-attribute to given value
The previous operations take a list of derivation names. The special
name `*' may be used to indicate all derivations.
@@ -21,6 +20,8 @@ name `*' may be used to indicate all derivations.
--delete-generations GENERATIONS...: deleted listed generations,
`old' for all non-current generations
--import / -I FILE: set default Nix expression
--version: output version information
--help: display help
@@ -54,7 +55,7 @@ Query flags:
--xml: show output in XML format
--status / -s: print installed/present status
--no-name: hide derivation names
--attr-path / -P: shows the unambiguous attribute name of the
--attr / -A: shows the unambiguous attribute name of the
derivation which can be used when installing with -A
--system: print the platform type of the derivation
--compare-versions / -c: compare version to available or installed
@@ -62,6 +63,8 @@ Query flags:
--out-path: print path of derivation output
--description: print description
--meta: print all meta attributes (only with --xml)
--prebuilt-only: only show derivations whose prebuilt binaries are
available on this machine or are downloadable
Options:
@@ -71,5 +74,3 @@ Options:
--keep-failed / -K: keep temporary directories of failed builds
--preserve-installed: do not replace currently installed versions in `-i'
--system-filter SYSTEM: only use derivations for specified platform
--prebuilt-only / -b: only use derivations whose prebuilt binaries are
available on this machine or are downloadable

View File

@@ -15,8 +15,6 @@
#include "store-api.hh"
#include "db.hh"
#include "util.hh"
#include "local-store.hh"
#include "store-state.hh"
#include <cerrno>
#include <ctime>
@@ -49,9 +47,8 @@ struct InstallSourceInfo
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
Path profile; /* for srcProfile */
string systemFilter; /* for srcNixExprDrvs */
bool prebuiltOnly;
ATermMap autoArgs;
InstallSourceInfo() : prebuiltOnly(false) { };
InstallSourceInfo() : autoArgs() { };
};
@@ -97,8 +94,6 @@ static bool parseInstallSourceOptions(Globals & globals,
}
else if (arg == "--attr" || arg == "-A")
globals.instSource.type = srcAttrPath;
else if (arg == "--prebuilt-only" || arg == "-b")
globals.instSource.prebuiltOnly = true;
else return false;
return true;
}
@@ -228,7 +223,8 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
i != elems.end(); ++i)
/* Call to `isDerivation' is for compatibility with Nix <= 0.7
user environments. */
if (i->queryDrvPath(state) != "" && isDerivation(i->queryDrvPath(state)))
if (i->queryDrvPath(state) != "" &&
isDerivation(i->queryDrvPath(state)))
drvsToBuild.insert(i->queryDrvPath(state));
debug(format("building user environment dependencies"));
@@ -242,7 +238,6 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
PathSet references;
ATermList manifest = ATempty;
ATermList inputs = ATempty;
ATermList stateIdentifiers = ATempty;
for (DrvInfos::const_iterator i = elems.begin();
i != elems.end(); ++i)
{
@@ -251,7 +246,7 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
the meta attributes. */
Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
ATermList as = ATmakeList5(
ATermList as = ATmakeList4(
makeBind(toATerm("type"),
makeStr("derivation"), makeNoPos()),
makeBind(toATerm("name"),
@@ -259,21 +254,18 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
makeBind(toATerm("system"),
makeStr(i->system), makeNoPos()),
makeBind(toATerm("outPath"),
makeStr(i->queryOutPath(state)), makeNoPos()),
makeBind(toATerm("stateIdentifier"),
makeStr(i->queryStateIdentifier(state)), makeNoPos()) );
makeStr(i->queryOutPath(state)), makeNoPos()));
if (drvPath != "")
as = ATinsert(as, makeBind(toATerm("drvPath"), makeStr(drvPath), makeNoPos()));
if (drvPath != "") as = ATinsert(as,
makeBind(toATerm("drvPath"),
makeStr(drvPath), makeNoPos()));
if (i->attrs->get(toATerm("meta")))
as = ATinsert(as, makeBind(toATerm("meta"),
strictEvalExpr(state, i->attrs->get(toATerm("meta"))), makeNoPos()));
if (i->attrs->get(toATerm("meta"))) as = ATinsert(as,
makeBind(toATerm("meta"),
strictEvalExpr(state, i->attrs->get(toATerm("meta"))),
makeNoPos()));
manifest = ATinsert(manifest, makeAttrs(as)); //All DrvInfo's are inserted here
//Insert the new stateIdentifier into the stateIdentifiers Atermlist
stateIdentifiers = ATinsert(stateIdentifiers, makeStr(i->queryStateIdentifier(state)));
manifest = ATinsert(manifest, makeAttrs(as));
inputs = ATinsert(inputs, makeStr(i->queryOutPath(state)));
@@ -281,46 +273,31 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
`nix-env -i /nix/store/abcd...-foo'. */
store->addTempRoot(i->queryOutPath(state));
store->ensurePath(i->queryOutPath(state));
references.insert(i->queryOutPath(state));
if (drvPath != "")
references.insert(drvPath);
if (drvPath != "") references.insert(drvPath);
}
//printMsg(lvlError, format("TEST '%1%'") % atPrint(canonicaliseExpr(makeList(ATreverse(stateIdentifiers)))) );
//printMsg(lvlError, format("TEST '%1%'") % atPrint(canonicaliseExpr(makeList(ATreverse(runtimeStateArgs)))) );
/* Also write a copy of the list of inputs to the store; we need
it for future modifications of the environment. */
Path manifestFile = store->addTextToStore("env-manifest", atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
Path manifestFile = store->addTextToStore("env-manifest",
atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
Expr topLevel = makeCall(envBuilder, makeAttrs(
ATinsert(
ATmakeList5(
makeBind(toATerm("system"),
makeStr(thisSystem), makeNoPos()),
makeBind(toATerm("derivations"),
makeList(ATreverse(manifest)), makeNoPos()),
makeBind(toATerm("stateIdentifiers"),
makeList(ATreverse(stateIdentifiers)), makeNoPos()),
makeBind(toATerm("manifest"),
makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos()),
makeBind(toATerm("nixBinDir"),
makeStr(nixBinDir), makeNoPos()) )
,
makeBind(toATerm("nixStore"),
makeStr(nixStore), makeNoPos())
)
));
Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3(
makeBind(toATerm("system"),
makeStr(thisSystem), makeNoPos()),
makeBind(toATerm("derivations"),
makeList(ATreverse(manifest)), makeNoPos()),
makeBind(toATerm("manifest"),
makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos())
)));
/* Instantiate it. */
debug(format("evaluating builder expression `%1%'") % topLevel);
DrvInfo topLevelDrv;
if (!getDerivation(state, topLevel, topLevelDrv))
abort();
/* Realise the resulting store expression. */
debug(format("building user environment"));
store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state)));
@@ -342,16 +319,9 @@ static int comparePriorities(EvalState & state,
}
static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
{
return
store->isValidPath(elem.queryOutPath(state)) ||
store->hasSubstitutes(elem.queryOutPath(state));
}
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
const Strings & args, bool newestOnly, bool prebuiltOnly)
static DrvInfos filterBySelector(EvalState & state,
const DrvInfos & allElems,
const Strings & args, bool newestOnly)
{
DrvNames selectors = drvNamesFromArgs(args);
@@ -370,8 +340,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
DrvName drvName(j->name);
if (i->matches(drvName)) {
i->hits++;
if (!prebuiltOnly || isPrebuilt(state, *j))
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
}
}
@@ -430,7 +399,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
/* Check that all selectors have been used. */
for (DrvNames::iterator i = selectors.begin();
i != selectors.end(); ++i)
if (i->hits == 0 && i->fullName != "*")
if (i->hits == 0)
throw Error(format("selector `%1%' matches no derivations")
% i->fullName);
@@ -438,18 +407,12 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
}
static bool isPath(const string & s)
{
return s.find('/') != string::npos;
}
static void queryInstSources(EvalState & state,
const InstallSourceInfo & instSource, const Strings & args,
DrvInfos & elems, bool newestOnly)
{
InstallSourceType type = instSource.type;
if (type == srcUnknown && args.size() > 0 && isPath(args.front()))
if (type == srcUnknown && args.size() > 0 && args.front()[0] == '/')
type = srcStorePaths;
switch (type) {
@@ -466,8 +429,7 @@ static void queryInstSources(EvalState & state,
loadDerivations(state, instSource.nixExprPath,
instSource.systemFilter, instSource.autoArgs, "", allElems);
elems = filterBySelector(state, allElems, args,
newestOnly, instSource.prebuiltOnly);
elems = filterBySelector(state, allElems, args, newestOnly);
break;
}
@@ -501,24 +463,23 @@ static void queryInstSources(EvalState & state,
for (Strings::const_iterator i = args.begin();
i != args.end(); ++i)
{
Path path = followLinksToStorePath(*i);
assertStorePath(*i);
DrvInfo elem;
elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
string name = baseNameOf(path);
elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
string name = baseNameOf(*i);
string::size_type dash = name.find('-');
if (dash != string::npos)
name = string(name, dash + 1);
if (isDerivation(path)) {
elem.setDrvPath(path);
elem.setOutPath(findOutput(derivationFromPath(path), "out"));
if (isDerivation(*i)) {
elem.setDrvPath(*i);
elem.setOutPath(findOutput(derivationFromPath(*i), "out"));
if (name.size() >= drvExtension.size() &&
string(name, name.size() - drvExtension.size()) == drvExtension)
name = string(name, 0, name.size() - drvExtension.size());
}
else elem.setOutPath(path);
else elem.setOutPath(*i);
elem.name = name;
@@ -534,7 +495,7 @@ static void queryInstSources(EvalState & state,
case srcProfile: {
elems = filterBySelector(state,
queryInstalled(state, instSource.profile),
args, newestOnly, instSource.prebuiltOnly);
args, newestOnly);
break;
}
@@ -595,36 +556,12 @@ static void installDerivations(Globals & globals,
DrvInfos newElems;
queryInstSources(globals.state, globals.instSource, args, newElems, true);
StringPairs externalStates; //Mapping from statePath --> some path outide the store
StringSet newNames;
for (DrvInfos::iterator i = newElems.begin(); i != newElems.end(); ++i) {
/* `forceName' is a hack to get package names right in some
one-click installs, namely those where the name used in the
path is not the one we want (e.g., `java-front' versus
`java-front-0.9pre15899'). */
//Set the state indentifier if it is a state-component
printMsg(lvlError, format("New component to install DRV: '%1%'") % i->queryDrvPath(globals.state));
Derivation drv = derivationFromPath(i->queryDrvPath(globals.state));
if(isStateDrv(drv))
{
DerivationStateOutputs stateOutputs = drv.stateOutputs;
string stateIdentifier = stateOutputs.find("state")->second.stateIdentifier;
printMsg(lvlError, format("Add state component '%2%' with identifier '%1%'") % stateIdentifier % i->queryOutPath(globals.state));
if(stateIdentifier == "")
i->setStateIdentifier("__EMTPY__");
else
i->setStateIdentifier(stateIdentifier);
//Later on we need to link the sharedState path out of the store to the statePath inside the state-store
string externalState = stateOutputs.find("state")->second.externalState;
Path statePath = stateOutputs.find("state")->second.statepath;
if(externalState != ""){
externalStates[statePath] = externalState;
}
}
if (globals.forceName != "")
i->name = globals.forceName;
newNames.insert(DrvName(i->name).name);
@@ -636,72 +573,24 @@ static void installDerivations(Globals & globals,
lockProfile(lock, profile);
DrvInfos installedElems = queryInstalled(globals.state, profile);
StringPairs toBeShared; //Mapping from old --> new statePath
DrvInfos allElems(newElems);
for (DrvInfos::iterator i = installedElems.begin(); i != installedElems.end(); ++i)
for (DrvInfos::iterator i = installedElems.begin();
i != installedElems.end(); ++i)
{
DrvName drvName(i->name);
MetaInfo meta = i->queryMetaInfo(globals.state);
//We may need to share state
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
meta["keep"] != "true"
){
//******** We're gonna check if the component and state indentifiers are the same,
// since we may need to share state in that case.
string oldStateIdentifier = i->queryStateIdentifier(globals.state); //get the old stateIdentifier
if(oldStateIdentifier != "__NOSTATE__")
{
//get the new stateIdentifier
string newStateIdentifier;
string newStatePath;
string newSharedState;
for (DrvInfos::iterator j = newElems.begin(); j != newElems.end(); ++j) {
DrvName newDrvName(j->name);
if(newDrvName.name == drvName.name){
Derivation newDrv = derivationFromPath(j->queryDrvPath(globals.state));
DerivationStateOutputs newStateOutputs = newDrv.stateOutputs;
newStateIdentifier = newStateOutputs.find("state")->second.stateIdentifier;
newStatePath = newDrv.stateOutputs.find("state")->second.statepath;
newSharedState = newDrv.stateOutputs.find("state")->second.sharedState;
break;
}
}
//printMsg(lvlError, format("old '%1%' new '%2%'") % oldStateIdentifier % newStateIdentifier);
//if it doesnt need to share state with some other component
//&& the identifiers are equal
if( newSharedState == ""
&&
(oldStateIdentifier == newStateIdentifier || (oldStateIdentifier == "__EMTPY__" && newStateIdentifier == ""))
){
//query old state path
Path oldStatePath = store->lookupStatePath(i->queryOutPath(globals.state), newStateIdentifier, queryCurrentUsername());
//SharePaths
printMsg(lvlError, format("Sharing state from old <-- new component '%1%' <-- '%2%'") % oldStatePath % newStatePath);
toBeShared[oldStatePath] = newStatePath;
}
else{ //If not equal, then we do not replace, so we push back (just like the else branch)
printMsg(lvlError, format("Installing new state-component component '%1%' with identifier '%2%'") % drvName.name % newStateIdentifier);
allElems.push_back(*i);
}
}
else
printMsg(lvlInfo, format("replacing old `%1%'") % i->name);
}
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
meta["keep"] != "true")
printMsg(lvlInfo,
format("replacing old `%1%'") % i->name);
else
allElems.push_back(*i);
}
for (DrvInfos::iterator i = newElems.begin(); i != newElems.end(); ++i)
printMsg(lvlInfo, format("installing `%1%'") % i->name);
printMsg(lvlInfo,
format("installing `%1%'") % i->name);
if (globals.dryRun) {
printMissing(globals.state, newElems);
@@ -710,63 +599,6 @@ static void installDerivations(Globals & globals,
createUserEnv(globals.state, allElems,
profile, globals.keepDerivations);
//After all components have been built succesfully, share their state paths with the old ones
for (StringPairs::iterator i = toBeShared.begin(); i != toBeShared.end(); ++i){
printMsg(lvlError, format("Sharing state from old <-- new component '%1%' <-- '%2%'") % i->first % i->second);
//Share from new --> to existing
store->shareState(i->second, i->first, false);
}
//**********************
//Let the stateDirs in /nix/state point to the solidStateDependencies
for (StringPairs::iterator i = externalStates.begin(); i != externalStates.end(); ++i){
Path statePath = i->first;
Path externalState = i->second;
//1. If dir externalState exists, we move its data into the statePath
//2. We ensure that the parent dir of externalState exists so we can create a symlink
if(DirectoryExist(externalState)){
//We cannot copy into itself so we have to test that
if(IsSymlink(externalState)){
Path read_statePath = readLink(externalState);
assert(store->isValidStatePath(read_statePath));
PathSet comparePaths;
comparePaths.insert(statePath);
comparePaths.insert(read_statePath);
if(store->toNonSharedPathSet(comparePaths).size() != 1)
rsyncPaths(externalState, statePath, true);
}
else
rsyncPaths(externalState, statePath, true);
deletePath(externalState);
}
else{
//Ensure parent dir
string externalState_p = externalState;
if(externalState_p[externalState_p.length() - 1] == '/')
externalState_p.erase(externalState_p.length(),1);
externalState_p = externalState_p.substr(0,externalState_p.find_last_of('/'));
ensureDirExists(externalState_p);
//printMsg(lvlError, format("EnsureDir: '%1%'") % externalState_p);
}
//Now we create a symlink externalState --> statePath
printMsg(lvlError, format("SYMLINK: '%1%' --> '%2%'") % externalState % statePath); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DOESNT WORK OK >.....
executeShellCommand("whoami");
symlinkPath(statePath, externalState);
}
//**********************
//TODO !!!!!!!!!!!!!!!!!!!!!!!
//Remove the external links when a user uninstalls
}
@@ -788,7 +620,6 @@ static void opInstall(Globals & globals,
typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType;
//TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
static void upgradeDerivations(Globals & globals,
const Strings & args, UpgradeType upgradeType)
{
@@ -968,7 +799,7 @@ static void opSet(Globals & globals,
}
static void uninstallDerivations(Globals & globals, Strings & selectors,
static void uninstallDerivations(Globals & globals, DrvNames & selectors,
Path & profile)
{
PathLocks lock;
@@ -981,13 +812,11 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
{
DrvName drvName(i->name);
bool found = false;
for (Strings::iterator j = selectors.begin(); j != selectors.end(); ++j)
/* !!! the repeated calls to followLinksToStorePath() are
expensive, should pre-compute them. */
if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j))
|| DrvName(*j).matches(drvName))
{
printMsg(lvlInfo, format("uninstalling `%1%'") % i->name);
for (DrvNames::iterator j = selectors.begin();
j != selectors.end(); ++j)
if (j->matches(drvName)) {
printMsg(lvlInfo,
format("uninstalling `%1%'") % i->name);
found = true;
break;
}
@@ -1006,7 +835,11 @@ static void opUninstall(Globals & globals,
{
if (opFlags.size() > 0)
throw UsageError(format("unknown flag `%1%'") % opFlags.front());
uninstallDerivations(globals, opArgs, globals.profile);
DrvNames drvNames = drvNamesFromArgs(opArgs);
uninstallDerivations(globals, drvNames,
globals.profile);
}
@@ -1166,7 +999,7 @@ static void opQuery(Globals & globals,
DrvInfos elems = filterBySelector(globals.state,
source == sInstalled ? installedElems : availElems,
remaining, false, prebuiltOnly);
remaining, false);
DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
@@ -1207,6 +1040,12 @@ static void opQuery(Globals & globals,
/* For XML output. */
XMLAttrs attrs;
if (prebuiltOnly) {
if (!store->isValidPath(i->queryOutPath(globals.state)) &&
!store->hasSubstitutes(i->queryOutPath(globals.state)))
continue;
}
if (printStatus) {
bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();

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