Compare commits

...
261 Commits
Author SHA1 Message Date
Eelco Dolstra a6ce6d9e7c Fix manual generation
Grmbl.
2012-12-06 16:55:57 +01:00
Eelco Dolstra 8cc19ed089 Set a long SQLite timeout in the binary cache substituter 2012-12-06 11:43:34 +01:00
Eelco Dolstra 52edef3495 Fix RPM build
http://hydra.nixos.org/build/3436627
2012-12-05 23:25:40 +01:00
Eelco Dolstra 8d100dbef1 Add a dependency on xz-utils 2012-12-05 12:18:07 +01:00
Eelco Dolstra 5c487761c4 Add a dependency on xz 2012-12-05 12:17:12 +01:00
Eelco Dolstra 3631dc6b2f Typo 2012-12-05 12:15:06 +01:00
Eelco Dolstra aa61bc7472 Fix RPM build
http://hydra.nixos.org/build/3436511
2012-12-05 12:05:43 +01:00
Eelco Dolstra 0f96966a44 Add release date 2012-12-05 11:03:06 +01:00
Eelco Dolstra 566afa1e9c Support xz compression of nixexprs.tar in channels 2012-12-05 11:02:44 +01:00
Eelco Dolstra 444b03a36f Produce an xz-compressed tarball
Footnote: doing "make dist-gzip dist-xz" doesn't work with Automake;
you have to do "make dist-gzip; dist-xz".  That's because the dist-*
targets delete the temporary distdir at the end.
2012-12-05 10:23:53 +01:00
Eelco Dolstra d5a01d0f9d Build Debs for Ubuntu 12.10 2012-12-05 09:54:41 +01:00
Eelco Dolstra a7b4aaa2c3 Updated release notes 2012-12-04 17:41:51 +01:00
Eelco Dolstra 3100b29fc5 Tiny optimisation in the filter primop 2012-12-04 17:22:20 +01:00
Eelco Dolstra 094a08f839 Document new primops 2012-12-04 17:15:32 +01:00
Florian FriesdorfandEelco Dolstra 4387d19359 nix-channel --update needs bzip2 2012-12-04 16:35:27 +01:00
Eelco Dolstra 24d5875514 Document multiple output support 2012-12-04 16:03:56 +01:00
Eelco Dolstra b215b23e9e Test priorities 2012-12-04 14:47:50 +01:00
Eelco Dolstra 56d29dcd62 buildenv.pl: Create symlinks in priority order
This reduces unnecessary symlink/unlink steps.
2012-12-04 14:45:32 +01:00
Eelco Dolstra 2d5e8e267b Add a test for ‘nix-env --set-flag active ...’ 2012-12-04 14:30:34 +01:00
Eelco Dolstra 5ad89398d1 nix-env: Install all outputs of a derivation
If you explicitly install a package, presumably you want all of it.
So symlink all outputs in the user environment.
2012-12-04 14:20:36 +01:00
Eelco Dolstra 21c2d8d102 Test the ‘--prebuilt-only’ flag 2012-12-03 21:02:06 +01:00
Eelco Dolstra d62fc71b85 Fix the ‘--prebuilt-only’ flag 2012-12-03 21:01:41 +01:00
Eelco Dolstra 4bb4d5479a Whitespace 2012-12-03 18:19:49 +01:00
Eelco Dolstra 8eed07cda4 nix-env -q --out-path: Support multiple outputs
We now print all output paths of a package, e.g.

  openssl-1.0.0i  bin=/nix/store/gq2mvh0wb9l90djvsagln3aqywqmr6vl-openssl-1.0.0i-bin;man=/nix/store/7zwf5r5hsdarl3n86dasvb4chm2xzw9n-openssl-1.0.0i-man;/nix/store/cj7xvk7fjp9q887359j75pw3pzjfmqf1-openssl-1.0.0i

or (in XML mode)

  <item attrPath="openssl" name="openssl-1.0.0i" system="x86_64-linux">
    <output name="bin" path="/nix/store/gq2mvh0wb9l90djvsagln3aqywqmr6vl-openssl-1.0.0i-bin" />
    <output name="man" path="/nix/store/7zwf5r5hsdarl3n86dasvb4chm2xzw9n-openssl-1.0.0i-man" />
    <output name="out" path="/nix/store/cj7xvk7fjp9q887359j75pw3pzjfmqf1-openssl-1.0.0i" />
  </item>
2012-11-28 13:49:44 +01:00
Eelco Dolstra 6c98e6a5de Optionally ignore null-valued derivation attributes
This allows adding attributes like

  attr = if stdenv.system == "bla" then something else null;

without changing the resulting derivation on non-<bla> platforms.

We once considered adding a special "ignore" value for this purpose,
but using null seems more elegant.
2012-11-27 15:01:32 +01:00
Eelco Dolstra 8b8ee53bc7 Add builtin constants ‘langVersion’ and ‘nixVersion’
The integer constant ‘langVersion’ denotes the current language
version.  It gets increased every time a language feature is
added/changed/removed.  It's currently 1.

The string constant ‘nixVersion’ contains the current Nix version,
e.g. "1.2pre2980_9de6bc5".
2012-11-27 13:29:55 +01:00
Eelco Dolstra 5943f41b8b queryMissing(): Handle partially valid derivations 2012-11-26 18:25:12 +01:00
Eelco Dolstra 08964d7328 Undo accidental debug change 2012-11-26 17:57:14 +01:00
Eelco Dolstra 69c88f5028 Fix the multiple-outputs test 2012-11-26 17:46:45 +01:00
Eelco Dolstra 408a7bfac1 nix-instantiate: Fix read-only evaluation 2012-11-26 17:41:10 +01:00
Eelco Dolstra 8d8d47abd2 Only substitute wanted outputs of a derivation
If a derivation has multiple outputs, then we only want to download
those outputs that are actuallty needed.  So if we do "nix-build -A
openssl.man", then only the "man" output should be downloaded.
Likewise if another package depends on ${openssl.man}.

The tricky part is that different derivations can depend on different
outputs of a given derivation, so we may need to restart the
corresponding derivation goal if that happens.
2012-11-26 17:15:09 +01:00
Eelco Dolstra 46a369ad95 Make "nix-build -A <derivation>.<output>" do the right thing
For example, given a derivation with outputs "out", "man" and "bin":

  $ nix-build -A pkg

produces ./result pointing to the "out" output;

  $ nix-build -A pkg.man

produces ./result-man pointing to the "man" output;

  $ nix-build -A pkg.all

produces ./result, ./result-man and ./result-bin;

  $ nix-build -A pkg.all -A pkg2

produces ./result, ./result-man, ./result-bin and ./result-2.
2012-11-26 15:39:10 +01:00
Eelco Dolstra a3d6585c5a nix-copy-closure: Add flag ‘--use-substitutes’ 2012-11-23 16:20:16 +01:00
Eelco Dolstra 9de6bc5d05 nix-store -r: Add ‘--ignore-unknown’ flag
This flag causes paths that do not have a known substitute to be
quietly ignored.  This is mostly useful for Charon, allowing it to
speed up deployment by letting a machine use substitutes for all
substitutable paths, instead of uploading them.  The latter is
frequently faster, e.g. if the target machine has a fast Internet
connection while the source machine is on a slow ADSL line.
2012-11-20 00:27:25 +01:00
Eelco Dolstra bf3725da2a nix-store -r: Don't quietly ignore missing paths 2012-11-19 23:51:56 +01:00
Eelco Dolstra 17dc306aa3 Revert "prim_toPath: Actually make the string a path"
This reverts commit 2980d1fba9.  It
causes a regression in NixOS evaluation:

string `/nix/store/ya3s5gmj3b28170fpbjhgsk8wzymkpa1-pommed-1.39/etc/pommed.conf' cannot refer to other paths
2012-11-15 23:04:34 +01:00
Eelco Dolstra f794465ca8 Disable use of vfork()
vfork() is just too weird.  For instance, in this build:

  http://hydra.nixos.org/build/3330487

the value fromHook.writeSide becomes corrupted in the parent, even
though the child only reads from it.  At -O0 the problem goes away.
Probably the child is overriding some spilled temporary variable.

If I get bored I may implement using posix_spawn() instead.
2012-11-15 19:35:18 +01:00
Eelco Dolstra 8541d27fce Don't use std::cerr in a few places
Slightly scared of using std::cerr in a vforked process...
2012-11-15 15:01:02 +01:00
Eelco Dolstra 3acc8adcad Add some debug code 2012-11-15 13:55:02 +01:00
Eelco Dolstra ea89df2b76 Use vfork() instead of fork() if available
Hopefully this reduces the chance of hitting ‘unable to fork: Cannot
allocate memory’ errors.  vfork() is used for everything except
starting builders.
2012-11-09 18:00:33 +01:00
Eelco Dolstra 48c19c4633 Remove definition of non-existant function 2012-11-09 17:59:41 +01:00
Eelco Dolstra 198dbe7fa1 Remove some redundant close() calls
They are unnecessary because we set the close-on-exec flag.
2012-11-09 16:58:51 +01:00
Eelco Dolstra 10dcee99ed Remove the quickExit function 2012-11-09 16:42:10 +01:00
Eelco Dolstra 4c9e3fa641 Remove a Darwin hack that should no longer be needed 2012-11-09 16:35:42 +01:00
Eelco Dolstra 182e15b661 Manual: Don't use a store path in our closure
http://hydra.nixos.org/build/3313227
2012-11-09 16:30:15 +01:00
Eelco Dolstra 88164325fa Fix a segfault when auto-calling a "a@{...}" function
Since the called function can return its argument attribute set
(e.g. "a"), the latter should not be allocated on the stack.

Reported by Shea.
2012-11-09 15:09:31 +01:00
Eelco Dolstra f581ce0b0c Don't start copy-from-other-stores if $NIX_OTHER_STORES is unset
Slight optimisation.
2012-11-09 14:58:10 +01:00
Eelco Dolstra 91ef4d9a81 Remove unnecessary call to closeMostFDs()
We have close-on-exec on all FDs now, and there is no security risk in
passing open FDs to substituters anyway.
2012-11-09 14:43:47 +01:00
Eelco Dolstra a9a8baaccb Use a shorter Nixpkgs channel URL 2012-11-09 13:33:35 +01:00
Eelco Dolstra c31ebc50aa Update release notes 2012-11-09 13:33:24 +01:00
Eelco Dolstra e28b683324 download-from-binary-cache: Try next cache if downloading a NAR fails 2012-11-06 17:55:53 +01:00
Eelco Dolstra bbc107ef1e Process binary caches in order of priority
Binary caches can now specify a priority in their nix-cache-info file.
The binary cache substituter checks caches in order of priority.  This
is to ensure that fast, static caches like nixos.org/binary-cache are
processed before slow, dynamic caches like hydra.nixos.org.
2012-11-06 17:45:20 +01:00
Eelco Dolstra 3a95e1a17c Update nix-push manpage and document the binary cache format 2012-11-06 15:49:53 +01:00
Eelco Dolstra 82951e5582 nix-push: Handle pushing a symlink 2012-11-06 13:53:53 +01:00
Eelco Dolstra 620e92e880 Add an option ‘use-binary-caches’
This allows disabling the use of binary caches, e.g.

  $ nix-build ... --option use-binary-caches false

Note that

  $ nix-build ... --option binary-caches ''

does not disable all binary caches, since the caches defined by
channels will still be used.
2012-11-06 13:44:59 +01:00
Eelco Dolstra df27db712d Fix "Not an ARRAY reference" error
It's a mystery why this error is not triggered in the build farm
(e.g. http://hydra.nixos.org/build/3265602).  Ah well.
2012-11-06 13:43:46 +01:00
Shea LevyandEelco Dolstra d0fc615af6 canonicalizePathMetaData: Fall-back to utimes if lutimes fails due to ENOSYS 2012-11-06 11:29:59 +01:00
Eelco Dolstra 4c34d384e6 If hashes do not match, print them in base-32 for SHA-1/SHA-256
Fixes #57.
2012-10-23 18:05:50 +02:00
Eelco Dolstra a28b4445a4 nix-prefetch-url: Improve option handling 2012-10-23 18:04:11 +02:00
Shea LevyandEelco Dolstra 2980d1fba9 prim_toPath: Actually make the string a path 2012-10-23 16:39:35 +02:00
Eelco Dolstra dde6486eab nix-push: Add a flag ‘--manifest-path’ to write the manifest to another directory 2012-10-18 10:36:16 -04:00
Eelco Dolstra c8daeba303 nix-push: Add ‘--link’ flag
If ‘--link’ is given, nix-push will create hard links to the NAR files
in the store, rather than copying them.  This is faster and requires
less disk space.  However, it doesn't work if the store is on a
different file system.
2012-10-17 17:01:23 -04:00
Eelco Dolstra 167e36a5c3 nix-push: Only generate and copy a NAR if it doesn't already exist
This prevents unnecessary and slow rebuilds of NARs that already exist
in the binary cache.
2012-10-17 16:58:05 -04:00
Eelco Dolstra ac238d619c Typo
Reported by Shea.
2012-10-12 10:21:04 -04:00
Eelco Dolstra 600daf972f download-from-binary-cache: Remove duplicate entries in trustedURLs 2012-10-11 14:21:22 -04:00
Eelco Dolstra e345182055 Shorten the names of temporary build directories 2012-10-11 14:03:06 -04:00
Mats Erik AnderssonandEelco Dolstra ecedd9c50c Out-of-tree building of perl modules. 2012-10-10 10:28:33 -04:00
Eelco Dolstra 70f75be199 getDerivation(): Don't always quietly ignore assertion failure
Ignoring assertion failures makes some sense for nix-env -qa, but not
for nix-instantiate/nix-build or hydra-eval-jobs.
2012-10-04 15:22:25 -04:00
Eelco Dolstra ad328bea15 XML writer: flush after newlines
This is useful for hydra-eval-jobs.
2012-10-04 14:07:36 -04:00
Eelco Dolstra bfaa5635de Manual: Don't use a store path that actually exists
http://hydra.nixos.org/build/3124130
2012-10-04 13:37:30 -04:00
Eelco Dolstra 904f50412c nix-store --verify: Continue on errors 2012-10-04 10:20:23 -04:00
Eelco Dolstra 90b8a34f82 Fix regular expression
http://hydra.nixos.org/build/3123177
2012-10-04 09:46:10 -04:00
Eelco Dolstra d1de836938 Fix the tarball build 2012-10-03 21:01:03 -04:00
Eelco Dolstra f766e146f4 Fix the test 2012-10-03 18:01:35 -04:00
Eelco Dolstra e35d6f78dc Rename nix-worker to nix-daemon 2012-10-03 17:59:23 -04:00
Eelco Dolstra 522ecab9b8 Drop support for running nix-worker in "slave" mode
AFAIK nobody uses this, setuid binaries are evil, and there is no good
reason why people can't just run the daemon.
2012-10-03 17:30:45 -04:00
Eelco Dolstra 7586095504 Remove bin2c 2012-10-03 16:59:28 -04:00
Eelco Dolstra a562d544d8 When ‘--help’ is given, just run ‘man’ to show the manual page
I.e. do what git does.  I'm too lazy to keep the builtin help text up
to date :-)

Also add ‘--help’ to various commands that lacked it
(e.g. nix-collect-garbage).
2012-10-03 16:40:09 -04:00
Eelco Dolstra 9c41c66c5b Document ‘--repair’ 2012-10-03 15:53:25 -04:00
Eelco Dolstra 2bbc4a214e nix-env: Support ‘--repair’ flag 2012-10-03 15:35:42 -04:00
Eelco Dolstra 2e90a5a2a7 nix-build: Support ‘--repair’ flag 2012-10-03 15:14:02 -04:00
Eelco Dolstra 0a7084567f Add a ‘--repair’ flag to nix-instantiate
This allows repairing corrupted derivations and other source files.
2012-10-03 15:09:18 -04:00
Eelco Dolstra a807edfae8 Handle repairing paths that are in build-chroot-dirs 2012-10-03 11:20:16 -04:00
Eelco Dolstra a3f205b249 When repairing a derivation, check and repair the entire output closure
If we find a corrupted path in the output closure, we rebuild the
derivation that produced that particular path.
2012-10-03 10:38:09 -04:00
Eelco Dolstra 2001895f3d Add a --repair flag to ‘nix-store -r’ to repair derivation outputs
With this flag, if any valid derivation output is missing or corrupt,
it will be recreated by using a substitute if available, or by
rebuilding the derivation.  The latter may use hash rewriting if
chroots are not available.
2012-10-02 17:13:46 -04:00
Eelco Dolstra cf46f19444 nix-store -r: Get rid of an unnecessary call to buildPaths/ensurePaths 2012-10-02 16:00:09 -04:00
Eelco Dolstra 8e3a7bd712 nix-store --verify: Add an option ‘--repair’ to repair all missing/corrupt paths
Also, return a non-zero exit code if errors remain after
verifying/repairing.
2012-10-02 15:12:56 -04:00
Eelco Dolstra 9958bd6992 Add operation ‘nix-store --repair-path’
This operation allows fixing corrupted or accidentally deleted store
paths by redownloading them using substituters, if available.

Since the corrupted path cannot be replaced atomically, there is a
very small time window (one system call) during which neither the old
(corrupted) nor the new (repaired) contents are available.  So
repairing should be used with some care on critical packages like
Glibc.
2012-10-02 14:08:59 -04:00
Eelco Dolstra e666e1156f Handle octal escapes in /proc/self/mountinfo 2012-09-28 21:39:30 -04:00
Eelco Dolstra f406288cc7 Print a more descriptive error message if setting up the build environment fails 2012-09-28 21:37:02 -04:00
Eelco Dolstra 95c74eae26 Allow dashes in identifiers
In Nixpkgs, the attribute in all-packages.nix corresponding to a
package is usually equal to the package name.  However, this doesn't
work if the package contains a dash, which is fairly common.  The
convention is to replace the dash with an underscore (e.g. "dbus-lib"
becomes "dbus_glib"), but that's annoying.  So now dashes are valid in
variable / attribute names, allowing you to write:

  dbus-glib = callPackage ../development/libraries/dbus-glib { };

and

  buildInputs = [ dbus-glib ];

Since we don't have a negation or subtraction operation in Nix, this
is unambiguous.
2012-09-27 15:49:20 -04:00
Eelco Dolstra f46612be96 Add "on Linux" qualifier 2012-09-25 16:30:37 -04:00
Eelco Dolstra d534f137f0 Make the store writable before creating /nix/store/.links 2012-09-25 16:30:08 -04:00
Eelco Dolstra 0f358ca5b6 Document that Nix requires GNU Make
Fixes #44.
2012-09-25 16:17:30 -04:00
Eelco Dolstra cb6651e878 Update release notes 2012-09-25 16:04:50 -04:00
Eelco Dolstra e464b0247d Merge branch 'readonly-store' 2012-09-25 15:38:00 -04:00
Eelco Dolstra 28bf183d2d Include <sys/types.h> for off_t
Reported by "gio" on IRC.
2012-09-25 13:00:19 -04:00
Eelco Dolstra c1f91570b3 Style fix 2012-09-21 15:02:33 -04:00
Eelco Dolstra 20582e9ae3 Support xz compression in the download-using-manifests substituter 2012-09-19 17:33:42 -04:00
Eelco Dolstra b9c2b4d5b4 Remove setting of the immutable bit
Using the immutable bit is problematic, especially in conjunction with
store optimisation.  For instance, if the garbage collector deletes a
file, it has to clear its immutable bit, but if the file has
additional hard links, we can't set the bit afterwards because we
don't know the remaining paths.

So now that we support having the entire Nix store as a read-only
mount, we may as well drop the immutable bit.  Unfortunately, we have
to keep the code to clear the immutable bit for backwards
compatibility.
2012-09-19 16:17:54 -04:00
Eelco Dolstra b9124a5c33 Support having /nix/store as a read-only bind mount
It turns out that the immutable bit doesn't work all that well.  A
better way is to make the entire Nix store a read-only bind mount,
i.e. by doing

  $ mount --bind /nix/store /nix/store
  $ mount -o remount,ro,bind /nix/store

(This would typically done in an early boot script, before anything
from /nix/store is used.)

Since Nix needs to be able to write to the Nix store, it now detects
if /nix/store is a read-only bind mount and then makes it writable in
a private mount namespace.
2012-09-19 15:45:29 -04:00
Eelco Dolstra 76e88871b2 Templatise tokenizeString() 2012-09-19 15:43:23 -04:00
Eelco Dolstra 00092b2d35 Keep build directory if not all expected outputs were produced
Fixes issue #123 in Nixpkgs.
2012-09-18 10:11:42 -04:00
Eelco Dolstra b674665765 Test whether GNU tar understands --warning=no-timestamp
http://hydra.nixos.org/build/3031618
2012-09-14 10:18:27 -04:00
Eelco Dolstra 09eb230900 Fix test
http://hydra.nixos.org/build/3031382
2012-09-14 09:39:54 -04:00
Eelco Dolstra 983220bcd4 nix-collect-garbage: Support --dry-run 2012-09-13 18:09:20 -04:00
Eelco Dolstra 9fd9dedf12 nix-env --delete-generations: Support --dry-run flag
Fixes #43.
2012-09-13 18:05:04 -04:00
Eelco Dolstra 1bda006b74 Add a test for nix-profile.sh 2012-09-13 17:58:14 -04:00
Eelco Dolstra aac14222f5 nix-profile.sh: Revert to single-user version
Commit 6a214f3e06 copied most of the Nix
shell initialisation code from NixOS to nix-profile.sh; however, that
code assumes a multi-user install and is Linux-specific (e.g. it calls
the "stat" command).  So go back to the simple single-user version.

Fixes #49.
2012-09-13 17:48:19 -04:00
Eelco Dolstra 54fe8e0773 Remove double Fedora release suffix 2012-09-13 17:23:12 -04:00
Eelco Dolstra 47ae3ce2ca Put a time-to-live on negative binary cache lookups
Negative lookups are purged from the DB after a day, at most once per
day.  However, for non-"have" lookups (e.g. all except "nix-env
-qas"), negative lookups are ignored after one hour.  This is to
ensure that you don't have to wait a day for an operation like
"nix-env -i" to start using new binaries in the cache.

Should probably make this configurable.
2012-09-13 16:39:16 -04:00
Eelco Dolstra e03a8a1c92 Doh 2012-09-13 14:58:56 -04:00
Eelco Dolstra 8fbe96cb31 RemoteStore::connectToDaemon(): Set close-on-exec flag
This ensures that "nix-build --run-env" doesn't keep a connection to
the worker open, preventing it from exiting.
2012-09-13 14:46:37 -04:00
Eelco Dolstra e6e495649c Vacuum the SQLite DB after running the garbage collector 2012-09-13 14:33:41 -04:00
Eelco Dolstra 2923b55f9d Delete paths in a component in topologically sorted order
The outputs of a derivation can refer to each other (even though they
cannot have cycles), so they have to be deleted in the right order.

http://hydra.nixos.org/build/3026118
2012-09-13 13:08:27 -04:00
Eelco Dolstra 31114ec3a5 Fix the store optimisation test
http://hydra.nixos.org/build/3026118
2012-09-13 12:54:23 -04:00
Eelco Dolstra c845c0ccad nix-channel: Add option to force fetching of manifests 2012-09-13 12:11:40 -04:00
Eelco Dolstra b14717ab90 Delete manifests in "nix-channel --remove" or when a binary cache is available 2012-09-13 11:35:46 -04:00
Eelco Dolstra 6c4ac29917 Disable auto store optimisation for now
I've seen operations like "nix-store --import" take much longer on one
system.  So default to off until I've investigated this a bit further.
2012-09-13 10:28:20 -04:00
Eelco Dolstra e56f71edaf In startBuilder(), only print the new paths we're building 2012-09-13 10:06:24 -04:00
Eelco Dolstra 4fca02077c Handle gc-keep-outputs and gc-keep-derivations both enabled
If the options gc-keep-outputs and gc-keep-derivations are both
enabled, you can get a cycle in the liveness graph.  There was a hack
to handle this, but it didn't work with multiple-output derivations,
causing the garbage collector to fail with errors like ‘error: cannot
delete path `...' because it is in use by `...'’.  The garbage
collector now handles strongly connected components in the liveness
graph as a unit and decides whether to delete all or none of the paths
in an SCC.
2012-09-12 18:49:35 -04:00
Eelco Dolstra 479e9172b3 Build hook: Pass the timeout to the remote builder
Note that this will only work if the client has a very recent Nix
version (post 15e1b2c223), otherwise the
--option flag will just be ignored.

Fixes #50.
2012-09-12 12:18:14 -04:00
Eelco Dolstra 4ba47205c6 Fix test failure on Darwin
Apparently our DBD::SQLite links against /usr/lib/libsqlite3.dylib,
which is an old version that doesn't respect foreign key constraints.
So manifests/cache.sqlite doesn't get updated properly when a manifest
disappears.  We should fix our DBD::SQLite, but in the meantime this
will fix the test.

http://hydra.nixos.org/build/3017959
2012-09-12 11:29:10 -04:00
Eelco Dolstra 6762424e24 Compatibility fix for WWW::Curl < 4.14
Older versions of WWW::Curl don't support scalar references for
CURLOPT_WRITEDATA directly.

http://hydra.nixos.org/build/3017188
2012-09-12 10:41:17 -04:00
Eelco Dolstra d4c3b6327f Don't put results symlinks in the tests directory 2012-09-11 19:14:15 -04:00
Eelco Dolstra 1f7901ec3b Test hash rewriting 2012-09-11 19:10:11 -04:00
Eelco Dolstra 5e2ffd0b8a Fix "non-zero padding" error
Probably it's not a good idea to pass a temporary object to
StringSource.
2012-09-11 19:09:01 -04:00
Eelco Dolstra 8cf672091e Support building a derivation if some outputs are already valid (non-chroot case)
This uses scary hash rewriting.

Fixes #21.
2012-09-11 18:39:22 -04:00
Eelco Dolstra 9dbda2b3fe Remove debug line 2012-09-11 17:05:35 -04:00
Eelco Dolstra a2785b7391 Support building a derivation if some outputs are already valid
This handles the chroot and build hook cases, which are easy.
Supporting the non-chroot-build case will require more work (hash
rewriting!).

Issue #21.
2012-09-11 16:59:59 -04:00
Eelco Dolstra 295027f533 Include config.h before any other header
"config.h" must be included first, because otherwise the compiler
might not see the right value of _FILE_OFFSET_BITS.  We've had this
before; see 705868a8a9.  In this case,
GCC would compute a different address for ‘settings.useSubstitutes’ in
misc.cc because of the off_t in ‘settings’.

Reverts 3854fc9b42.

http://hydra.nixos.org/build/3016700
2012-09-11 14:45:42 -04:00
Eelco Dolstra d3004c78d9 Don't use -warning=no-timestamp unless we have GNU tar
http://hydra.nixos.org/build/2998485
2012-09-11 13:07:22 -04:00
Shea LevyandEelco Dolstra 360056e174 Document importing from a directory in the import documentation 2012-08-29 18:07:51 -04:00
Eelco Dolstra f7b650d234 Make "nix-instantiate -" interruptible 2012-08-28 11:47:26 -04:00
Eelco Dolstra 15e1b2c223 Merge branch 'no-manifests' 2012-08-27 14:34:51 -04:00
Eelco Dolstra 9e2fc6951c Disable the binary cache substituter by default for now 2012-08-27 14:20:14 -04:00
Eelco Dolstra cfd968dd94 Fix stupid type error in calling std::max 2012-08-27 14:17:13 -04:00
Eelco Dolstra f3077fd88d Add libwww-curl-perl to the Debian/Ubuntu images 2012-08-27 13:45:53 -04:00
Eelco Dolstra cc8641815b Add WWW::Curl to the RPM dependencies 2012-08-27 11:47:49 -04:00
Eelco Dolstra 2688fb73f1 Add perl-WWW-Curl to the RPM image 2012-08-27 11:47:04 -04:00
Eelco Dolstra 8207359227 Whitespace 2012-08-27 11:28:48 -04:00
Eelco Dolstra 8b8fe6139e Drop dependency on List::MoreUtils 2012-08-27 11:28:34 -04:00
Eelco Dolstra babe54bf97 Add missing file 2012-08-27 11:11:30 -04:00
Eelco Dolstra e94806d030 Merge branch 'master' into no-manifests 2012-08-27 11:09:07 -04:00
Eelco Dolstra a9e6752bbd Include the output name in the GC root link
Output names are now appended to resulting GC symlinks, e.g. by
nix-build.  For backwards compatibility, if the output is named "out",
nothing is appended.  E.g. doing "nix-build -A foo" on a derivation
that produces outputs "out", "bin" and "dev" will produce symlinks
"./result", "./result-bin" and "./result-dev", respectively.
2012-08-24 16:58:11 -04:00
Eelco Dolstra 4aa1e5c554 Receive reserveSpace before calling startWork()
Otherwise we can get a SIGPOLL.  Reported by Ludovic.
2012-08-22 10:58:24 -04:00
Eelco Dolstra d950cfe70b Check if MS_PRIVATE is defined
http://hydra.nixos.org/build/2955671
2012-08-20 15:55:49 -04:00
Eelco Dolstra 56e30e161c In the chroot, make all mounted filesystems private
This is required on systemd, which mounts filesystems as "shared"
subtrees.  Changes to shared trees in a private mount namespace are
propagated to the outside world, which is bad.
2012-08-20 15:27:30 -04:00
Eelco Dolstra f0eab0636b Don't bind-mount /proc since we mount our own 2012-08-20 15:27:00 -04:00
Eelco Dolstra 862c4c5ec5 Fix 1755 permission on temporary directories left behind by ‘-K’ 2012-08-19 16:32:42 -04:00
Eelco Dolstra 767101824a Avoid concatenating lists of one string 2012-08-13 15:10:29 -04:00
Eelco Dolstra e5c589d271 Don't allocate empty lists
This saves about 4 MB when evaluating a NixOS system configuration.
2012-08-13 15:02:09 -04:00
Eelco Dolstra 3e89ef597c Optimise concatenating a list to an empty list
More precisely, in concatLists, if all lists except one are empty,
then just return the non-empty list.  This reduces the number of list
element allocations by 32% when evaluating a NixOS system
configuration.
2012-08-13 14:58:54 -04:00
Eelco Dolstra 9c2d63084b Add a primop ‘elemAt’ to get an element from a list 2012-08-13 13:46:42 -04:00
Eelco Dolstra 198d0338be Add a primop ‘concatLists’
This can serve as a generic efficient list builder.  For instance, the
function ‘catAttrs’ in Nixpkgs can be rewritten from

  attr: l: fold (s: l: if hasAttr attr s then [(getAttr attr s)] ++ l else l) [] l

to

  attr: l: builtins.concatLists (map (s: if hasAttr attr s then [(getAttr attr s)] else []) l)

Statistics before:

  time elapsed: 1.08683
  size of a value: 24
  environments allocated: 1384376 (35809568 bytes)
  list elements: 6946783 (55574264 bytes)
  list concatenations: 37434
  values allocated: 1760440 (42250560 bytes)
  attribute sets allocated: 392040
  right-biased unions: 186334
  values copied in right-biased unions: 591137
  symbols in symbol table: 18273
  number of thunks: 1297673
  number of thunks avoided: 1380759
  number of attr lookups: 430802
  number of primop calls: 628912
  number of function calls: 1333544

Statistics after (including new catAttrs):

  time elapsed: 0.959854
  size of a value: 24
  environments allocated: 1010198 (26829296 bytes)
  list elements: 1984878 (15879024 bytes)
  list concatenations: 30488
  values allocated: 1589760 (38154240 bytes)
  attribute sets allocated: 392040
  right-biased unions: 186334
  values copied in right-biased unions: 591137
  symbols in symbol table: 18274
  number of thunks: 1040925
  number of thunks avoided: 1038428
  number of attr lookups: 438419
  number of primop calls: 474844
  number of function calls: 959366
2012-08-13 01:53:10 -04:00
Eelco Dolstra b9e5b908ed Provide an efficient implementation of ‘elem’
The one in Nixpkgs is O(n^2), this one is O(n).  Big reduction in the
number of list allocations.

Statistics before (on a NixOS system config):

  time elapsed: 1.17982
  size of a value: 24
  environments allocated: 1543334 (39624560 bytes)
  list elements: 9612638 (76901104 bytes)
  list concatenations: 37434
  values allocated: 1854933 (44518392 bytes)
  attribute sets allocated: 392040
  right-biased unions: 186334
  values copied in right-biased unions: 591137
  symbols in symbol table: 18272
  number of thunks: 1392467
  number of thunks avoided: 1507311
  number of attr lookups: 430801
  number of primop calls: 691600
  number of function calls: 1492502

Statistics after:

  time elapsed: 1.08683
  size of a value: 24
  environments allocated: 1384376 (35809568 bytes)
  list elements: 6946783 (55574264 bytes)
  list concatenations: 37434
  values allocated: 1760440 (42250560 bytes)
  attribute sets allocated: 392040
  right-biased unions: 186334
  values copied in right-biased unions: 591137
  symbols in symbol table: 18273
  number of thunks: 1297673
  number of thunks avoided: 1380759
  number of attr lookups: 430802
  number of primop calls: 628912
  number of function calls: 1333544
2012-08-13 01:05:35 -04:00
Eelco Dolstra 4ccd48ce24 Add a "filter" primop
Evaluation of a NixOS configuration spends quite a lot of time in the
"filter" function in Nixpkgs.  As implemented in Nixpkgs, this is a
O(n^2) operation, so it's a good candidate for providing a more
efficient (i.e. primop) implementation.  Using it gives a ~10% speed
increase and a significant reduction in the number of evaluations.

Statistics before (on a NixOS system config):

  time elapsed: 1.3258
  size of a value: 24
  environments allocated: 1980939 (50127080 bytes)
  list elements: 14679308 (117434464 bytes)
  list concatenations: 50828
  values allocated: 2098938 (50374512 bytes)
  attribute sets allocated: 392040
  right-biased unions: 186334
  values copied in right-biased unions: 591137
  symbols in symbol table: 18271
  number of thunks: 1645752
  number of thunks avoided: 1921196
  number of attr lookups: 430798
  number of primop calls: 838807
  number of function calls: 1930107

Statistics after:

  time elapsed: 1.17982
  size of a value: 24
  environments allocated: 1543334 (39624560 bytes)
  list elements: 9612638 (76901104 bytes)
  list concatenations: 37434
  values allocated: 1854933 (44518392 bytes)
  attribute sets allocated: 392040
  right-biased unions: 186334
  values copied in right-biased unions: 591137
  symbols in symbol table: 18272
  number of thunks: 1392467
  number of thunks avoided: 1507311
  number of attr lookups: 430801
  number of primop calls: 691600
  number of function calls: 1492502
2012-08-13 00:28:08 -04:00
Eelco Dolstra 62f72eb9e1 Add some more evaluations stats 2012-08-12 23:41:48 -04:00
Eelco Dolstra e82767910c Add some basic profiling support to the evaluator
Setting the environment variable NIX_COUNT_CALLS to 1 enables some
basic profiling in the evaluator.  It will count calls to functions
and primops as well as evaluations of attributes.

For example, to see where evaluation of a NixOS configuration spends
its time:

$ NIX_SHOW_STATS=1 NIX_COUNT_CALLS=1 ./src/nix-instantiate/nix-instantiate '<nixos>' -A system --readonly-mode
...
calls to 39 primops:
    239532 head
    233962 tail
    191252 hasAttr
...
calls to 1595 functions:
    224157 `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/lists.nix:17:19'
    221767 `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/lists.nix:17:14'
    221767 `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/lists.nix:17:10'
...
evaluations of 7088 attributes:
    167377 undefined position
    132459 `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/attrsets.nix:119:41'
     47322 `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/attrsets.nix:13:21'
...
2012-08-12 23:29:28 -04:00
Eelco Dolstra 325d1cfebf Don't warn about maximum link count exceeded on 0-byte files 2012-08-07 16:22:54 -04:00
Eelco Dolstra d025142f52 Handle amount of disk space saved by hard linking being negative
Fixes bogus messages like "currently hard linking saves
17592186044416.00 MiB".
2012-08-05 21:45:27 -04:00
Eelco Dolstra b6c989b801 Fix race condition when two processes create a hard link to a file in .links
This is a problem because one process may set the immutable bit before
the second process has created its link.

Addressed random Hydra failures such as:

error: cannot rename `/nix/store/.tmp-link-17397-1804289383' to
`/nix/store/rsvzm574rlfip3830ac7kmaa028bzl6h-nixos-0.1pre-git/upstart-interface-version':
Operation not permitted
2012-08-05 21:41:44 -04:00
Eelco Dolstra 108e14bb18 Fix race condition when two processes create the same link in /nix/store/.links 2012-08-05 18:17:55 -04:00
Eelco Dolstra 6763084ae5 Count bytes freed deleting unused links 2012-08-01 22:43:03 -04:00
Eelco Dolstra 01d56c1eec Drop the block count in the garbage collector 2012-08-01 22:34:46 -04:00
Eelco Dolstra 967d066d8e nix-store --gc: Make ‘--max-freed 0’ do the right thing
That is, delete almost nothing (it will still remove unused links from
/nix/store/.links).
2012-08-01 19:14:58 -04:00
Eelco Dolstra 1df702d347 removeUnusedLinks(): Print stats on disk space saved by hard linking 2012-08-01 19:01:50 -04:00
Eelco Dolstra 9c2decaa19 Merge branch 'master' into no-manifests 2012-08-01 18:03:49 -04:00
Eelco Dolstra 5170c5691a nix-channel: Use binary caches advertised by channels
Channels can now advertise a binary cache by creating a file
<channel-url>/binary-cache-url.  The channel unpacker puts these in
its "binary-caches" subdirectory.  Thus, the URLS of the binary caches
for the channels added by root appear in
/nix/var/nix/profiles/per-user/eelco/channels/binary-caches/*.  The
binary cache substituter reads these and adds them to the list of
binary caches.
2012-08-01 17:56:11 -04:00
Eelco Dolstra 79bba3782c Doh 2012-08-01 17:21:47 -04:00
Eelco Dolstra ca94b38371 nix-env: Ignore manifest.nix when recursing into ~/.nix-defexpr
Channels are implemented using a profile now, and profiles contain a
manifest.nix file.  This should be ignored to prevent bogus packages
from showing up in nix-env.
2012-08-01 17:17:07 -04:00
Eelco Dolstra 46f852cda0 Use a GNU tar flag to shut up warnings about implausibly old timestamp 2012-08-01 16:42:51 -04:00
Eelco Dolstra afa7e01878 Inline unpack-channel.sh 2012-08-01 16:34:17 -04:00
Eelco Dolstra 234ce610e0 Doh 2012-08-01 16:09:47 -04:00
Eelco Dolstra 7b10562370 Make ‘nix-store --optimise’ interruptible 2012-08-01 16:06:49 -04:00
Eelco Dolstra 8a25d787d7 download-from-binary-cache: Remove duplicate URLs 2012-08-01 11:33:15 -04:00
Eelco Dolstra c770a2422a Report substituter errors to clients of the Nix daemon 2012-08-01 11:19:24 -04:00
Eelco Dolstra 4d1b64f118 Allow daemon users to override ‘binary-caches’
For security reasons, daemon users can only specify caches that appear
in the ‘binary-caches’ and ‘trusted-binary-caches’ options in
nix.conf.
2012-07-31 18:56:22 -04:00
Eelco Dolstra eb7849e3a2 Prevent an injection attack in passing untrusted options to substituters 2012-07-31 18:50:32 -04:00
Eelco Dolstra 90d9c58d4d Pass all --option flags to the daemon 2012-07-31 18:19:44 -04:00
Eelco Dolstra 89a8207029 Add an option ‘build-fallback’ (equivalent to the --fallback flag) 2012-07-31 17:56:02 -04:00
Eelco Dolstra 157170059d Manual: Remove reference to non-existent -I option 2012-07-30 22:29:42 -04:00
Eelco Dolstra 97421eb5ec Refactor settings processing
Put all Nix configuration flags in a Settings object.
2012-07-30 19:55:41 -04:00
Eelco Dolstra d50d7a2874 Whitespace 2012-07-30 17:13:25 -04:00
Eelco Dolstra 9cd63d2244 Do some validation of URLs 2012-07-30 17:09:36 -04:00
Eelco Dolstra f3eb29c653 Fix the test 2012-07-30 17:09:13 -04:00
Eelco Dolstra 9de6d10d11 Get rid of $NIX_BINARY_CACHES
You can use ‘--option binary-caches URLs’ instead.
2012-07-30 16:39:31 -04:00
Eelco Dolstra ab42bf1dab nix-install-package: Support binary caches
The .nixpkg file format is extended to optionally include the URL of a
binary cache, which will be used in preference to the manifest URL
(which can be set to a non-existent value).
2012-07-30 16:11:02 -04:00
Eelco Dolstra d059bf48e4 Pass configuration settings to the substituters
Previously substituters could read nix.conf themselves, but this
didn't take --option flags into account.
2012-07-30 16:09:54 -04:00
Eelco Dolstra f9613da180 Remove unused variables 2012-07-30 15:43:37 -04:00
Eelco Dolstra 6183cf2f19 Fix whitespace 2012-07-30 15:42:18 -04:00
Eelco Dolstra f15083c10a Document the --option flag
Pointed out by Daniel Santa Cruz on IRC.
2012-07-29 14:37:40 -04:00
Eelco Dolstra 66a3ac6a56 Allow a binary cache to declare that it doesn't support "nix-env -qas"
Querying all substitutable paths via "nix-env -qas" is potentially
hard on a server, since it involves sending thousands of HEAD
requests.  So a binary cache must now have a meta-info file named
"nix-cache-info" that specifies whether the server wants this.  It
also specifies the store prefix so that we don't send useless queries
to a binary cache for a different store prefix.
2012-07-27 18:16:05 -04:00
Eelco Dolstra 6ecf4f13f6 Use "set -x" in the tests to see where a test fails 2012-07-27 14:33:01 -04:00
Eelco Dolstra e6ab52cdd1 Test "nix-env -qas" with the binary cache substituter 2012-07-27 14:15:03 -04:00
Eelco Dolstra 73acb8b836 Let build.cc verify the expected hash of a substituter's output
Since SubstitutionGoal::finished() in build.cc computes the hash
anyway, we can prevent the inefficiency of computing the hash twice by
letting the substituter tell Nix about the expected hash, which can
then verify it.
2012-07-27 12:16:02 -04:00
Eelco Dolstra fbf59d95f6 Remove more tabs 2012-07-27 10:56:33 -04:00
Eelco Dolstra 3a8f841612 download-using-manifests: Don't use nix-prefetch-url
Instead call curl directly and pipe it into ‘nix-store --restore’.
This saves I/O and prevents creating garbage in the Nix store.
2012-07-27 10:47:36 -04:00
Eelco Dolstra b4ea83249b Remove trailing whitespace / tabs 2012-07-27 09:59:18 -04:00
Eelco Dolstra 7f8e805c8e download-from-binary-cache: Only use the default cache for /nix/store 2012-07-27 09:57:42 -04:00
Eelco Dolstra 67c6f3eded nix-push: Support generating a manifest again
This makes all the tests succeed.  Woohoo!
2012-07-26 18:28:12 -04:00
Eelco Dolstra 50395b71a9 Fix the substituter tests 2012-07-26 17:36:07 -04:00
Eelco Dolstra e3ce54ccee nix-push: Remove the upload feature 2012-07-26 17:13:38 -04:00
Eelco Dolstra 609586a16d Add a test for the binary cache substituter 2012-07-26 17:13:14 -04:00
Eelco Dolstra 7861260a5f Clear NIX_STORE when running the tests 2012-07-26 17:12:42 -04:00
Eelco Dolstra 7892ad15ab download-from-binary-cache: Support file://
The file:// URI schema requires checking for errors in a more general
way.  Also, don't cache file:// lookups.
2012-07-26 17:11:11 -04:00
Eelco Dolstra dbce685e91 Add some .gitignore entries 2012-07-26 17:10:28 -04:00
Eelco Dolstra 8c79100839 Merge branch 'master' into no-manifests 2012-07-26 15:14:33 -04:00
Eelco Dolstra 3a4623afbb Set permissions on temporary build directories to 0700
Fixes #39.
2012-07-26 15:04:40 -04:00
Eelco Dolstra 2605f4f4e6 nix-profile.sh: Don't set NIX_REMOTE on single user installations
Commit 6a214f3e06 reused the NixOS
environment initialisation for nix-profile.sh, but this is
inappropriate on systems that don't have multi-user support enabled.
2012-07-25 17:06:09 -04:00
Eelco Dolstra 477b0fbeca Subscribe to the Nixpkgs rather than NixOS channel 2012-07-25 16:56:56 -04:00
Shea LevyandEelco Dolstra f5954e2d94 prim_import: When importing .drvs, allocate the intermediate attrset on the heap just in case it escapes the stack frame. 2012-07-25 09:30:17 -04:00
Shea LevyandEelco Dolstra 1ef2d5765b Turn tests back on 2012-07-25 09:30:17 -04:00
Shea LevyandEelco Dolstra b1112bbef1 import: If the path is a valid .drv file, parse it and generate a derivation attrset.
The generated attrset has drvPath and outPath with the right string context, type 'derivation', outputName with
the right name, all with a list of outputs, and an attribute for each output.

I see three uses for this (though certainly there may be more):

* Using derivations generated by something besides nix-instantiate (e.g. guix)

* Allowing packages provided by channels to be used in nix expressions. If a channel installed a valid deriver
  for each package it provides into the store, then those could be imported and used as dependencies or installed
  in environment.systemPackages, for example.

* Enable hydra to be consistent in how it treats inputs that are outputs of another build. Right now, if an
  input is passed as an argument to the job, it is passed as a derivation, but if it is accessed via NIX_PATH
  (i.e. through the <> syntax), then it is a path that can be imported. This is problematic because the build
  being depended upon may have been built with non-obvious arguments passed to its jobset file. With this
  feature, hydra can just set the name of that input to the path to its drv file in NIX_PATH
2012-07-25 09:30:17 -04:00
Shea LevyandEelco Dolstra 566a30c007 Disable tests temporarily 2012-07-25 09:30:17 -04:00
Eelco Dolstra e98c029717 Handle platforms that don't support linking to a symlink
E.g. Darwin doesn't allow this.
2012-07-23 18:42:18 -04:00
Eelco Dolstra fd63c8bfcd Unlink the right file 2012-07-23 18:06:37 -04:00
Eelco Dolstra 0f65793f94 Add a test for Nix store optimisation 2012-07-23 17:40:23 -04:00
Eelco Dolstra 680ab6f83d Garbage collect unused links in /nix/store/.links
Incremental optimisation requires creating links in /nix/store/.links
to all files in the store.  However, this means that if we delete a
store path, no files are actually deleted because links in
/nix/store/.links still exists.  So we need to check /nix/store/.links
for files with a link count of 1 and delete them.
2012-07-23 17:14:16 -04:00
Eelco Dolstra 6193105710 Automatically optimise the Nix store when a new path is added
Auto-optimisation is enabled by default.  It can be turned off by
setting auto-optimise-store to false in nix.conf.
2012-07-23 17:14:15 -04:00
Eelco Dolstra 564fb7d9fa optimiseStore(): Use a content-addressed file store in /nix/store/.links
optimiseStore() now creates persistent, content-addressed hard links
in /nix/store/.links.  For instance, if it encounters a file P with
hash H, it will create a hard link

  P' = /nix/store/.link/<H>

to P if P' doesn't already exist; if P' exist, then P is replaced by a
hard link to P'.  This is better than the previous in-memory map,
because it had the tendency to unnecessarily replace hard links with a
hard link to whatever happened to be the first file with a given hash
it encountered.  It also allows on-the-fly, incremental optimisation.
2012-07-23 17:14:15 -04:00
Eelco Dolstra ed59bf7a18 nix-build: Support the ‘-’ argument to build an expression from stdin 2012-07-23 17:11:12 -04:00
Eelco Dolstra 6852289c46 Use lutimes() if available to canonicalise the timestamp of symlinks
Also use utimes() instead of utime() if lutimes() is not available.
2012-07-23 16:52:25 -04:00
Eelco Dolstra 1832ab71db Bump version 2012-07-18 17:17:23 -04:00
Eelco Dolstra aa115e22df download-from-binary-cache: Print correct URL 2012-07-18 11:01:17 -04:00
Eelco Dolstra fe241ece29 Merge branch 'master' into no-manifests 2012-07-18 10:47:59 -04:00
Eelco Dolstra a6f348599c Print some debug output 2012-07-17 16:19:40 -04:00
Eelco Dolstra 04559a0d45 Merge branch 'master' of github.com:NixOS/nix into no-manifests 2012-07-11 18:53:27 -04:00
Eelco Dolstra e4d6bcb6cd Update release notes 2012-07-11 18:52:23 -04:00
Eelco Dolstra f2bdc87595 Update the other substituters 2012-07-11 18:52:09 -04:00
Eelco Dolstra 15c15da482 Add some missing --version switches 2012-07-11 18:07:41 -04:00
Eelco Dolstra d287b62b64 Set the User-Agent header to "Nix/<version>" 2012-07-11 18:05:30 -04:00
Eelco Dolstra b74d92755d download-from-binary-cache: Use HEAD requests if possible
In "nix-env -qas", we don't need the substitute info, we just need to
know if it exists.  This can be done using a HTTP HEAD request, which
saves bandwidth.

Note however that curl currently has a bug that prevents it from
reusing HTTP connections if HEAD requests return a 404:

https://sourceforge.net/tracker/?func=detail&aid=3542731&group_id=976&atid=100976

Without the patch attached to the issue, using HEAD is actually quite
a bit slower than GET.
2012-07-11 17:53:20 -04:00
Eelco Dolstra 09a6321aeb Replace hasSubstitutes() with querySubstitutablePaths()
querySubstitutablePaths() takes a set of paths, so this greatly
reduces daemon <-> client latency.
2012-07-11 17:52:18 -04:00
Eelco Dolstra 58ef4d9a95 Add a function queryValidPaths()
queryValidPaths() combines multiple calls to isValidPath() in one.
This matters when using the Nix daemon because it reduces latency.
For instance, on "nix-env -qas \*" it reduces execution time from 5.7s
to 4.7s (which is indistinguishable from the non-daemon case).
2012-07-11 11:08:47 -04:00
Eelco Dolstra 667d5f1936 Rename queryValidPaths() to queryAllValidPaths() 2012-07-11 10:49:04 -04:00
Eelco Dolstra eb3036da87 Implement querySubstitutablePathInfos() in the daemon
Also removed querySubstitutablePathInfo().
2012-07-11 10:43:24 -04:00
Eelco Dolstra 6586414bc7 nix-env: Determine which paths have substitutes in parallel 2012-07-11 10:14:06 -04:00
Eelco Dolstra 5ee8944155 Cleanup 2012-07-11 10:13:16 -04:00
Eelco Dolstra ae72be1b8b Add WWW::Curl as a dependency 2012-07-09 13:11:37 -04:00
Eelco Dolstra 099125435f download-from-binary-cache: add nix.conf options 2012-07-09 10:57:28 -04:00
Eelco Dolstra 98a423b75a prim_import(): prefetch substitute info in parallel using queryMissing() 2012-07-09 09:59:34 -04:00
Eelco Dolstra 425cc612ad build.cc: Don't use hasSubstitute()
Instead make a single call to querySubstitutablePathInfo() per
derivation output.  This is faster and prevents having to implement
the "have" function in the binary cache substituter.
2012-07-08 18:39:24 -04:00
Eelco Dolstra 400e556b34 Cleanup 2012-07-08 18:39:07 -04:00
Eelco Dolstra 11800e6198 download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead.  A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these.  If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.

To fix this problem, the binary cache substituter can now perform
requests in parallel.  This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism.  (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)

For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s.  It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9.  So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.

Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit.  Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 19:08:20 -04:00
Eelco Dolstra cd94665f38 download-from-binary-cache: use WWW::Curl
Using WWW::Curl rather than running an external curl process for every
NAR info file halves the time it takes to get info thanks to libcurl's
support for persistent HTTP connections.  (We save a roundtrip per
file.)  But the real gain will come from using parallel and/or
pipelined requests.
2012-07-06 00:30:40 -04:00
Eelco Dolstra ae60643c15 download-from-binary-cache: do negative NAR info caching
I.e. if a NAR info file does *not* exist, we record it in the cache DB
so that we don't retry it later.
2012-07-03 18:54:46 -04:00
Eelco Dolstra 89380c03e9 download-from-binary-cache: in queries, preferred cached info 2012-07-03 18:35:39 -04:00
Eelco Dolstra 2a8e5c8b11 download-from-binary-cache: strip trailing / from URLs 2012-07-03 17:47:01 -04:00
Eelco Dolstra d694c599e2 download-from-binary-cache: cache binary cache info in a SQLite DB 2012-07-03 17:29:33 -04:00
Eelco Dolstra 8319b1ab9f download-from-binary-cache: Verify NAR hashes 2012-07-02 18:53:04 -04:00
Eelco Dolstra cf49472d60 nix-push: Always generate base-32 hashes 2012-07-02 18:05:57 -04:00
Eelco Dolstra 6ec7460af1 Binary caches: use a better key
Use the hash part of the store path as a key rather than a hash of the
store path.  This is enough to get the desired privacy property.
2012-07-02 12:42:58 -04:00
Eelco Dolstra 6b1e671ac6 Fix xz compression 2012-07-01 21:57:25 -04:00
Eelco Dolstra 440adfbdd1 Add an environment variable $NIX_BINARY_CACHES specifying URLs of binary caches 2012-07-01 21:55:36 -04:00
Eelco Dolstra f4190c38ba Allow both bzip2 and xz compression 2012-07-01 18:46:38 -04:00
Eelco Dolstra 000132cbd1 nix-push: Don't pollute the current directory with result symlink 2012-06-29 18:30:28 -04:00
Eelco Dolstra 37f7098464 First attempt at the manifest-less substituter 2012-06-29 18:28:52 -04:00
Eelco Dolstra 91b8814f0d Doh 2012-06-29 17:16:00 -04:00
Eelco Dolstra 4911a10a4e Use XZ compression in binary caches
XZ compresses significantly better than bzip2.  Here are the
compression ratios and execution times (using 4 cores in parallel) on
my /var/run/current-system (3.1 GiB):

  bzip2: total compressed size 849.56 MiB, 30.8% [2m08]
  xz -6: total compressed size 641.84 MiB, 23.4% [6m53]
  xz -7: total compressed size 621.82 MiB, 22.6% [7m19]
  xz -8: total compressed size 599.33 MiB, 21.8% [7m18]
  xz -9: total compressed size 588.18 MiB, 21.4% [7m40]

Note that compression takes much longer.  More importantly, however,
decompression is much faster:

  bzip2: 1m47.274s
  xz -6: 0m55.446s
  xz -7: 0m54.119s
  xz -8: 0m52.388s
  xz -9: 0m51.842s

The only downside to using -9 is that decompression takes a fair
amount (~65 MB) of memory.
2012-06-29 15:24:52 -04:00
Eelco Dolstra 49cd7387ad nix-push: create a manifest-less binary cache
Manifests are a huge pain, since users need to run nix-pull directly
or indirectly to obtain them.  They tend to be large and lag behind
the available binaries; also, the downloaded manifests in
/nix/var/nix/manifest need to be in sync with the Nixpkgs sources.  So
we want to get rid of them.

The idea of manifest-free operation works as follows.  Nix is
configured with a set of URIs of binary caches, e.g.

  http://nixos.org/binary-cache

Whenever Nix needs a store path X, it checks each binary cache for the
existence of a file <CACHE-URI>/<SHA-256 hash of X>.narinfo, e.g.

  http://nixos.org/binary-cache/bi1gh9...ia17.narinfo

The .narinfo file contains the necessary information about the store
path that was formerly kept in the manifest, i.e., (relative) URI of
the compressed NAR, references, size, hash, etc.  For example:

  StorePath: /nix/store/xqp4l88cr9bxv01jinkz861mnc9p7qfi-neon-0.29.6
  URL: 1bjxbg52l32wj8ww47sw9f4qz0r8n5vs71l93lcbgk2506v3cpfd.nar.bz2
  CompressedHash: sha256:1bjxbg52l32wj8ww47sw9f4qz0r8n5vs71l93lcbgk2506v3cpfd
  CompressedSize: 202542
  NarHash: sha256:1af26536781e6134ab84201b33408759fc59b36cc5530f57c0663f67b588e15f
  NarSize: 700440
  References: 043zrsanirjh8nbc5vqpjn93hhrf107f-bash-4.2-p24 cj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13 ...
  Deriver: 4idz1bgi58h3pazxr3akrw4fsr6zrf3r-neon-0.29.6.drv
  System: x86_64-linux

Nix then knows that it needs to download

  http://nixos.org/binary-cache/1bjxbg52l32wj8ww47sw9f4qz0r8n5vs71l93lcbgk2506v3cpfd.nar.bz2

to substitute the store path.

Note that the store directory is omitted from the References and
Deriver fields to save space, and that the URL can be relative to the
binary cache prefix.

This patch just makes nix-push create binary caches in this format.
The next step is to make a substituter that supports them.
2012-06-28 17:19:32 -04:00
151 changed files with 5587 additions and 3077 deletions
+6 -11
View File
@@ -35,8 +35,11 @@ Makefile.in
# /doc/manual/
/doc/manual/manual.html
/doc/manual/manual.xmli
/doc/manual/manual.pdf
/doc/manual/manual.is-valid
/doc/manual/*.1
/doc/manual/*.5
/doc/manual/*.8
/doc/manual/images
/doc/manual/version.txt
@@ -60,14 +63,12 @@ Makefile.in
/scripts/GeneratePatches.pm
/scripts/download-using-manifests.pl
/scripts/copy-from-other-stores.pl
/scripts/download-from-binary-cache.pl
/scripts/find-runtime-roots.pl
/scripts/build-remote.pl
/scripts/nix-reduce-build
/scripts/nix-http-export.cgi
# /src/bin2c/
/src/bin2c/bin2c
# /src/bsdiff-4.3/
/src/bsdiff-4.3/bsdiff
/src/bsdiff-4.3/bspatch
@@ -85,15 +86,12 @@ Makefile.in
# /src/nix-env/
/src/nix-env/nix-env
/src/nix-env/help.txt.hh
# /src/nix-hash/
/src/nix-hash/nix-hash
/src/nix-hash/help.txt.hh
# /src/nix-instantiate/
/src/nix-instantiate/nix-instantiate
/src/nix-instantiate/help.txt.hh
# /src/nix-log2xml/
/src/nix-log2xml/nix-log2xml
@@ -104,15 +102,12 @@ Makefile.in
# /src/nix-setuid-helper/
/src/nix-setuid-helper/nix-setuid-helper
/src/nix-setuid-helper/help.txt.hh
# /src/nix-store/
/src/nix-store/help.txt.hh
/src/nix-store/nix-store
# /src/nix-worker/
/src/nix-worker/nix-worker
/src/nix-worker/help.txt.hh
# /src/nix-daemon/
/src/nix-daemon/nix-daemon
# /tests/
/tests/test-tmp
+1 -1
View File
@@ -1,6 +1,6 @@
SUBDIRS = src perl scripts corepkgs doc misc tests
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
NEWS version misc/systemd/nix-worker.service
NEWS version misc/systemd/nix-daemon.service
pkginclude_HEADERS = config.h
+52 -8
View File
@@ -25,12 +25,12 @@ AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
case "$host_os" in
linux-gnu*)
# For backward compatibility, strip the `-gnu' part.
system="$machine_name-linux";;
# For backward compatibility, strip the `-gnu' part.
system="$machine_name-linux";;
*)
# Strip the version number from names such as `gnu0.3',
# `darwin10.2.0', etc.
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
esac])
sys_name=$(uname -s | tr 'A-Z ' 'a-z_')
@@ -40,7 +40,7 @@ case $sys_name in
sys_name=cygwin
;;
esac
AC_MSG_RESULT($system)
AC_SUBST(system)
AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
@@ -115,6 +115,27 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
])
# Check for vfork.
#AC_FUNC_FORK()
# Check for lutimes, optionally used for changing the mtime of
# symlinks.
AC_CHECK_FUNCS([lutimes])
# Check whether the store optimiser can optimise symlinks.
AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
ln -s bla tmp_link
if ln tmp_link tmp_link2 2> /dev/null; then
AC_MSG_RESULT(yes)
AC_DEFINE(CAN_LINK_SYMLINK, 1, [Whether link() works on symlinks.])
else
AC_MSG_RESULT(no)
fi
rm -f tmp_link tmp_link2
# Check for <locale>.
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([locale])
@@ -161,6 +182,7 @@ NEED_PROG(perl, perl)
NEED_PROG(sed, sed)
NEED_PROG(tar, tar)
NEED_PROG(bzip2, bzip2)
NEED_PROG(xz, xz)
AC_PATH_PROG(dot, dot)
AC_PATH_PROG(dblatex, dblatex)
AC_PATH_PROG(gzip, gzip)
@@ -249,7 +271,7 @@ if test "$gc" = yes; then
fi
# Check for the required Perl dependencies (DBI and DBD::SQLite).
# Check for the required Perl dependencies (DBI, DBD::SQLite and WWW::Curl).
perlFlags="-I$perllibdir"
AC_ARG_WITH(dbi, AC_HELP_STRING([--with-dbi=PATH],
@@ -260,13 +282,24 @@ AC_ARG_WITH(dbd-sqlite, AC_HELP_STRING([--with-dbd-sqlite=PATH],
[prefix of the Perl DBD::SQLite library]),
perlFlags="$perlFlags -I$withval")
AC_ARG_WITH(www-curl, AC_HELP_STRING([--with-www-curl=PATH],
[prefix of the Perl WWW::Curl library]),
perlFlags="$perlFlags -I$withval")
AC_MSG_CHECKING([whether DBD::SQLite works])
if ! $perl $perlFlags -e 'use DBI; use DBD::SQLite;' 2>&5; then
AC_MSG_RESULT(no)
AC_MSG_FAILURE([The Perl modules DBI and/or DBD::SQLite are missing.])
fi
AC_MSG_RESULT(yes)
AC_MSG_CHECKING([whether WWW::Curl works])
if ! $perl $perlFlags -e 'use WWW::Curl;' 2>&5; then
AC_MSG_RESULT(no)
AC_MSG_FAILURE([The Perl module WWW::Curl is missing.])
fi
AC_MSG_RESULT(yes)
AC_SUBST(perlFlags)
@@ -310,10 +343,21 @@ eval dynlib_suffix=$shrext_cmds
AC_SUBST(dynlib_suffix)
# Do we have GNU tar?
AC_MSG_CHECKING([if you have a recent GNU tar])
if $tar --version 2> /dev/null | grep -q GNU && tar cvf /dev/null --warning=no-timestamp ./config.log > /dev/null; then
AC_MSG_RESULT(yes)
tarFlags="--warning=no-timestamp"
else
AC_MSG_RESULT(no)
fi
AC_SUBST(tarFlags)
AM_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile
src/Makefile
src/bin2c/Makefile
src/boost/Makefile
src/boost/format/Makefile
src/libutil/Makefile
@@ -324,7 +368,7 @@ AC_CONFIG_FILES([Makefile
src/libexpr/Makefile
src/nix-instantiate/Makefile
src/nix-env/Makefile
src/nix-worker/Makefile
src/nix-daemon/Makefile
src/nix-setuid-helper/Makefile
src/nix-log2xml/Makefile
src/bsdiff-4.3/Makefile
+2 -1
View File
@@ -1,6 +1,7 @@
all-local: config.nix
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh derivation.nix fetchurl.nix
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix derivation.nix fetchurl.nix \
imported-drv-to-derivation.nix
install-exec-local:
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/corepkgs
+9 -5
View File
@@ -2,18 +2,22 @@ with import <nix/config.nix>;
{ derivations, manifest }:
derivation {
derivation {
name = "user-environment";
system = builtins.currentSystem;
builder = perl;
args = [ "-w" ./buildenv.pl ];
manifest = manifest;
# !!! grmbl, need structured data for passing this in a clean way.
paths = derivations;
active = map (x: if x ? meta && x.meta ? active then x.meta.active else "true") derivations;
priority = map (x: if x ? meta && x.meta ? priority then x.meta.priority else "5") derivations;
derivations =
map (d:
[ (d.meta.active or "true")
(d.meta.priority or 5)
(builtins.length d.outputs)
] ++ map (output: builtins.getAttr output d) d.outputs)
derivations;
# Building user environments remotely just causes huge amounts of
# network traffic, so don't do that.
+25 -23
View File
@@ -43,12 +43,12 @@ sub createLinks {
$srcFile =~ /\/log$/)
{
# Do nothing.
}
}
elsif (-d $srcFile) {
lstat $dstFile;
if (-d _) {
createLinks($srcFile, $dstFile, $priority);
}
@@ -59,7 +59,7 @@ sub createLinks {
die "collission between directory `$srcFile' and non-directory `$target'";
}
unlink $dstFile or die "error unlinking `$dstFile': $!";
mkdir $dstFile, 0755 ||
mkdir $dstFile, 0755 ||
die "error creating directory `$dstFile': $!";
createLinks($target, $dstFile, $priorities{$dstFile});
createLinks($srcFile, $dstFile, $priority);
@@ -78,15 +78,15 @@ sub createLinks {
if (-l $dstFile) {
my $target = readlink $dstFile;
my $prevPriority = $priorities{$dstFile};
die ( "Collission between `$srcFile' and `$target'. "
. "Suggested solution: use `nix-env --set-flag "
die ( "collission between `$srcFile' and `$target'; "
. "use `nix-env --set-flag "
. "priority NUMBER PKGNAME' to change the priority of "
. "one of the conflicting packages.\n" )
. "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;
@@ -125,27 +125,29 @@ sub addPkg {
# Convert the stuff we get from the environment back into a coherent
# data type.
my @paths = split ' ', $ENV{"paths"};
my @active = split ' ', $ENV{"active"};
my @priority = split ' ', $ENV{"priority"};
die if scalar @paths != scalar @active;
die if scalar @paths != scalar @priority;
my %pkgs;
for (my $n = 0; $n < scalar @paths; $n++) {
$pkgs{$paths[$n]} =
{ active => $active[$n]
, priority => $priority[$n] };
my @pkgs;
my @derivations = split ' ', $ENV{"derivations"};
while (scalar @derivations) {
my $active = shift @derivations;
my $priority = shift @derivations;
my $outputs = shift @derivations;
for (my $n = 0; $n < $outputs; $n++) {
my $path = shift @derivations;
push @pkgs,
{ path => $path
, active => $active ne "false"
, priority => int($priority) };
}
}
# Symlink to the packages that have been installed explicitly by the
# user.
foreach my $pkg (sort (keys %pkgs)) {
# user. Process in priority order to reduce unnecessary
# symlink/unlink steps.
@pkgs = sort { $a->{priority} <=> $b->{priority} || $a->{path} cmp $b->{path} } @pkgs;
foreach my $pkg (@pkgs) {
#print $pkg, " ", $pkgs{$pkg}->{priority}, "\n";
addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false";
addPkg($pkg->{path}, $pkg->{priority}) if $pkg->{active};
}
+3 -1
View File
@@ -6,8 +6,10 @@ in {
perl = "@perl@";
shell = "@shell@";
coreutils = "@coreutils@";
bzip2 = fromEnv "NIX_BZIP2" "@bzip2@";
bzip2 = "@bzip2@";
xz = "@xz@";
tar = "@tar@";
tarFlags = "@tarFlags@";
tr = "@tr@";
curl = "@curl@";
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
+3 -3
View File
@@ -6,7 +6,7 @@ drvAttrs @ { outputs ? [ "out" ], ... }:
let
strict = derivationStrict drvAttrs;
commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
{ all = map (x: x.value) outputsList;
inherit drvAttrs;
@@ -21,7 +21,7 @@ let
inherit outputName;
};
};
outputsList = map outputToAttrListElement outputs;
in (builtins.head outputsList).value
+21
View File
@@ -0,0 +1,21 @@
attrs @ { drvPath, outputs, ... }:
let
commonAttrs = (builtins.listToAttrs outputsList) //
{ all = map (x: x.value) outputsList;
inherit drvPath;
type = "derivation";
};
outputToAttrListElement = outputName:
{ name = outputName;
value = commonAttrs // {
outPath = builtins.getAttr outputName attrs;
inherit outputName;
};
};
outputsList = map outputToAttrListElement outputs;
in (builtins.head outputsList).value
+15 -6
View File
@@ -6,28 +6,37 @@ let
''
export PATH=${nixBinDir}:${coreutils}
if [ $compressionType = "xz" ]; then
ext=xz
compressor="${xz} -9"
else
ext=bz2
compressor="${bzip2}"
fi
echo "packing $storePath..."
mkdir $out
dst=$out/tmp.nar.bz2
dst=$out/tmp.nar.$ext
set -o pipefail
nix-store --dump "$storePath" | ${bzip2} > $dst
nix-store --dump "$storePath" | $compressor > $dst
nix-hash --flat --type $hashAlgo --base32 $dst > $out/narbz2-hash
hash=$(nix-hash --flat --type $hashAlgo --base32 $dst)
echo -n $hash > $out/nar-compressed-hash
mv $out/tmp.nar.bz2 $out/$(cat $out/narbz2-hash).nar.bz2
mv $dst $out/$hash.nar.$ext
'';
in
{ storePath, hashAlgo }:
{ storePath, hashAlgo, compressionType }:
derivation {
name = "nar";
system = builtins.currentSystem;
builder = shell;
args = [ "-e" builder ];
inherit storePath hashAlgo;
inherit storePath hashAlgo compressionType;
# Don't build in a chroot because Nix's dependencies may not be there.
__noChroot = true;
+26 -4
View File
@@ -1,14 +1,36 @@
with import <nix/config.nix>;
{ name, channelName, src }:
let
builder = builtins.toFile "unpack-channel.sh"
''
mkdir $out
cd $out
pat="\.xz\$"
if [[ "$src" =~ $pat ]]; then
${xz} -d < $src | ${tar} xf - ${tarFlags}
else
${bzip2} -d < $src | ${tar} xf - ${tarFlags}
fi
mv * $out/$channelName
if [ -n "$binaryCacheURL" ]; then
mkdir $out/binary-caches
echo -n "$binaryCacheURL" > $out/binary-caches/$channelName
fi
'';
in
{ name, channelName, src, binaryCacheURL ? "" }:
derivation {
system = builtins.currentSystem;
builder = shell;
args = [ "-e" ./unpack-channel.sh ];
inherit name channelName src bzip2 tar tr;
args = [ "-e" builder ];
inherit name channelName src binaryCacheURL;
PATH = "${nixBinDir}:${coreutils}";
# No point in doing this remotely.
preferLocalBuild = true;
-4
View File
@@ -1,4 +0,0 @@
mkdir $out
cd $out
$bzip2 -d < $src | $tar xf -
mv * $out/$channelName
+1 -1
View File
@@ -23,7 +23,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
man5_MANS = nix.conf.5
man8_MANS = nix-worker.8
man8_MANS = nix-daemon.8
FIGURES = figures/user-environments.png
+100 -56
View File
@@ -19,7 +19,7 @@ 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
@@ -27,7 +27,7 @@ is also available as <function>builtins.derivation</function>.</para>
</varlistentry>
<varlistentry><term><function>builtins.add</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -37,7 +37,7 @@ is also available as <function>builtins.derivation</function>.</para>
</varlistentry>
<varlistentry><term><function>builtins.attrNames</function>
<replaceable>attrs</replaceable></term>
@@ -55,7 +55,7 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
</varlistentry>
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
<listitem><para>Return the <emphasis>base name</emphasis> of the
@@ -65,7 +65,7 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
</varlistentry>
<varlistentry><term><varname>builtins</varname></term>
<listitem><para>The attribute set <varname>builtins</varname>
@@ -82,7 +82,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.compareVersions</function>
<replaceable>s1</replaceable> <replaceable>s2</replaceable></term>
@@ -99,7 +99,16 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.concatLists</function>
<replaceable>lists</replaceable></term>
<listitem><para>Concatenate a list of lists into a single
list.</para></listitem>
</varlistentry>
<varlistentry
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
@@ -124,7 +133,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
-->
<!--
<varlistentry><term><function>dependencyClosure</function></term>
@@ -133,7 +142,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
-->
<varlistentry><term><function>derivation</function>
<replaceable>attrs</replaceable></term>
@@ -152,7 +161,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.div</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -162,7 +171,40 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.elem</function>
<replaceable>x</replaceable> <replaceable>xs</replaceable></term>
<listitem><para>Return <literal>true</literal> if a value equal to
<replaceable>x</replaceable> occurs in the list
<replaceable>xs</replaceable>, and <literal>false</literal>
otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.elemAt</function>
<replaceable>xs</replaceable> <replaceable>n</replaceable></term>
<listitem><para>Return element <replaceable>n</replaceable> from
the list <replaceable>xs</replaceable>. Elements are counted
starting from 0. A fatal error occurs in the index is out of
bounds.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.filter</function>
<replaceable>f</replaceable> <replaceable>xs</replaceable></term>
<listitem><para>Return a list consisting of the elements of
<replaceable>xs</replaceable> for which the function
<replaceable>f</replaceable> returns
<literal>true</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.filterSource</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -214,7 +256,7 @@ stdenv.mkDerivation {
</varlistentry>
<varlistentry><term><function>builtins.getAttr</function>
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
@@ -227,7 +269,7 @@ stdenv.mkDerivation {
</varlistentry>
<varlistentry><term><function>builtins.getEnv</function>
<replaceable>s</replaceable></term>
@@ -245,7 +287,7 @@ stdenv.mkDerivation {
</varlistentry>
<varlistentry><term><function>builtins.hasAttr</function>
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
@@ -259,7 +301,7 @@ stdenv.mkDerivation {
</varlistentry>
<varlistentry><term><function>builtins.head</function>
<replaceable>list</replaceable></term>
@@ -270,13 +312,15 @@ stdenv.mkDerivation {
</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
file <replaceable>path</replaceable>. If <replaceable>path
</replaceable> is a directory, the file <filename>default.nix
</filename> in that directory is loaded. Evaluation aborts if
the file doesnt exist or contains an incorrect Nix
expression. <function>import</function> implements Nixs module
system: you can put any Nix expression (such as an attribute set
or a function) in a separate file, and use it from Nix expressions
@@ -288,7 +332,7 @@ stdenv.mkDerivation {
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;
@@ -323,7 +367,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.intersectAttrs</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -332,8 +376,8 @@ x: x + 456</programlisting>
exist in the set <replaceable>e1</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.isAttrs</function>
<replaceable>e</replaceable></term>
@@ -343,7 +387,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isList</function>
<replaceable>e</replaceable></term>
@@ -353,7 +397,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isFunction</function>
<replaceable>e</replaceable></term>
@@ -363,7 +407,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isString</function>
<replaceable>e</replaceable></term>
@@ -373,7 +417,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isInt</function>
<replaceable>e</replaceable></term>
@@ -383,7 +427,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.isBool</function>
<replaceable>e</replaceable></term>
@@ -393,7 +437,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>isNull</function>
<replaceable>e</replaceable></term>
@@ -403,12 +447,12 @@ x: x + 456</programlisting>
<warning><para>This function is <emphasis>deprecated</emphasis>;
just write <literal>e == null</literal> instead.</para></warning>
</listitem>
</varlistentry>
<varlistentry><term><function>builtins.length</function>
<replaceable>e</replaceable></term>
@@ -417,7 +461,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.lessThan</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -430,7 +474,7 @@ x: x + 456</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.listToAttrs</function>
<replaceable>e</replaceable></term>
@@ -457,7 +501,7 @@ builtins.listToAttrs
</para></listitem>
</varlistentry>
<varlistentry><term><function>map</function>
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
@@ -470,10 +514,10 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
evaluates to <literal>[ "foobar" "foobla" "fooabc"
]</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.mul</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -483,7 +527,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.parseDrvName</function>
<replaceable>s</replaceable></term>
@@ -498,7 +542,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.pathExists</function>
<replaceable>path</replaceable></term>
@@ -531,7 +575,7 @@ in config.someSetting</programlisting>
</varlistentry>
-->
<varlistentry><term><function>builtins.readFile</function>
<replaceable>path</replaceable></term>
@@ -539,8 +583,8 @@ in config.someSetting</programlisting>
<replaceable>path</replaceable> as a string.</para></listitem>
</varlistentry>
<varlistentry><term><function>removeAttrs</function>
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
@@ -556,7 +600,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
</varlistentry>
<varlistentry><term><function>builtins.stringLength</function>
<replaceable>e</replaceable></term>
@@ -566,7 +610,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
</varlistentry>
<varlistentry><term><function>builtins.sub</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -576,7 +620,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
</varlistentry>
<varlistentry><term><function>builtins.substring</function>
<replaceable>start</replaceable> <replaceable>len</replaceable>
<replaceable>s</replaceable></term>
@@ -594,7 +638,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
</varlistentry>
<varlistentry><term><function>builtins.tail</function>
<replaceable>list</replaceable></term>
@@ -604,7 +648,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
</varlistentry>
<varlistentry><term><function>throw</function>
<replaceable>s</replaceable></term>
@@ -618,7 +662,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
</varlistentry>
<varlistentry
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
@@ -636,7 +680,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
stdenv.mkDerivation {
name = "hello-2.1.1";
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
@@ -655,7 +699,7 @@ stdenv.mkDerivation {
};
inherit perl;
}</programlisting>
</para>
<para>It is even possible for one file to refer to another, e.g.,
@@ -693,7 +737,7 @@ in foo</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
<listitem><para>Convert the string value
@@ -706,7 +750,7 @@ in foo</programlisting>
</varlistentry>
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
<listitem><para>Convert the expression
@@ -718,7 +762,7 @@ in foo</programlisting>
</varlistentry>
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
<listitem><para>Return a string containing an XML representation
@@ -763,7 +807,7 @@ in foo</programlisting>
<example xml:id='ex-toxml'><title>Passing information to a builder
using <function>toXML</function></title>
<programlisting><![CDATA[
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
@@ -771,7 +815,7 @@ stdenv.mkDerivation (rec {
name = "web-server";
buildInputs = [ libxslt ];
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
mkdir $out
@@ -804,7 +848,7 @@ stdenv.mkDerivation (rec {
<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>
@@ -833,7 +877,7 @@ stdenv.mkDerivation (rec {
</varlistentry>
<varlistentry><term><function>builtins.trace</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -844,7 +888,7 @@ stdenv.mkDerivation (rec {
</varlistentry>
</variablelist>
+95 -5
View File
@@ -30,6 +30,9 @@ gc-keep-derivations = true # Idem
env-keep-derivations = false
</programlisting>
<para>You can override settings using the <option>--option</option>
flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
<para>The following settings are currently available:
<variablelist>
@@ -198,10 +201,8 @@ env-keep-derivations = false
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>
<literal>daemon</literal>). Obviously, this should not be used in
multi-user settings with untrusted users.</para>
</listitem>
@@ -243,6 +244,16 @@ env-keep-derivations = false
</varlistentry>
<varlistentry><term><literal>build-fallback</literal></term>
<listitem><para>If set to <literal>true</literal>, Nix will fall
back to building from source if a binary substitute fails. This
is equivalent to the <option>--fallback</option> flag. The
default is <literal>false</literal>.</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,
@@ -307,6 +318,72 @@ build-use-chroot = /dev /proc /bin</programlisting>
</varlistentry>
<varlistentry><term><literal>use-binary-caches</literal></term>
<listitem><para>If set to <literal>true</literal> (the default),
Nix will check the binary caches specified by
<option>binary-caches</option> and related options to obtain
binary substitutes.</para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-caches</literal></term>
<listitem><para>A list of URLs of binary caches, separated by
whitespace. The default is empty.<!-- The default is
<literal>http://nixos.org/binary-cache</literal>. --></para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-caches-files</literal></term>
<listitem><para>A list of names of files that will be read to
obtain additional binary cache URLs. The default is
<literal>/nix/var/nix/profiles/per-user/root/channels/binary-caches/*</literal>,
which ensures that Nix will use the binary caches corresponding to
the channels installed by root. Do not set this option to read
files created by untrusted users!</para></listitem>
</varlistentry>
<varlistentry><term><literal>trusted-binary-caches</literal></term>
<listitem><para>A list of URLs of binary caches, separated by
whitespace. These are not used by default, but can be enabled by
users of the Nix daemon by specifying <literal>--option
binary-caches <replaceable>urls</replaceable></literal> on the
command line. Daemon users are only allowed to pass a subset of
the URLs listed in <literal>binary-caches</literal> and
<literal>trusted-binary-caches</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
<listitem><para>The maximum number of parallel HTTP connections
used by the binary cache substituter to get NAR info files. This
number should be high to minimise latency. It defaults to
150.</para></listitem>
</varlistentry>
<varlistentry><term><literal>force-manifest</literal></term>
<listitem><para>If this option is set to <literal>false</literal>
(default) and a Nix channel provides both a manifest and a binary
cache, only the binary cache will be used. If set to
<literal>true</literal>, the manifest will be fetched as well.
This is useful if you want to use binary patches (which are
currently not supported by binary caches).</para></listitem>
</varlistentry>
<varlistentry><term><literal>system</literal></term>
<listitem><para>This option specifies the canonical Nix system
@@ -337,7 +414,20 @@ build-use-chroot = /dev /proc /bin</programlisting>
<literal>true</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>auto-optimise-store</literal></term>
<listitem><para>If set to <literal>true</literal> (the default),
Nix automatically detects files in the store that have identical
contents, and replaces them with hard links to a single copy.
This saves disk space. If set to <literal>false</literal>, you
can still run <command>nix-store --optimise</command> to get rid
of duplicate files.</para></listitem>
</varlistentry>
</variablelist>
</para>
+2 -2
View File
@@ -304,9 +304,9 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
<literal>/var/run/nix/remote-stores</literal>.</para>
<para>Note that if youre building through the <link
linkend="sec-nix-worker">Nix daemon</link>, the only setting for
linkend="sec-nix-daemon">Nix daemon</link>, the only setting for
this variable that matters is the one that the
<command>nix-worker</command> process uses. So if you want to
<command>nix-daemon</command> process uses. So if you want to
change it, you have to restart the daemon.</para></listitem>
</varlistentry>
+12
View File
@@ -160,6 +160,18 @@
</glossentry>
<glossentry xml:id="gloss-nar"><glossterm>NAR</glossterm>
<glossdef><para>A <emphasis>N</emphasis>ix
<emphasis>AR</emphasis>chive. This is a serialisation of a path in
the Nix store. It can contain regular files, directories and
symbolic links. NARs are generated and unpacked using
<command>nix-store --dump</command> and <command>nix-store
--restore</command>.</para></glossdef>
</glossentry>
</glosslist>
+9 -6
View File
@@ -116,6 +116,8 @@ a source distribution.</para>
<itemizedlist>
<listitem><para>GNU Make.</para></listitem>
<listitem><para>A fairly recent version of GCC/G++. Version 2.95
and higher should work. Clang will also work.</para></listitem>
@@ -217,7 +219,8 @@ $ ./configure <replaceable>options...</replaceable>
$ make
$ make install</screen>
</para>
Nix requires GNU Make so you may need to invoke
<command>gmake</command> instead.</para>
<para>When building from the Git repository, these should be preceded
by the command:
@@ -409,11 +412,11 @@ $ chown -R root /nix/store /nix/var/nix</screen>
</para>
<para>The <link linkend="sec-nix-worker">Nix daemon</link> should be
<para>The <link linkend="sec-nix-daemon">Nix daemon</link> should be
started as follows (as <literal>root</literal>):
<screen>
$ nix-worker --daemon</screen>
$ nix-daemon</screen>
Youll want to put that line somewhere in your systems boot
scripts.</para>
@@ -447,11 +450,11 @@ named anything. It should own the Nix store and database:
<screen>
$ chown -R nix /nix/store /nix/var/nix</screen>
and of course <command>nix-worker --daemon</command> should be started
under that user, e.g.,
and of course <command>nix-daemon</command> should be started under
that user, e.g.,
<screen>
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
$ su - nix -c "exec /nix/bin/nix-daemon"</screen>
</para>
+1 -1
View File
@@ -59,7 +59,7 @@
<xi:include href="nix-prefetch-url.xml" />
<xi:include href="nix-pull.xml" />
<xi:include href="nix-push.xml" />
<xi:include href="nix-worker.xml" />
<xi:include href="nix-daemon.xml" />
</section>
<section>
+27 -8
View File
@@ -96,7 +96,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<variablelist>
<varlistentry><term><option>--drv-link</option> <replaceable>drvlink</replaceable></term>
<listitem><para>Add a symlink named
<replaceable>drvlink</replaceable> to the store derivation
produced by <command>nix-instantiate</command>. The derivation is
@@ -108,14 +108,14 @@ also <xref linkend="sec-common-options" />.</phrase></para>
</varlistentry>
<varlistentry><term><option>--add-drv-link</option></term>
<listitem><para>Shorthand for <option>--drv-link</option>
<filename>./derivation</filename>.</para></listitem>
</varlistentry>
<varlistentry><term><option>--no-out-link</option></term>
<listitem><para>Do not create a symlink to the output path. Note
that as a result the output does not become a root of the garbage
collector, and so might be deleted by <command>nix-store
@@ -125,7 +125,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<varlistentry xml:id='opt-out-link'><term><option>--out-link</option> /
<option>-o</option> <replaceable>outlink</replaceable></term>
<listitem><para>Change the name of the symlink to the output path
created from <filename>result</filename> to
<replaceable>outlink</replaceable>.</para></listitem>
@@ -143,7 +143,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<variablelist>
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
<listitem><para>In the environment of the derivation, executeq the
command <replaceable>cmd</replaceable> instead of the default
interactive shell.</para></listitem>
@@ -151,7 +151,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
</varlistentry>
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
<listitem><para>Do not build any dependencies whose store path
matches the regular expression <replaceable>regexp</replaceable>.
This option may be specified multiple times.</para></listitem>
@@ -190,6 +190,25 @@ $ ./pan/gui/pan
</para>
<para>If a derivation has multiple outputs,
<command>nix-build</command> will build the default (first) output.
You can also build all outputs:
<screen>
$ nix-build '&lt;nixpkgs>' -A openssl.all
</screen>
This will create a symlink for each output named
<filename>result-<replaceable>outputname</replaceable></filename>.
The suffix is omitted if the output name is <literal>out</literal>.
So if <literal>openssl</literal> has outputs <literal>out</literal>,
<literal>bin</literal> and <literal>man</literal>,
<command>nix-build</command> will create symlinks
<literal>result</literal>, <literal>result-bin</literal> and
<literal>result-man</literal>. Its also possible to build a specific
output:
<screen>
$ nix-build '&lt;nixpkgs>' -A openssl.man
</screen>
This will create a symlink <literal>result-man</literal>.</para>
</refsection>
@@ -201,6 +220,6 @@ $ ./pan/gui/pan
</variablelist>
</refsection>
</refentry>
+1 -1
View File
@@ -103,7 +103,7 @@ respectively.</para>
<para>To subscribe to the Nixpkgs channel and install the GNU Hello package:</para>
<screen>
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable
$ nix-channel --update
$ nix-env -iA nixpkgs.hello</screen>
+1
View File
@@ -26,6 +26,7 @@
<arg choice='plain'><option>--print-dead</option></arg>
<arg choice='plain'><option>--delete</option></arg>
</group>
<arg><option>--dry-run</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
+20 -6
View File
@@ -28,6 +28,8 @@
<arg><option>--xz</option></arg>
<arg><option>--show-progress</option></arg>
<arg><option>--include-outputs</option></arg>
<arg><option>--use-substitutes</option></arg>
<arg><option>-s</option></arg>
<arg choice='plain'>
<replaceable>user@</replaceable><replaceable>machine</replaceable>
</arg>
@@ -65,7 +67,7 @@ those paths. If this bothers you, use
<refsection><title>Options</title>
<variablelist>
<varlistentry><term><option>--to</option></term>
<listitem><para>Copy the closure of
@@ -119,10 +121,22 @@ those paths. If this bothers you, use
</varlistentry>
<varlistentry><term><option>--include-outputs</option></term>
<listitem><para>Also copy the outputs of store derivations included
in the closure.</para></listitem>
<listitem><para>Also copy the outputs of store derivations
included in the closure.</para></listitem>
</varlistentry>
<varlistentry><term><option>--use-substitutes</option> / <option>-s</option></term>
<listitem><para>Attempt to download missing paths on the target
machine using Nixs substitute mechanism. Any paths that cannot
be substituted on the target are still copied normally from the
source. This is useful, for instance, if the connection between
the source and target machine is slow, but the connection between
the target machine and <literal>nixos.org</literal> (the default
binary cache server) is fast.</para></listitem>
</varlistentry>
</variablelist>
@@ -140,7 +154,7 @@ those paths. If this bothers you, use
<command>ssh</command> on the command line.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
@@ -1,24 +1,23 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xml:id="sec-nix-worker">
xml:id="sec-nix-daemon">
<refmeta>
<refentrytitle>nix-worker</refentrytitle>
<refentrytitle>nix-daemon</refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">Nix</refmiscinfo>
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
</refmeta>
<refnamediv>
<refname>nix-worker</refname>
<refname>nix-daemon</refname>
<refpurpose>Nix multi-user support daemon</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-worker</command>
<arg choice="plain"><option>--daemon</option></arg>
<command>nix-daemon</command>
</cmdsynopsis>
</refsynopsisdiv>
+2 -1
View File
@@ -105,7 +105,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<listitem><para>For the <option>--install</option>,
<option>--upgrade</option>, <option>--uninstall</option>,
<option>--switch-generation</option> and
<option>--switch-generation</option>,
<option>--delete-generations</option> and
<option>--rollback</option> operations, this flag will cause
<command>nix-env</command> to print what
<emphasis>would</emphasis> be done if this flag had not been
+3 -2
View File
@@ -91,8 +91,9 @@ cryptographic hash as <literal>nix-store --dump
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
<listitem><para>Specify a cryptographic hash, which can be one of
<literal>md5</literal>, <literal>sha1</literal>, and
<listitem><para>Use the specified cryptographic hash algorithm,
which can be one of <literal>md5</literal>,
<literal>sha1</literal>, and
<literal>sha256</literal>.</para></listitem>
</varlistentry>
+19 -5
View File
@@ -18,6 +18,7 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-prefetch-url</command>
<arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
<arg choice='plain'><replaceable>url</replaceable></arg>
<arg><replaceable>hash</replaceable></arg>
</cmdsynopsis>
@@ -45,11 +46,6 @@ download it again when you build your Nix expression. Since
as <command>nix-prefetch-url</command>, the redundant download can be
avoided.</para>
<para>The environment variable <envar>NIX_HASH_ALGO</envar> specifies
which hash algorithm to use. It can be either <literal>md5</literal>,
<literal>sha1</literal>, or <literal>sha256</literal>. The default is
<literal>sha256</literal>.</para>
<para>If <replaceable>hash</replaceable> is specified, then a download
is not performed if the Nix store already contains a file with the
same hash and base name. Otherwise, the file is downloaded, and an
@@ -63,6 +59,24 @@ of the downloaded file in the Nix store is also printed.</para>
</refsection>
<refsection><title>Options</title>
<variablelist>
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
<listitem><para>Use the specified cryptographic hash algorithm,
which can be one of <literal>md5</literal>,
<literal>sha1</literal>, and
<literal>sha256</literal>.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Examples</title>
<screen>
+338 -76
View File
@@ -12,24 +12,19 @@
<refnamediv>
<refname>nix-push</refname>
<refpurpose>push store paths onto a network cache</refpurpose>
<refpurpose>generate a binary cache</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nix-push</command>
<group choice='req'>
<arg choice='req'>
<arg choice='plain'><replaceable>archivesPutURL</replaceable></arg>
<arg choice='plain'><replaceable>archivesGetURL</replaceable></arg>
<arg choice='plain'><replaceable>manifestPutURL</replaceable></arg>
</arg>
<arg choice='req'>
<arg choice='plain'><option>--copy</option></arg>
<arg choice='plain'><replaceable>archivesDir</replaceable></arg>
<arg choice='plain'><replaceable>manifestFile</replaceable></arg>
</arg>
</group>
<arg choice='plain'><option>--dest</option> <replaceable>dest-dir</replaceable></arg>
<arg><option>--bzip2</option></arg>
<arg><option>--force</option></arg>
<arg><option>--link</option></arg>
<arg><option>--manifest</option></arg>
<arg><option>--manifest-path</option> <replaceable>filename</replaceable></arg>
<arg><option>--url-prefix</option> <replaceable>url</replaceable></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -37,93 +32,360 @@
<refsection><title>Description</title>
<para>The command <command>nix-push</command> builds a set of store
paths (if necessary), and then packages and uploads all store paths in
the resulting closures to a server. A network cache thus populated
can subsequently be used to speed up software deployment on other
machines using the <command>nix-pull</command> command.</para>
<para>The command <command>nix-push</command> produces a
<emphasis>binary cache</emphasis>, a directory containing compressed
Nix archives (NARs) plus some metadata of the closure of the specified
store paths. This directory can then be made available through a web
server to other Nix installations, allowing them to skip building from
source and instead download binaries from the cache
automatically.</para>
<para><command>nix-push</command> performs the following actions.
<orderedlist>
<listitem><para>Each path in <replaceable>paths</replaceable> is
realised (using <link
linkend='rsec-nix-store-realise'><literal>nix-store
--realise</literal></link>).</para></listitem>
built (using <link
linkend='rsec-nix-store-realise'><command>nix-store
--realise</command></link>).</para></listitem>
<listitem><para>All paths in the closure of the store expressions
stored in <replaceable>paths</replaceable> are determined (using
<literal>nix-store --query --requisites
--include-outputs</literal>). It should be noted that since the
<option>--include-outputs</option> flag is used, you get a combined
source/binary distribution.</para></listitem>
<listitem><para>All paths in the closure of
<replaceable>paths</replaceable> are determined (using
<command>nix-store --query --requisites
--include-outputs</command>). Note that since the
<option>--include-outputs</option> flag is used, if
<replaceable>paths</replaceable> includes a store derivation, you
get a combined source/binary distribution (e.g., source tarballs
will be included).</para></listitem>
<listitem><para>All store paths determined in the previous step are
packaged and compressed into a <command>bzip</command>ped NAR
archive (extension <filename>.nar.bz2</filename>).</para></listitem>
packaged into a NAR (using <command>nix-store --dump</command>) and
compressed using <command>xz</command> or <command>bzip2</command>.
The resulting files have the extension <filename>.nar.xz</filename>
or <filename>.nar.bz2</filename>. Also for each store path, Nix
generates a file with extension <filename>.narinfo</filename>
containing metadata such as the references, cryptographic hash and
size of each path.</para></listitem>
<listitem><para>A <emphasis>manifest</emphasis> is created that
contains information on the store paths, their eventual URLs in the
cache, and cryptographic hashes of the contents of the NAR
archives.</para></listitem>
<listitem><para>Optionally, a single <emphasis>manifest</emphasis>
file is created that contains the same metadata as the
<filename>.narinfo</filename> files. This is for compatibility with
Nix versions prior to 1.2 (see <command>nix-pull</command> for
details).</para></listitem>
<listitem><para>Each store path is uploaded to the remote directory
specified by <replaceable>archivesPutURL</replaceable>. HTTP PUT
requests are used to do this. However, before a file
<varname>x</varname> is uploaded to
<literal><replaceable>archivesPutURL</replaceable>/</literal><varname>x</varname>,
<command>nix-push</command> first determines whether this upload is
unnecessary by issuing a HTTP HEAD request on
<literal><replaceable>archivesGetURL</replaceable>/</literal><varname>x</varname>.
This allows a cache to be shared between many partially overlapping
<command>nix-push</command> invocations. (We use two URLs because
the upload URL typically refers to a CGI script, while the download
URL just refers to a file system directory on the
server.)</para></listitem>
<listitem><para>A file named <option>nix-cache-info</option> is
placed in the destination directory. The existence of this file
marks the directory as a binary cache.</para></listitem>
<listitem><para>The manifest is uploaded using an HTTP PUT request
to <replaceable>manifestPutURL</replaceable>. The corresponding
URL to download the manifest can then be used by
<command>nix-pull</command>.</para></listitem>
</orderedlist>
</para>
<!--
<para>TODO: <option>- -copy</option></para>
-->
</refsection>
<refsection><title>Options</title>
<variablelist>
<varlistentry><term><option>--dest</option> <replaceable>dest-dir</replaceable></term>
<listitem><para>Set the destination directory to
<replaceable>dir</replaceable>, which is created if it does not
exist. This flag is required.</para></listitem>
</varlistentry>
<varlistentry><term><option>--bzip2</option></term>
<listitem><para>Compress NARs using <command>bzip2</command>
instead of <command>xz -9</command>. The latter compresses about
30% better on typical archives, decompresses about twice as fast,
but compresses a lot slower and is not supported by Nix prior to
version 1.2.</para></listitem>
</varlistentry>
<varlistentry><term><option>--force</option></term>
<listitem><para>Overwrite <filename>.narinfo</filename> files if
they already exist.</para></listitem>
</varlistentry>
<varlistentry><term><option>--link</option></term>
<listitem><para>By default, NARs are generated in the Nix store
and then copied to <replaceable>dest-dir</replaceable>. If this
option is given, hard links are used instead. This only works if
<replaceable>dest-dir</replaceable> is on the same filesystem as
the Nix store.</para></listitem>
</varlistentry>
<varlistentry><term><option>--manifest</option></term>
<listitem><para>Force the generation of a manifest suitable for
use by <command>nix-pull</command>. The manifest is stored as
<filename><replaceable>dest-dir</replaceable>/MANIFEST</filename>.</para></listitem>
</varlistentry>
<varlistentry><term><option>--manifest-path</option> <replaceable>filename</replaceable></term>
<listitem><para>Like <option>--manifest</option>, but store the
manifest in <replaceable>filename</replaceable>.</para></listitem>
</varlistentry>
<varlistentry><term><option>--url-prefix</option> <replaceable>url</replaceable></term>
<listitem><para>Manifests are expected to contain the absolute
URLs of NARs. For generating these URLs, the prefix
<replaceable>url</replaceable> is used. It defaults to
<uri>file://<replaceable>dest-dir</replaceable></uri>.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Examples</title>
<para>To upload files there typically is some CGI script on the server
side. This script should be be protected with a password. The
following example uploads the store paths resulting from building the
Nix expressions in <filename>foo.nix</filename>, passing appropriate
authentication information:
<screen>
$ nix-push \
http://foo@bar:server.domain/cgi-bin/upload.pl/cache \
http://server.domain/cache \
http://foo@bar:server.domain/cgi-bin/upload.pl/MANIFEST \
$(nix-instantiate foo.nix)</screen>
<para>To add the closure of Thunderbird to a binary cache:
This will push both sources and binaries (and any build-time
dependencies used in the build, such as compilers).</para>
<para>If we just want to push binaries, not sources and build-time
dependencies, we can do:
<screen>
$ nix-push <replaceable>urls</replaceable> $(nix-store -r $(nix-instantiate foo.nix))</screen>
$ nix-push --dest /tmp/cache $(nix-build -A thunderbird)
</screen>
Assuming that <filename>/tmp/cache</filename> is exported by a web
server as <uri>http://example.org/cache</uri>, you can then use this
cache on another machine to speed up the installation of Thunderbird:
<screen>
$ nix-build -A thunderbird --option binary-caches http://example.org/cache
</screen>
Alternatively, you could add <literal>binary-caches =
http://example.org/cache</literal> to
<filename>nix.conf</filename>.</para>
<para>To also include build-time dependencies (such as source
tarballs):
<screen>
$ nix-push --dest /tmp/cache $(nix-instantiate -A thunderbird)
</screen>
</para>
<para>To generate a manifest suitable for <command>nix-pull</command>:
<screen>
$ nix-push --dest /tmp/cache $(nix-build -A thunderbird) --manifest
</screen>
On another machine you can then do:
<screen>
$ nix-pull http://example.org/cache
</screen>
to cause the binaries to be used by subsequent Nix operations.</para>
</refsection>
<refsection><title>Binary cache format and operation</title>
<para>A binary cache with URL <replaceable>url</replaceable> only
denotes a valid binary cache if the file
<uri><replaceable>url</replaceable>/nix-cache-info</uri> exists. If
this file does not exist (or cannot be downloaded), the cache is
ignored. If it does exist, it must be a text file containing cache
properties. Heres an example:
<screen>
StoreDir: /nix/store
WantMassQuery: 1
Priority: 10
</screen>
The properties that are currently supported are:
<variablelist>
<varlistentry><term><literal>StoreDir</literal></term>
<listitem><para>The path of the Nix store to which this binary
cache applies. Binaries are not relocatable — a binary built for
<filename>/nix/store</filename> wont generally work in
<filename>/home/alice/store</filename> — so to prevent binaries
from being used in a wrong store, a binary cache is only used if
its <literal>StoreDir</literal> matches the local Nix
configuration. The default is
<filename>/nix/store</filename>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>WantMassQuery</literal></term>
<listitem><para>Query operations such as <command>nix-env
-qas</command> can cause thousands of cache queries, and thus
thousands of HTTP requests, to determine which packages are
available in binary form. While these requests are small, not
every server may appreciate a potential onslaught of queries. If
<literal>WantMassQuery</literal> is set to <literal>0</literal>
(default), “mass queries” such as <command>nix-env -qas</command>
will skip this cache. Thus a package may appear not to have a
binary substitute. However, the binary will still be used when
you actually install the package. If
<literal>WantMassQuery</literal> is set to <literal>1</literal>,
mass queries will use this cache.</para></listitem>
</varlistentry>
<varlistentry><term><literal>Priority</literal></term>
<listitem><para>Each binary cache has a priority (defaulting to
50). Binary caches are checked for binaries in order of ascending
priority; thus a higher number denotes a lower priority. The
binary cache <uri>http://nixos.org/binary-cache</uri> has priority
40.</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>Every time Nix needs to build some store path
<replaceable>p</replaceable>, it will check each configured binary
cache to see if it has a NAR file for <replaceable>p</replaceable>,
until it finds one. If no cache has a NAR, Nix will fall back to
building the path from source (if applicable). To see if a cache with
URL <replaceable>url</replaceable> has a binary for
<replaceable>p</replaceable>, Nix fetches
<replaceable>url/h</replaceable>, where <replaceable>h</replaceable>
is the hash part of <replaceable>p</replaceable>. Thus, if we have a
cache <uri>http://nixos.org/binary-cache</uri> and we want to obtain
the store path
<screen>
/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
</screen>
then Nix will attempt to fetch
<screen>
http://nixos.org/binary-cache/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
</screen>
(Commands such as <command>nix-env -qas</command> will issue an HTTP
HEAD request, since it only needs to know if the
<filename>.narinfo</filename> file exists.) The
<filename>.narinfo</filename> file is a simple text file that looks
like this:
<screen>
StorePath: /nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
URL: nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
Compression: bzip2
FileHash: sha256:0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70
FileSize: 24473768
NarHash: sha256:0s491y1h9hxj5ghiizlxk7ax6jwbha00zwn7lpyd5xg5bhf60vzg
NarSize: 109521136
References: 2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16 ...
Deriver: 7akyyc87ka32xwmqza9dvyg5pwx3j212-glibc-2.7.drv
</screen>
The fields are as follows:
<variablelist>
<varlistentry><term><literal>StorePath</literal></term>
<listitem><para>The full store path, including the name part
(e.g., <literal>glibc-2.7</literal>). It must match the
requested store path.</para></listitem>
</varlistentry>
<varlistentry><term><literal>URL</literal></term>
<listitem><para>The URL of the NAR, relative to the binary cache
URL.</para></listitem>
</varlistentry>
<varlistentry><term><literal>Compression</literal></term>
<listitem><para>The compression method; either
<literal>xz</literal> or
<literal>bzip2</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>FileHash</literal></term>
<listitem><para>The SHA-256 hash of the compressed
NAR.</para></listitem>
</varlistentry>
<varlistentry><term><literal>FileSize</literal></term>
<listitem><para>The size of the compressed NAR.</para></listitem>
</varlistentry>
<varlistentry><term><literal>NarHash</literal></term>
<listitem><para>The SHA-256 hash of the uncompressed NAR. This is
equal to the hash of the store path as returned by
<command>nix-store -q --hash
<replaceable>p</replaceable></command>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>NarSize</literal></term>
<listitem><para>The size of the uncompressed NAR.</para></listitem>
</varlistentry>
<varlistentry><term><literal>References</literal></term>
<listitem><para>The references of the store path, without the Nix
store prefix.</para></listitem>
</varlistentry>
<varlistentry><term><literal>Deriver</literal></term>
<listitem><para>The deriver of the store path, without the Nix
store prefix. This field is optional.</para></listitem>
</varlistentry>
<varlistentry><term><literal>System</literal></term>
<listitem><para>The Nix platform type of this binary, if known.
This field is optional.</para></listitem>
</varlistentry>
</variablelist>
</para>
<para>Thus, in our example, after recursively ensuring that the
references exist (e.g.,
<filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>),
Nix will fetch <screen>
http://nixos.org/binary-cache/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
</screen> and decompress and unpack it to
<filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para>
</refsection>
</refentry>
+80 -7
View File
@@ -174,10 +174,26 @@ the specified store paths. Realisation is a somewhat overloaded term:
output. (For non-derivations argument, the argument itself is
printed.)</para>
<para>If the <option>--dry-run</option> option is used, then
<command>nix-store</command> will print on standard error a
description of what packages would be built or downloaded, and then
quit.</para>
<para>The following flags are available:</para>
<variablelist>
<varlistentry><term><option>--dry-run</option></term>
<listitem><para>Print on standard error a description of what
packages would be built or downloaded, without actually performing
the operation.</para></listitem>
</varlistentry>
<varlistentry><term><option>--ignore-unknown</option></term>
<listitem><para>If a non-derivation path does not have a
substitute, then silently ignore it.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
@@ -773,6 +789,7 @@ $ nix-store --add ./foo.c
<command>nix-store</command>
<arg choice='plain'><option>--verify</option></arg>
<arg><option>--check-contents</option></arg>
<arg><option>--repair</option></arg>
</cmdsynopsis>
</refsection>
@@ -785,7 +802,7 @@ automatically repaired. Inconsistencies are generally the result of
the Nix store or database being modified by non-Nix tools, or of bugs
in Nix itself.</para>
<para>There is one option:
<para>This operation has the following options:
<variablelist>
@@ -800,6 +817,16 @@ in Nix itself.</para>
</varlistentry>
<varlistentry><term><option>--repair</option></term>
<listitem><para>If any valid path is missing from the store, or
(if <option>--check-contents</option> is given) the contents of a
valid path has been modified, then try to repair the path by
redownloading it. See <command>nix-store --repair-path</command>
for details.</para></listitem>
</varlistentry>
</variablelist>
</para>
@@ -848,6 +875,52 @@ $ nix-store --verify-path $(nix-store -qR $(which svn))
</refsection>
<!--######################################################################-->
<refsection><title>Operation <option>--repair-path</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'><option>--repair-path</option></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The operation <option>--repair-path</option> attempts to
“repair” the specified paths by redownloading them using the available
substituters. If no substitutes are available, then repair is not
possible.</para>
<warning><para>During repair, there is a very small time window during
which the old path (if it exists) is moved out of the way and replaced
with the new path. If repair is interrupted in between, then the
system may be left in a broken state (e.g., if the path contains a
critical system component like the GNU C Library).</para></warning>
</refsection>
<refsection><title>Example</title>
<screen>
$ nix-store --verify-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
path `/nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13' was modified!
expected hash `2db57715ae90b7e31ff1f2ecb8c12ec1cc43da920efcbe3b22763f36a1861588',
got `481c5aa5483ebc97c20457bb8bca24deea56550d3985cda0027f67fe54b808e4'
$ nix-store --repair-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
fetching path `/nix/store/d7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13'...
</screen>
</refsection>
</refsection>
<!--######################################################################-->
<refsection xml:id='refsec-nix-store-dump'><title>Operation <option>--dump</option></title>
@@ -1246,8 +1319,8 @@ paths.</para>
<refsection><title>Example</title>
<screen>
$ nix-store --clear-failed-path /nix/store/000zi5dcla86l92jn1g997jb06sidm7x-perl-PerlMagick-6.59
$ nix-store --clear-failed-path *
$ nix-store --clear-failed-paths /nix/store/000zi5dcla86l92jn1g997jb06sidm7x-perl-PerlMagick-6.59
$ nix-store --clear-failed-paths *
</screen>
</refsection>
+5
View File
@@ -37,6 +37,11 @@
<option>-I</option>
<replaceable>path</replaceable>
</arg>
<arg>
<option>--option</option>
<replaceable>name</replaceable>
<replaceable>value</replaceable>
</arg>
<sbr />
</nop>
+22 -1
View File
@@ -337,7 +337,7 @@
<listitem><para>Causes Nix to print out a stack trace in case of Nix
expression evaluation errors.</para></listitem>
</varlistentry>
@@ -351,6 +351,27 @@
</varlistentry>
<varlistentry><term><option>--option</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
<listitem><para>Set the Nix configuration option
<replaceable>name</replaceable> to <replaceable>value</replaceable>.
This overrides settings in the Nix configuration file (see
<citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para></listitem>
</varlistentry>
<varlistentry><term><option>--repair</option></term>
<listitem><para>Fix corrupted or missing store paths by
redownloading or rebuilding them. Note that this is slow because it
requires computing a cryptographic hash of the contents of every
path in the closure of the build. Also note the warning under
<command>nix-store --repair-path</command>.</para></listitem>
</varlistentry>
</variablelist>
+1 -16
View File
@@ -175,21 +175,6 @@ upgrading `coreutils-5.0' to `coreutils-5.2.1'</screen>
</para>
<para>If you grow bored of specifying the Nix expressions using
<parameter>-f</parameter> all the time, you can set a default
location:
<screen>
$ nix-env -I nixpkgs-<replaceable>version</replaceable></screen>
After this you can just say, for instance, <literal>nix-env -u
'*'</literal>.<footnote><para>Setting a default using
<parameter>-I</parameter> currently clashes with using Nix channels,
since <literal>nix-channel --update</literal> calls <literal>nix-env
-I</literal> to set the default to the Nix expressions it downloaded
from the channel, replacing whatever default you had
set.</para></footnote></para>
</section>
@@ -458,7 +443,7 @@ URL.</para>
<command>nix-channel --add</command>, e.g.,
<screen>
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable</screen>
subscribes you to a channel that always contains that latest version
of the Nix Packages collection. (Instead of
+1 -1
View File
@@ -41,7 +41,7 @@ file).</para></listitem>
<listitem><para>Subscribe to the Nix Packages channel.
<screen>
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable</screen>
</para></listitem>
+160
View File
@@ -6,6 +6,161 @@
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.2"><title>Release 1.2 (December 6, 2012)</title>
<para>This release has the following improvements and changes:</para>
<itemizedlist>
<listitem>
<para>Nix has a new binary substituter mechanism: the
<emphasis>binary cache</emphasis>. A binary cache contains
pre-built binaries of Nix packages. Whenever Nix wants to build a
missing Nix store path, it will check a set of binary caches to
see if any of them has a pre-built binary of that path. The
configuration setting <option>binary-caches</option> contains a
list of URLs of binary caches. For instance, doing
<screen>
$ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
</screen>
will install Thunderbird and its dependencies, using the available
pre-built binaries in <uri>http://nixos.org/binary-cache</uri>.
The main advantage over the old “manifest”-based method of getting
pre-built binaries is that you dont have to worry about your
manifest being in sync with the Nix expressions youre installing
from; i.e., you dont need to run <command>nix-pull</command> to
update your manifest. Its also more scalable because you dont
need to redownload a giant manifest file every time.
</para>
<para>A Nix channel can provide a binary cache URL that will be
used automatically if you subscribe to that channel. If you use
the Nixpkgs or NixOS channels
(<uri>http://nixos.org/channels</uri>) you automatically get the
cache <uri>http://nixos.org/binary-cache</uri>.</para>
<para>Binary caches are created using <command>nix-push</command>.
For details on the operation and format of binary caches, see the
<command>nix-push</command> manpage. More details are provided in
<link xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2012-September/009826.html">this
nix-dev posting</link>.</para>
</listitem>
<listitem>
<para>Multiple output support should now be usable. A derivation
can declare that it wants to produce multiple store paths by
saying something like
<programlisting>
outputs = [ "lib" "headers" "doc" ];
</programlisting>
This will cause Nix to pass the intended store path of each output
to the builder through the environment variables
<literal>lib</literal>, <literal>headers</literal> and
<literal>doc</literal>. Other packages can refer to a specific
output by referring to
<literal><replaceable>pkg</replaceable>.<replaceable>output</replaceable></literal>,
e.g.
<programlisting>
buildInputs = [ pkg.lib pkg.headers ];
</programlisting>
If you install a package with multiple outputs using
<command>nix-env</command>, each output path will be symlinked
into the user environment.</para>
</listitem>
<listitem>
<para>Dashes are now valid as part of identifiers and attribute
names.</para>
</listitem>
<listitem>
<para>The new operation <command>nix-store --repair-path</command>
allows corrupted or missing store paths to be repaired by
redownloading them. <command>nix-store --verify --check-contents
--repair</command> will scan and repair all paths in the Nix
store. Similarly, <command>nix-env</command>,
<command>nix-build</command>, <command>nix-instantiate</command>
and <command>nix-store --realise</command> have a
<option>--repair</option> flag to detect and fix bad paths by
rebuilding or redownloading them.</para>
</listitem>
<listitem>
<para>Nix no longer sets the immutable bit on files in the Nix
store. Instead, the recommended way to guard the Nix store
against accidental modification on Linux is to make it a read-only
bind mount, like this:
<screen>
$ mount --bind /nix/store /nix/store
$ mount -o remount,ro,bind /nix/store
</screen>
Nix will automatically make <filename>/nix/store</filename>
writable as needed (using a private mount namespace) to allow
modifications.</para>
</listitem>
<listitem>
<para>Store optimisation (replacing identical files in the store
with hard links) can now be done automatically every time a path
is added to the store. This is enabled by setting the
configuration option <literal>auto-optimise-store</literal> to
<literal>true</literal> (disabled by default).</para>
</listitem>
<listitem>
<para>Nix now supports <command>xz</command> compression for NARs
in addition to <command>bzip2</command>. It compresses about 30%
better on typical archives and decompresses about twice as
fast.</para>
</listitem>
<listitem>
<para>Basic Nix expression evaluation profiling: setting the
environment variable <envar>NIX_COUNT_CALLS</envar> to
<literal>1</literal> will cause Nix to print how many times each
primop or function was executed.</para>
</listitem>
<listitem>
<para>New primops: <varname>concatLists</varname>,
<varname>elem</varname>, <varname>elemAt</varname> and
<varname>filter</varname>.</para>
</listitem>
<listitem>
<para>The command <command>nix-copy-closure</command> has a new
flag <option>--use-substitutes</option> (<option>-s</option>) to
download missing paths on the target machine using the substitute
mechanism.</para>
</listitem>
<listitem>
<para>The command <command>nix-worker</command> has been renamed
to <command>nix-daemon</command>. Support for running the Nix
worker in “slave” mode has been removed.</para>
</listitem>
<listitem>
<para>The <option>--help</option> flag of every Nix command now
invokes <command>man</command>.</para>
</listitem>
<listitem>
<para>Chroot builds are now supported on systemd machines.</para>
</listitem>
</itemizedlist>
<para>This release has contributions from Eelco Dolstra, Florian
Friesdorf, Mats Erik Andersson and Shea Levy.</para>
</section>
<!--==================================================================-->
<section xml:id="ssec-relnotes-1.1"><title>Release 1.1 (July 18, 2012)</title>
@@ -94,6 +249,11 @@
<citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
<listitem>
<para>When using the Nix daemon, the <option>-s</option> flag in
<command>nix-env -qa</command> is now much faster.</para>
</listitem>
</itemizedlist>
</section>
+81 -48
View File
@@ -106,7 +106,7 @@ the single Nix expression in that directory
here, the entire remainder of the file is the body of the
function; when given the required arguments, the body should
describe how to build an instance of the Hello package.</para>
</callout>
<callout arearefs='ex-hello-nix-co-2'>
@@ -190,7 +190,7 @@ perl = perl;</programlisting>
with the same name happen to be in scope.</para>
</callout>
</calloutlist>
</para>
@@ -301,7 +301,7 @@ steps:</para>
(<literal>make install</literal>).</para>
</callout>
</calloutlist>
<para>If you are wondering about the absence of error checking on the
@@ -321,7 +321,7 @@ error check.</para>
...
rec { <co xml:id='ex-hello-composition-co-1' />
hello = import ../applications/misc/hello/ex-1 <co xml:id='ex-hello-composition-co-2' /> { <co xml:id='ex-hello-composition-co-3' />
inherit fetchurl stdenv perl;
};
@@ -330,10 +330,10 @@ rec { <co xml:id='ex-hello-composition-co-1' />
inherit fetchurl stdenv;
};
fetchurl = import ../build-support/fetchurl {
fetchurl = import ../build-support/fetchurl {
inherit stdenv; ...
};
stdenv = ...;
}
@@ -476,8 +476,8 @@ that the path denoted by <envar>out</envar> is now
will see that the path is already valid and finish immediately. If a
build fails, either because it returns a non-zero exit code, because
Nix or the builder are killed, or because the machine crashes, then
the output path will not be registered as valid. If you try to build
the derivation again, Nix will remove the output path if it exists
the output paths will not be registered as valid. If you try to build
the derivation again, Nix will remove the output paths if they exist
(e.g., because the builder died half-way through <literal>make
install</literal>) and try again. Note that there is no
<quote>negative caching</quote>: Nix doesn't remember that a build
@@ -543,7 +543,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
<calloutlist>
<callout arearefs='ex-hello-builder2-co-1'>
<para>The <envar>buildInputs</envar> variable tells
<filename>setup</filename> to use the indicated packages as
<quote>inputs</quote>. This means that if a package provides a
@@ -568,7 +568,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
the file <literal>$stdenv/setup</literal>.</para>
</callout>
<callout arearefs='ex-hello-builder2-co-3'>
<para>The final step calls the shell function
@@ -580,7 +580,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
see <xref linkend='sec-standard-environment' />.</para>
</callout>
</calloutlist>
<para>Discerning readers will note that the
@@ -704,7 +704,7 @@ configureFlags = "
the third line is indented three spaces. Thus, two spaces are
stripped from each line, so the resulting string is
<programlisting>
<programlisting>
"This is the first line.\nThis is the second line.\n This is the third line.\n"</programlisting>
</para>
@@ -726,7 +726,7 @@ configureFlags = "
linefeed, carriage-return and tab characters can be writted as
<literal>''\n</literal>, <literal>''\r</literal>,
<literal>''\t</literal>.</para>
<para>Indented strings are primarily useful in that they allow
multi-line string literals to follow the indentation of the
enclosing Nix expression, and that less escaping is typically
@@ -745,7 +745,7 @@ stdenv.mkDerivation {
${if enableBar then "cp bar $out/bin" else ""}
'';
<replaceable>...</replaceable>
}
}
</programlisting>
</para>
@@ -781,7 +781,7 @@ stdenv.mkDerivation {
<listitem><para><emphasis>Booleans</emphasis> with values
<literal>true</literal> and
<literal>false</literal>.</para></listitem>
</itemizedlist>
</para>
@@ -931,7 +931,7 @@ propagate attributes). This can be shortened using the
<literal>inherit</literal> keyword. For instance,
<programlisting>
let x = 123; in
let x = 123; in
{ inherit x;
y = 456;
}</programlisting>
@@ -982,7 +982,7 @@ argument. There are three kinds of patterns:</para>
<itemizedlist>
<listitem><para>If a pattern is a single identifier, then the
function matches any argument. Example:
@@ -1002,7 +1002,7 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
evaluates to <literal>[ "foobar" "foobla"
"fooabc" ]</literal>.</para></listitem>
<listitem><para>An <emphasis>attribute set pattern</emphasis> of the
form <literal>{ name1, name2, …, nameN }</literal>
matches an attribute set containing the listed attributes, and binds
@@ -1016,7 +1016,7 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
<varname>x</varname>, <varname>y</varname> and
<varname>z</varname>. No other attributes are allowed. If you want
to allow additional arguments, you can use an ellipsis
(<literal>...</literal>):
(<literal>...</literal>):
<programlisting>
{ x, y, z, ... }: z + y + x</programlisting>
@@ -1164,7 +1164,7 @@ used in the Nix expression for Subversion.</para>
</calloutlist>
</simplesect>
<simplesect><title>With-expressions</title>
@@ -1343,7 +1343,7 @@ set, the attributes of which specify the inputs of the build.</para>
<listitem><para>There must be an attribute named
<varname>name</varname> whose value must be a string. This is used
as a symbolic name for the package by <command>nix-env</command>,
and it is appended to the hash in the output path of the
and it is appended to the output paths of the
derivation.</para></listitem>
<listitem><para>There must be an attribute named
@@ -1358,7 +1358,7 @@ set, the attributes of which specify the inputs of the build.</para>
<itemizedlist>
<listitem><para>Strings, URIs, and integers are just passed
<listitem><para>Strings and integers are just passed
verbatim.</para></listitem>
<listitem><para>A <emphasis>path</emphasis> (e.g.,
@@ -1369,8 +1369,8 @@ set, the attributes of which specify the inputs of the build.</para>
should reside in the Nix store.</para></listitem>
<listitem><para>A <emphasis>derivation</emphasis> causes that
derivation to be built prior to the present derivation; the
output path is put in the environment
derivation to be built prior to the present derivation; its
default output path is put in the environment
variable.</para></listitem>
<listitem><para>Lists of the previous types are also allowed.
@@ -1389,14 +1389,48 @@ set, the attributes of which specify the inputs of the build.</para>
specifies command-line arguments to be passed to the builder. It
should be a list.</para></listitem>
<listitem><para>The optional attribute <varname>outputs</varname>
specifies a list of symbolic outputs of the derivation. By default,
a derivation produces a single output path, denoted as
<literal>out</literal>. However, derivations can produce multiple
output paths. This is useful because it allows outputs to be
downloaded or garbage-collected separately. For instance, imagine a
library package that provides a dynamic library, header files, and
documentation. A program that links against the library doesnt
need the header files and documentation at runtime, and it doesnt
need the documentation at build time. Thus, the library package
could specify:
<programlisting>
outputs = [ "lib" "headers" "doc" ];
</programlisting>
This will cause Nix to pass environment variables
<literal>lib</literal>, <literal>headers</literal> and
<literal>doc</literal> to the builder containing the intended store
paths of each output. The builder would typically do something like
<programlisting>
./configure --libdir=$lib/lib --includedir=$headers/include --docdir=$doc/share/doc
</programlisting>
for an Autoconf-style package. You can refer to each output of a
derivation by selecting it as an attribute, e.g.
<programlisting>
buildInputs = [ pkg.lib pkg.headers ];
</programlisting>
The first element of <varname>output</varname> determines the
<emphasis>default output</emphasis>. Thus, you could also write
<programlisting>
buildInputs = [ pkg pkg.headers ];
</programlisting>
since <literal>pkg</literal> is equivalent to
<literal>pkg.lib</literal>.</para></listitem>
</itemizedlist>
<para>(Note that <function>mkDerivation</function> in the standard
<para>The function <function>mkDerivation</function> in the standard
environment is a wrapper around <function>derivation</function> that
adds a default value for <varname>system</varname> and always uses
Bash as the builder, to which the supplied builder is passed as a
command-line argument. See <xref linkend='sec-standard-environment'
/>.)</para>
/>.</para>
<para>The builder is executed as follows:
@@ -1440,17 +1474,19 @@ command-line argument. See <xref linkend='sec-standard-environment'
top-level Nix store directory (typically,
<filename>/nix/store</filename>).</para></listitem>
<listitem><para><envar>out</envar> is set to point to the output
path of the derivation, which is a subdirectory of the Nix store.
The output path is a concatenation of the cryptographic hash of
all build inputs, and the <varname>name</varname>
attribute.</para></listitem>
<listitem><para>For each output declared in
<varname>outputs</varname>, the corresponding environment variable
is set to point to the intended path in the Nix store for that
output. Each output path is a concatenation of the cryptographic
hash of all build inputs, the <varname>name</varname> attribute
and the output name. (The output name is omitted if its
<literal>out</literal>.)</para></listitem>
</itemizedlist>
</para></listitem>
<listitem><para>If the output path already exists, it is removed.
<listitem><para>If an output path already exists, it is removed.
Also, locks are acquired to prevent multiple Nix instances from
performing the same build at the same time.</para></listitem>
@@ -1464,14 +1500,11 @@ command-line argument. See <xref linkend='sec-standard-environment'
<listitem><para>The temporary directory is removed (unless the
<option>-K</option> option was specified).</para></listitem>
<listitem><para>If the build was successful, Nix scans the output
for references to the paths of the inputs. These so-called
<emphasis>retained dependencies</emphasis> could be used when the
output of the derivation is used (e.g., when it's executed or used
as input to another derivation), so if we deploy the derivation, we
should copy the retained dependencies as well. The scan is
performed by looking for the hash parts of file names of the
inputs.</para></listitem>
<listitem><para>If the build was successful, Nix scans each output
path for references to input paths by looking for the hash parts of
the input paths. Since these are potential runtime dependencies,
Nix registers them as dependencies of the output
paths.</para></listitem>
<listitem><para>After the build, Nix sets the last-modified
timestamp on all files in the build result to 1 (00:00:01 1/1/1970
@@ -1497,7 +1530,7 @@ attributes.</para>
<variablelist>
<varlistentry><term><varname>allowedReferences</varname></term>
<listitem><para>The optional attribute
<varname>allowedReferences</varname> specifies a list of legal
references (dependencies) of the output of the builder. For
@@ -1515,7 +1548,7 @@ allowedReferences = [];
</varlistentry>
<varlistentry><term><varname>exportReferencesGraph</varname></term>
<listitem><para>This attribute allows builders access to the
@@ -1626,7 +1659,7 @@ stdenv.mkDerivation {
outputHashMode = "flat";
outputHashAlgo = "md5";
outputHash = md5;
inherit url;
}
</programlisting>
@@ -1655,7 +1688,7 @@ stdenv.mkDerivation {
<para>This is the default.</para></listitem>
</varlistentry>
<varlistentry><term><literal>"recursive"</literal></term>
<listitem><para>The hash is computed over the NAR archive dump
@@ -1676,10 +1709,10 @@ stdenv.mkDerivation {
linkend="sec-nix-hash"><command>nix-hash</command> command</link>
for information about converting to and from base-32
notation.)</para></listitem>
</varlistentry>
<varlistentry><term><varname>impureEnvVars</varname></term>
<listitem><para>This attribute allows you to specify a list of
@@ -1706,7 +1739,7 @@ impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
</varlistentry>
<varlistentry><term><varname>preferLocalBuild</varname></term>
<listitem><para>If this attribute is set to
+1 -1
View File
@@ -78,7 +78,7 @@ The hook `nix-mode-hook' is run when Nix mode is started.
("\\<isNull\\>" . font-lock-builtin-face)
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
. font-lock-constant-face)
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\.]*\\)[ \t]*="
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\-\.]*\\)[ \t]*="
(1 font-lock-variable-name-face nil nil))
("<[a-zA-Z0-9._\\+-]+\\(/[a-zA-Z0-9._\\+-]+\\)*>"
. font-lock-constant-face)
@@ -4,7 +4,7 @@ After=syslog.target
[Service]
Type=simple
ExecStart=/usr/bin/nix-worker --daemon
ExecStart=/usr/bin/nix-daemon
[Install]
WantedBy=multi-user.target
+7 -5
View File
@@ -10,17 +10,19 @@ License: LGPLv2+
Group: Applications/System
%endif
URL: http://nixos.org/
Source0: %{name}-%{version}.tar.bz2
Source0: %{name}-%{version}.tar.gz
%if 0%{?el5}
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
%endif
BuildRequires: perl(DBD::SQLite)
BuildRequires: perl(DBI)
BuildRequires: perl(WWW::Curl)
BuildRequires: perl(ExtUtils::ParseXS)
Requires: /usr/bin/perl
Requires: curl
Requires: perl-DBD-SQLite
Requires: bzip2
Requires: xz
BuildRequires: bzip2-devel
BuildRequires: sqlite-devel
@@ -138,7 +140,7 @@ chmod -x $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/nix.sh
%if ! 0%{?rhel}
# install systemd service descriptor
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/systemd/system
cp -p misc/systemd/nix-worker.service \
cp -p misc/systemd/nix-daemon.service \
$RPM_BUILD_ROOT%{_prefix}/lib/systemd/system/
%endif
@@ -172,8 +174,8 @@ chgrp %{nixbld_group} /nix/store
chmod 1775 /nix/store
%if ! 0%{?rhel}
# Enable and start Nix worker
systemctl enable nix-worker.service
systemctl start nix-worker.service
systemctl enable nix-daemon.service
systemctl start nix-daemon.service
%endif
%files
@@ -185,7 +187,7 @@ systemctl start nix-worker.service
%exclude %dir %{perl_vendorarch}/auto/
%{_prefix}/libexec/*
%if ! 0%{?rhel}
%{_prefix}/lib/systemd/system/nix-worker.service
%{_prefix}/lib/systemd/system/nix-daemon.service
%endif
%{_datadir}/emacs/site-lisp/nix-mode.el
%{_datadir}/nix
+2 -1
View File
@@ -1,4 +1,4 @@
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in lib/Nix/Utils.pm
all: $(PERL_MODULES:.in=)
@@ -26,6 +26,7 @@ AM_CXXFLAGS = \
-D_FILE_OFFSET_BITS=64
lib/Nix/Store.cc: lib/Nix/Store.xs
$(INSTALL) -d lib/Nix
xsubpp $^ -output $@
else
+17 -5
View File
@@ -1,27 +1,39 @@
package Nix::Config;
$version = "@version@";
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
$libexecDir = $ENV{"NIX_LIBEXEC_DIR"} || "@libexecdir@";
$stateDir = $ENV{"NIX_STATE_DIR"} || "@localstatedir@/nix";
$manifestDir = $ENV{"NIX_MANIFESTS_DIR"} || "@localstatedir@/nix/manifests";
$logDir = $ENV{"NIX_LOG_DIR"} || "@localstatedir@/log/nix";
$confDir = $ENV{"NIX_CONF_DIR"} || "@sysconfdir@/nix";
$storeDir = $ENV{"NIX_STORE_DIR"} || "@storedir@";
$bzip2 = $ENV{"NIX_BZIP2"} || "@bzip2@";
$bzip2 = "@bzip2@";
$xz = "@xz@";
$curl = "@curl@";
$useBindings = "@perlbindings@" eq "yes";
%config = ();
sub readConfig {
my %config;
my $config = "@sysconfdir@/nix/nix.conf";
if (defined $ENV{'_NIX_OPTIONS'}) {
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
my ($n, $v) = split '=', $s, 2;
$config{$n} = $v;
}
return;
}
my $config = "$confDir/nix.conf";
return unless -f $config;
open CONFIG, "<$config" or die "cannot open `$config'";
while (<CONFIG>) {
/^\s*([\w|-]+)\s*=\s*(.*)$/ or next;
$config{$1} = $2;
print "|$1| -> |$2|\n";
}
close CONFIG;
}
+8 -1
View File
@@ -6,7 +6,8 @@ use Nix::Store;
sub copyTo {
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer) = @_;
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor,
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_;
$compressor = "$compressor |" if $compressor ne "";
$decompressor = "$decompressor |" if $decompressor ne "";
@@ -16,6 +17,12 @@ sub copyTo {
my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
map { followLinksToStorePath $_ } @{$storePaths})));
# Optionally use substitutes on the remote host.
if (!$dryRun && $useSubstitutes) {
system "ssh $sshHost @{$sshOpts} nix-store -r --ignore-unknown @closure";
# Ignore exit status because this is just an optimisation.
}
# Ask the remote host which paths are invalid. Because of limits
# to the command line length, do this in chunks. Eventually,
# we'll want to use --from-stdin, but we can't rely on the
+89 -27
View File
@@ -9,12 +9,12 @@ use Fcntl ':flock';
use Nix::Config;
our @ISA = qw(Exporter);
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch);
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
sub addNAR {
my ($narFiles, $storePath, $info) = @_;
$$narFiles{$storePath} = []
unless defined $$narFiles{$storePath};
@@ -24,7 +24,7 @@ sub addNAR {
foreach my $narFile (@{$narFileList}) {
$found = 1 if $narFile->{url} eq $info->{url};
}
push @{$narFileList}, $info if !$found;
}
@@ -43,7 +43,7 @@ sub addPatch {
$patch2->{url} eq $patch->{url} &&
$patch2->{basePath} eq $patch->{basePath};
}
push @{$patchList}, $patch if !$found;
return !$found;
@@ -68,7 +68,7 @@ sub readManifest_ {
my $manifestVersion = 2;
my ($storePath, $url, $hash, $size, $basePath, $baseHash, $patchType);
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system);
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system, $compressionType);
while (<MANIFEST>) {
chomp;
@@ -93,10 +93,11 @@ sub readManifest_ {
undef $system;
$references = "";
$deriver = "";
}
$compressionType = "bzip2";
}
} else {
if (/^\}$/) {
$inside = 0;
@@ -107,6 +108,7 @@ sub readManifest_ {
, references => $references
, deriver => $deriver
, system => $system
, compressionType => $compressionType
});
}
@@ -120,11 +122,12 @@ sub readManifest_ {
}
}
elsif (/^\s*StorePath:\s*(\/\S+)\s*$/) { $storePath = $1; }
elsif (/^\s*CopyFrom:\s*(\/\S+)\s*$/) { $copyFrom = $1; }
elsif (/^\s*Hash:\s*(\S+)\s*$/) { $hash = $1; }
elsif (/^\s*URL:\s*(\S+)\s*$/) { $url = $1; }
elsif (/^\s*Compression:\s*(\S+)\s*$/) { $compressionType = $1; }
elsif (/^\s*Size:\s*(\d+)\s*$/) { $size = $1; }
elsif (/^\s*BasePath:\s*(\/\S+)\s*$/) { $basePath = $1; }
elsif (/^\s*BaseHash:\s*(\S+)\s*$/) { $baseHash = $1; }
@@ -172,6 +175,7 @@ sub writeManifest {
print MANIFEST "{\n";
print MANIFEST " StorePath: $storePath\n";
print MANIFEST " NarURL: $narFile->{url}\n";
print MANIFEST " Compression: $narFile->{compressionType}\n";
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
print MANIFEST " NarHash: $narFile->{narHash}\n";
@@ -184,7 +188,7 @@ sub writeManifest {
print MANIFEST "}\n";
}
}
foreach my $storePath (sort (keys %{$patches})) {
my $patchList = $$patches{$storePath};
foreach my $patch (@{$patchList}) {
@@ -201,8 +205,8 @@ sub writeManifest {
print MANIFEST "}\n";
}
}
close MANIFEST;
rename("$manifest.tmp", $manifest)
@@ -211,11 +215,11 @@ sub writeManifest {
# Create a bzipped manifest.
unless (defined $noCompress) {
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
or die "cannot compress manifest";
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
or die "cannot compress manifest";
rename("$manifest.bz2.tmp", "$manifest.bz2")
or die "cannot rename $manifest.bz2.tmp: $!";
rename("$manifest.bz2.tmp", "$manifest.bz2")
or die "cannot rename $manifest.bz2.tmp: $!";
}
}
@@ -224,8 +228,9 @@ sub updateManifestDB {
my $manifestDir = $Nix::Config::manifestDir;
mkpath($manifestDir);
my $dbPath = "$manifestDir/cache.sqlite";
unlink "$manifestDir/cache.sqlite"; # remove obsolete cache
my $dbPath = "$manifestDir/cache-v2.sqlite";
# Open/create the database.
our $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
@@ -245,13 +250,14 @@ sub updateManifestDB {
timestamp integer not null
);
EOF
$dbh->do(<<EOF);
create table if not exists NARs (
id integer primary key autoincrement not null,
manifest integer not null,
storePath text not null,
url text not null,
compressionType text not null,
hash text,
size integer,
narHash text,
@@ -292,8 +298,8 @@ EOF
flock(MAINLOCK, LOCK_EX) or die;
our $insertNAR = $dbh->prepare(
"insert into NARs(manifest, storePath, url, hash, size, narHash, " .
"narSize, refs, deriver, system) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
"insert into NARs(manifest, storePath, url, compressionType, hash, size, narHash, " .
"narSize, refs, deriver, system) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
our $insertPatch = $dbh->prepare(
"insert into Patches(manifest, storePath, basePath, baseHash, url, hash, " .
@@ -304,7 +310,7 @@ EOF
# Read each manifest in $manifestDir and add it to the database,
# unless we've already done so on a previous run.
my %seen;
for my $manifestLink (glob "$manifestDir/*.nixmanifest") {
my $manifest = Cwd::abs_path($manifestLink);
next unless -f $manifest;
@@ -316,9 +322,9 @@ EOF
{}, $manifest, $timestamp)} == 1;
print STDERR "caching $manifest...\n";
$dbh->do("delete from Manifests where path = ?", {}, $manifest);
$dbh->do("insert into Manifests(path, timestamp) values (?, ?)",
{}, $manifest, $timestamp);
@@ -327,11 +333,11 @@ EOF
sub addNARToDB {
my ($storePath, $narFile) = @_;
$insertNAR->execute(
$id, $storePath, $narFile->{url}, $narFile->{hash}, $narFile->{size},
$narFile->{narHash}, $narFile->{narSize}, $narFile->{references},
$id, $storePath, $narFile->{url}, $narFile->{compressionType}, $narFile->{hash},
$narFile->{size}, $narFile->{narHash}, $narFile->{narSize}, $narFile->{references},
$narFile->{deriver}, $narFile->{system});
};
sub addPatchToDB {
my ($storePath, $patch) = @_;
$insertPatch->execute(
@@ -341,7 +347,7 @@ EOF
};
my $version = readManifest_($manifest, \&addNARToDB, \&addPatchToDB);
if ($version < 3) {
die "you have an old-style or corrupt manifest `$manifestLink'; please delete it\n";
}
@@ -364,4 +370,60 @@ EOF
}
# Delete all old manifests downloaded from a given URL.
sub deleteOldManifests {
my ($url, $curUrlFile) = @_;
for my $urlFile (glob "$Nix::Config::manifestDir/*.url") {
next if defined $curUrlFile && $urlFile eq $curUrlFile;
open URL, "<$urlFile" or die;
my $url2 = <URL>;
chomp $url2;
close URL;
next unless $url eq $url2;
my $base = $urlFile; $base =~ s/.url$//;
unlink "${base}.url";
unlink "${base}.nixmanifest";
}
}
# Parse a NAR info file.
sub parseNARInfo {
my ($storePath, $content) = @_;
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system);
my $compression = "bzip2";
my @refs;
foreach my $line (split "\n", $content) {
return undef unless $line =~ /^(.*): (.*)$/;
if ($1 eq "StorePath") { $storePath2 = $2; }
elsif ($1 eq "URL") { $url = $2; }
elsif ($1 eq "Compression") { $compression = $2; }
elsif ($1 eq "FileHash") { $fileHash = $2; }
elsif ($1 eq "FileSize") { $fileSize = int($2); }
elsif ($1 eq "NarHash") { $narHash = $2; }
elsif ($1 eq "NarSize") { $narSize = int($2); }
elsif ($1 eq "References") { @refs = split / /, $2; }
elsif ($1 eq "Deriver") { $deriver = $2; }
elsif ($1 eq "System") { $system = $2; }
}
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
return
{ url => $url
, compression => $compression
, fileHash => $fileHash
, fileSize => $fileSize
, narHash => $narHash
, narSize => $narSize
, refs => [ @refs ]
, deriver => $deriver
, system => $system
};
}
return 1;
+1 -1
View File
@@ -19,7 +19,7 @@ void doInit()
{
if (!store) {
try {
setDefaultsFromEnvironment();
settings.processEnvironment();
store = openStore();
} catch (Error & e) {
croak(e.what());
+38
View File
@@ -0,0 +1,38 @@
package Nix::Utils;
our @ISA = qw(Exporter);
our @EXPORT = qw(checkURL uniq writeFile readFile);
$urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*]+ )";
sub checkURL {
my ($url) = @_;
die "invalid URL $url\n" unless $url =~ /^ $urlRE $ /x;
}
sub uniq {
my %seen;
my @res;
foreach my $name (@_) {
next if $seen{$name};
$seen{$name} = 1;
push @res, $name;
}
return @res;
}
sub writeFile {
my ($fn, $s) = @_;
open TMP, ">$fn" or die;
print TMP "$s" or die;
close TMP or die;
}
sub readFile {
local $/ = undef;
my ($fn) = @_;
open TMP, "<$fn" or die;
my $s = <TMP>;
close TMP or die;
return $s;
}
+26 -12
View File
@@ -29,6 +29,7 @@ let
--with-xml-flags=--nonet
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
'';
postUnpack = ''
@@ -41,9 +42,18 @@ let
export VARTEXFONTS=$TMPDIR/texfonts
'';
distPhase =
''
runHook preDist
make dist-gzip
make dist-xz
mkdir -p $out/tarballs
cp *.tar.* $out/tarballs
'';
preDist = ''
make -C doc/manual install prefix=$out
make -C doc/manual manual.pdf prefix=$out
cp doc/manual/manual.pdf $out/manual.pdf
@@ -54,7 +64,7 @@ let
# to Windows and Macs, so there should be no Linux binaries
# in the closure).
nuke-refs $out/manual.pdf
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
echo "doc-pdf manual $out/manual.pdf" >> $out/nix-support/hydra-build-products
echo "doc release-notes $out/share/doc/nix/release-notes" >> $out/nix-support/hydra-build-products
@@ -77,6 +87,7 @@ let
--disable-init-state
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
--enable-gc
'';
@@ -134,12 +145,13 @@ let
--disable-init-state
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
'';
dontInstall = false;
doInstallCheck = true;
lcovFilter = [ "*/boost/*" "*-tab.*" ];
# We call `dot', and even though we just use it to
@@ -148,16 +160,16 @@ let
FONTCONFIG_FILE = texFunctions.fontsConf;
};
rpm_fedora13i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora13i386) 50;
rpm_fedora13x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora13x86_64) 50;
rpm_fedora16i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora16i386) 50;
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50;
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
deb_ubuntu1004i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1004i386) 50;
deb_ubuntu1004x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1004x86_64) 50;
deb_ubuntu1010i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1010i386) 50;
@@ -166,6 +178,8 @@ let
deb_ubuntu1110x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1110x86_64) 60;
deb_ubuntu1204i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1204i386) 60;
deb_ubuntu1204x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1204x86_64) 60;
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) 70;
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70;
# System tests.
@@ -183,16 +197,16 @@ let
makeRPM_i686 = makeRPM "i686-linux";
makeRPM_x86_64 = makeRPM "x86_64-linux";
makeRPM =
makeRPM =
system: diskImageFun: prio:
with import nixpkgs { inherit system; };
releaseTools.rpmBuild rec {
name = "nix-rpm-${diskImage.name}";
name = "nix-rpm";
src = jobs.tarball;
diskImage = (diskImageFun vmTools.diskImageFuns)
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" ]; };
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" ]; };
memSize = 1024;
meta.schedulingPriority = prio;
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
@@ -201,7 +215,7 @@ let
makeDeb_i686 = makeDeb "i686-linux";
makeDeb_x86_64 = makeDeb "x86_64-linux";
makeDeb =
system: diskImageFun: prio:
@@ -211,11 +225,11 @@ let
name = "nix-deb";
src = jobs.tarball;
diskImage = (diskImageFun vmTools.diskImageFuns)
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" ]; };
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" ]; };
memSize = 1024;
meta.schedulingPriority = prio;
configureFlags = "--sysconfdir=/etc";
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" ];
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" ];
doInstallCheck = true;
};
+3 -5
View File
@@ -7,17 +7,14 @@ noinst_SCRIPTS = nix-profile.sh \
find-runtime-roots.pl build-remote.pl nix-reduce-build \
copy-from-other-stores.pl nix-http-export.cgi
nix-pull nix-push: download-using-manifests.pl
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl find-runtime-roots.pl
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl find-runtime-roots.pl
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL_PROGRAM) copy-from-other-stores.pl $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL_PROGRAM) download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl $(DESTDIR)$(libexecdir)/nix/substituters
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
include ../substitute.mk
@@ -29,6 +26,7 @@ EXTRA_DIST = nix-collect-garbage.in \
nix-build.in \
download-using-manifests.pl.in \
copy-from-other-stores.pl.in \
download-from-binary-cache.pl.in \
nix-copy-closure.in \
find-runtime-roots.pl.in \
build-remote.pl.in \
+16 -14
View File
@@ -6,6 +6,7 @@ use IO::Handle;
use Nix::Config;
use Nix::SSH qw/sshOpts openSSHConnection/;
use Nix::CopyClosure;
use Nix::Store;
no warnings('once');
@@ -18,7 +19,7 @@ no warnings('once');
# 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 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.
@@ -45,8 +46,7 @@ sub all { $_ || return 0 for @_; 1 }
# Initialisation.
my $loadIncreased = 0;
my ($localSystem, $maxSilentTime, $printBuildTrace) = @ARGV;
$maxSilentTime = 0 unless defined $maxSilentTime;
my ($localSystem, $maxSilentTime, $printBuildTrace, $buildTimeout) = @ARGV;
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
@@ -102,14 +102,14 @@ REQ: while (1) {
sendReply "decline";
next;
}
# Acquire the exclusive lock on $currentLoad/main-lock.
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
my $mainLock = "$currentLoad/main-lock";
sysopen MAINLOCK, "$mainLock", O_RDWR|O_CREAT, 0600 or die;
flock(MAINLOCK, LOCK_EX) or die;
while (1) {
# Find all machine that can execute this build, i.e., that
# support builds for the given platform and features, and are
@@ -141,7 +141,7 @@ REQ: while (1) {
close $slotLock;
$slot++;
}
push @available, { machine => $cur, load => $load, free => $free }
if $load < $cur->{maxJobs};
}
@@ -160,7 +160,7 @@ REQ: while (1) {
if ($rightType && !$canBuildLocally) {
sendReply "postpone";
} else {
sendReply "decline";
sendReply "decline";
}
close MAINLOCK;
next REQ;
@@ -182,9 +182,9 @@ REQ: while (1) {
# Select the best available machine and lock a free slot.
my $selected = $available[0];
my $selected = $available[0];
my $machine = $selected->{machine};
$slotLock = openSlotLock($machine, $selected->{free});
flock($slotLock, LOCK_EX | LOCK_NB) or die;
utime undef, undef, $slotLock;
@@ -196,7 +196,7 @@ REQ: while (1) {
@sshOpts = ("-i", $machine->{sshKeys}, "-x");
$hostName = $machine->{hostName};
last REQ if openSSHConnection $hostName;
warn "unable to open SSH connection to $hostName, trying other available machines...\n";
$machine->{enabled} = 0;
}
@@ -257,7 +257,9 @@ close UPLOADLOCK;
# Perform the build.
my $buildFlags = "--max-silent-time $maxSilentTime --fallback --add-root $rootsDir/\$PPID.out --option verbosity 0";
my $buildFlags =
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout "
. "--fallback --add-root $rootsDir/\$PPID.out --option verbosity 0";
# We let the remote side kill its process group when the connection is
# closed unexpectedly. This is necessary to ensure that no processes
@@ -286,10 +288,10 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store
foreach my $output (@outputs) {
my $maybeSignRemote = "";
$maybeSignRemote = "--sign" if $UID != 0;
next if isValidPath($output);
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" .
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
or die "cannot copy $output from $hostName: $?";
or die "cannot copy $output from $hostName: $?";
}
+37 -32
View File
@@ -36,42 +36,45 @@ sub findStorePath {
if ($ARGV[0] eq "--query") {
while (<STDIN>) {
my $cmd = $_; chomp $cmd;
chomp;
my ($cmd, @args) = split " ", $_;
if ($cmd eq "have") {
my $storePath = <STDIN>; chomp $storePath;
print STDOUT (defined findStorePath($storePath) ? "1\n" : "0\n");
foreach my $storePath (@args) {
print "$storePath\n" if defined findStorePath($storePath);
}
print "\n";
}
elsif ($cmd eq "info") {
my $storePath = <STDIN>; chomp $storePath;
my ($store, $sourcePath) = findStorePath($storePath);
if (!defined $store) {
print "0\n";
next; # not an error
foreach my $storePath (@args) {
my ($store, $sourcePath) = findStorePath($storePath);
next unless defined $store;
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
my $deriver = `@bindir@/nix-store --query --deriver $storePath`;
die "cannot query deriver of `$storePath'" if $? != 0;
chomp $deriver;
$deriver = "" if $deriver eq "unknown-deriver";
my @references = split "\n",
`@bindir@/nix-store --query --references $storePath`;
die "cannot query references of `$storePath'" if $? != 0;
my $narSize = `@bindir@/nix-store --query --size $storePath`;
die "cannot query size of `$storePath'" if $? != 0;
chomp $narSize;
print "$storePath\n";
print "$deriver\n";
print scalar @references, "\n";
print "$_\n" foreach @references;
print "$narSize\n";
print "$narSize\n";
}
print "1\n";
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
my $deriver = `@bindir@/nix-store --query --deriver $storePath`;
die "cannot query deriver of `$storePath'" if $? != 0;
chomp $deriver;
$deriver = "" if $deriver eq "unknown-deriver";
my @references = split "\n",
`@bindir@/nix-store --query --references $storePath`;
die "cannot query references of `$storePath'" if $? != 0;
my $narSize = `@bindir@/nix-store --query --size $storePath`;
die "cannot query size of `$storePath'" if $? != 0;
chomp $narSize;
print "$deriver\n";
print scalar @references, "\n";
print "$_\n" foreach @references;
print "$narSize\n";
print "$narSize\n";
print "\n";
}
else { die "unknown command `$cmd'"; }
@@ -80,13 +83,15 @@ if ($ARGV[0] eq "--query") {
elsif ($ARGV[0] eq "--substitute") {
die unless scalar @ARGV == 2;
die unless scalar @ARGV == 3;
my $storePath = $ARGV[1];
my $destPath = $ARGV[2];
my ($store, $sourcePath) = findStorePath $storePath;
die unless $store;
print "\n*** Copying `$storePath' from `$sourcePath'\n\n";
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $storePath") == 0
print STDERR "\n*** Copying `$storePath' from `$sourcePath'\n\n";
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $destPath") == 0
or die "cannot copy `$sourcePath' to `$storePath'";
print "\n"; # no hash to verify
}
+550
View File
@@ -0,0 +1,550 @@
#! @perl@ -w @perlFlags@
use DBI;
use File::Basename;
use IO::Select;
use Nix::Config;
use Nix::Store;
use Nix::Utils;
use Nix::Manifest;
use WWW::Curl::Easy;
use WWW::Curl::Multi;
use strict;
Nix::Config::readConfig;
my @caches;
my $gotCaches = 0;
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
$maxParallelRequests = 1 if $maxParallelRequests < 1;
my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
my $didExpiration = 0;
my $debug = ($ENV{"NIX_DEBUG_SUBST"} // "") eq 1;
open(STDERR, ">>/dev/tty") if $debug;
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence);
my $curlm = WWW::Curl::Multi->new;
my $activeRequests = 0;
my $curlIdCount = 1;
my %requests;
my %scheduled;
my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
sub addRequest {
my ($storePath, $url, $head) = @_;
my $curl = WWW::Curl::Easy->new;
my $curlId = $curlIdCount++;
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET" };
$curl->setopt(CURLOPT_PRIVATE, $curlId);
$curl->setopt(CURLOPT_URL, $url);
open (my $fh, ">", \$requests{$curlId}->{content});
$curl->setopt(CURLOPT_WRITEDATA, $fh);
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
$curl->setopt(CURLOPT_FAILONERROR, 1);
if ($activeRequests >= $maxParallelRequests) {
$scheduled{$curlId} = 1;
} else {
$curlm->add_handle($curl);
$activeRequests++;
}
return $requests{$curlId};
}
sub processRequests {
while ($activeRequests) {
my ($rfds, $wfds, $efds) = $curlm->fdset();
#print STDERR "R = @{$rfds}, W = @{$wfds}, E = @{$efds}\n";
# Sleep until we can read or write some data.
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 0.1);
}
if ($curlm->perform() != $activeRequests) {
while (my ($id, $result) = $curlm->info_read) {
if ($id) {
my $request = $requests{$id} or die;
my $handle = $request->{handle};
$request->{result} = $result;
$request->{httpStatus} = $handle->getinfo(CURLINFO_RESPONSE_CODE);
print STDERR "$request->{type} on $request->{url} [$request->{result}, $request->{httpStatus}]\n" if $debug;
$activeRequests--;
delete $request->{handle};
if (scalar(keys %scheduled) > 0) {
my $id2 = (keys %scheduled)[0];
$curlm->add_handle($requests{$id2}->{handle});
$activeRequests++;
delete $scheduled{$id2};
}
}
}
}
}
}
sub initCache {
my $dbPath = "$Nix::Config::stateDir/binary-cache-v2.sqlite";
unlink "$Nix::Config::stateDir/binary-cache-v1.sqlite";
# Open/create the database.
$dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
or die "cannot open database `$dbPath'";
$dbh->{RaiseError} = 1;
$dbh->{PrintError} = 0;
$dbh->sqlite_busy_timeout(60 * 60 * 1000);
$dbh->do("pragma synchronous = off"); # we can always reproduce the cache
$dbh->do("pragma journal_mode = truncate");
# Initialise the database schema, if necessary.
$dbh->do(<<EOF);
create table if not exists BinaryCaches (
id integer primary key autoincrement not null,
url text unique not null,
timestamp integer not null,
storeDir text not null,
wantMassQuery integer not null,
priority integer not null
);
EOF
$dbh->do(<<EOF);
create table if not exists NARs (
cache integer not null,
storePath text not null,
url text not null,
compression text not null,
fileHash text,
fileSize integer,
narHash text,
narSize integer,
refs text,
deriver text,
system text,
timestamp integer not null,
primary key (cache, storePath),
foreign key (cache) references BinaryCaches(id) on delete cascade
);
EOF
$dbh->do(<<EOF);
create table if not exists NARExistence (
cache integer not null,
storePath text not null,
exist integer not null,
timestamp integer not null,
primary key (cache, storePath),
foreign key (cache) references BinaryCaches(id) on delete cascade
);
EOF
$dbh->do("create index if not exists NARExistenceByExistTimestamp on NARExistence (exist, timestamp)");
$queryCache = $dbh->prepare("select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?") or die;
$insertNAR = $dbh->prepare(
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " .
"narSize, refs, deriver, system, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
$insertNARExistence = $dbh->prepare(
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)") or die;
$queryNARExistence = $dbh->prepare("select exist, timestamp from NARExistence where cache = ? and storePath = ?") or die;
$expireNARExistence = $dbh->prepare("delete from NARExistence where exist = ? and timestamp < ?") or die;
}
sub getAvailableCaches {
return if $gotCaches;
$gotCaches = 1;
return if
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
sub strToList {
my ($s) = @_;
return map { s/\/+$//; $_ } split(/ /, $s);
}
my @urls = strToList ($Nix::Config::config{"binary-caches"} // "");
# // ($Nix::Config::storeDir eq "/nix/store" ? "http://nixos.org/binary-cache" : ""));
my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
// "/nix/var/nix/profiles/per-user/root/channels/binary-caches/*";
foreach my $urlFile (glob $urlsFiles) {
next unless -f $urlFile;
open FILE, "<$urlFile" or die "cannot open $urlFile\n";
my $url = <FILE>; chomp $url;
close FILE;
push @urls, strToList($url);
}
# Allow Nix daemon users to override the binary caches to a subset
# of those listed in the config file. Note that untrusted-*
# denotes options passed by the client.
if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
@urls = ();
foreach my $url (@untrustedUrls) {
die "binary cache $url is not trusted (please add it to trusted-binary-caches [@trustedUrls] in $Nix::Config::confDir/nix.conf)\n"
unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
push @urls, $url;
}
}
foreach my $url (uniq @urls) {
# FIXME: not atomic.
$queryCache->execute($url);
my $res = $queryCache->fetchrow_hashref();
if (defined $res) {
next if $res->{storeDir} ne $Nix::Config::storeDir;
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $res->{wantMassQuery}, priority => $res->{priority} };
next;
}
# Get the cache info file.
my $request = addRequest(undef, $url . "/nix-cache-info");
processRequests;
if ($request->{result} != 0) {
print STDERR "could not download $request->{url} (" .
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
next;
}
my $storeDir = "/nix/store";
my $wantMassQuery = 0;
my $priority = 50;
foreach my $line (split "\n", $request->{content}) {
unless ($line =~ /^(.*): (.*)$/) {
print STDERR "bad cache info file $request->{url}\n";
return undef;
}
if ($1 eq "StoreDir") { $storeDir = $2; }
elsif ($1 eq "WantMassQuery") { $wantMassQuery = int($2); }
elsif ($1 eq "Priority") { $priority = int($2); }
}
$dbh->do("insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
{}, $url, time(), $storeDir, $wantMassQuery, $priority);
my $id = $dbh->last_insert_id("", "", "", "");
next if $storeDir ne $Nix::Config::storeDir;
push @caches, { id => $id, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
}
@caches = sort { $a->{priority} <=> $b->{priority} } @caches;
expireNegative();
}
sub processNARInfo {
my ($storePath, $cache, $request) = @_;
if ($request->{result} != 0) {
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
print STDERR "could not download $request->{url} (" .
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
} else {
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
unless $request->{url} =~ /^file:/;
}
return undef;
}
my $narInfo = parseNARInfo($storePath, $request->{content});
return undef unless defined $narInfo;
# Cache the result.
$insertNAR->execute(
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time())
unless $request->{url} =~ /^file:/;
return $narInfo;
}
sub getCachedInfoFrom {
my ($storePath, $cache) = @_;
$queryNAR->execute($cache->{id}, basename($storePath));
my $res = $queryNAR->fetchrow_hashref();
return undef unless defined $res;
return
{ url => $res->{url}
, compression => $res->{compression}
, fileHash => $res->{fileHash}
, fileSize => $res->{fileSize}
, narHash => $res->{narHash}
, narSize => $res->{narSize}
, refs => [ split " ", $res->{refs} ]
, deriver => $res->{deriver}
} if defined $res;
}
sub negativeHit {
my ($storePath, $cache) = @_;
$queryNARExistence->execute($cache->{id}, basename($storePath));
my $res = $queryNARExistence->fetchrow_hashref();
return defined $res && $res->{exist} == 0 && time() - $res->{timestamp} < $ttlNegativeUse;
}
sub positiveHit {
my ($storePath, $cache) = @_;
return 1 if defined getCachedInfoFrom($storePath, $cache);
$queryNARExistence->execute($cache->{id}, basename($storePath));
my $res = $queryNARExistence->fetchrow_hashref();
return defined $res && $res->{exist} == 1;
}
sub expireNegative {
return if $didExpiration;
$didExpiration = 1;
my $time = time();
# Round up to the next multiple of the TTL to ensure that we do
# expiration only once per time interval. E.g. if $ttlNegative ==
# 3600, we expire entries at most once per hour. This is
# presumably faster than expiring a few entries per request (and
# thus doing a transaction).
my $limit = (int($time / $ttlNegative) - 1) * $ttlNegative;
$expireNARExistence->execute($limit, 0);
print STDERR "expired ", $expireNARExistence->rows, " negative entries\n" if $debug;
}
sub printInfo {
my ($storePath, $info) = @_;
print "$storePath\n";
print $info->{deriver} ? "$Nix::Config::storeDir/$info->{deriver}" : "", "\n";
print scalar @{$info->{refs}}, "\n";
print "$Nix::Config::storeDir/$_\n" foreach @{$info->{refs}};
print $info->{fileSize} || 0, "\n";
print $info->{narSize} || 0, "\n";
}
sub infoUrl {
my ($binaryCacheUrl, $storePath) = @_;
my $pathHash = substr(basename($storePath), 0, 32);
my $infoUrl = "$binaryCacheUrl/$pathHash.narinfo";
}
sub printInfoParallel {
my @paths = @_;
# First print all paths for which we have cached info.
my @left;
foreach my $storePath (@paths) {
my $found = 0;
foreach my $cache (@caches) {
my $info = getCachedInfoFrom($storePath, $cache);
if (defined $info) {
printInfo($storePath, $info);
$found = 1;
last;
}
}
push @left, $storePath if !$found;
}
return if scalar @left == 0;
foreach my $cache (@caches) {
my @left2;
%requests = ();
foreach my $storePath (@left) {
if (negativeHit($storePath, $cache)) {
push @left2, $storePath;
next;
}
addRequest($storePath, infoUrl($cache->{url}, $storePath));
}
processRequests;
foreach my $request (values %requests) {
my $info = processNARInfo($request->{storePath}, $cache, $request);
if (defined $info) {
printInfo($request->{storePath}, $info);
} else {
push @left2, $request->{storePath};
}
}
@left = @left2;
}
}
sub printSubstitutablePaths {
my @paths = @_;
# First look for paths that have cached info.
my @left;
foreach my $storePath (@paths) {
my $found = 0;
foreach my $cache (@caches) {
next unless $cache->{wantMassQuery};
if (positiveHit($storePath, $cache)) {
print "$storePath\n";
$found = 1;
last;
}
}
push @left, $storePath if !$found;
}
return if scalar @left == 0;
# For remaining paths, do HEAD requests.
foreach my $cache (@caches) {
next unless $cache->{wantMassQuery};
my @left2;
%requests = ();
foreach my $storePath (@left) {
if (negativeHit($storePath, $cache)) {
push @left2, $storePath;
next;
}
addRequest($storePath, infoUrl($cache->{url}, $storePath), 1);
}
processRequests;
foreach my $request (values %requests) {
if ($request->{result} != 0) {
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
print STDERR "could not check $request->{url} (" .
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
} else {
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
unless $request->{url} =~ /^file:/;
}
push @left2, $request->{storePath};
} else {
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
unless $request->{url} =~ /^file:/;
print "$request->{storePath}\n";
}
}
@left = @left2;
}
}
sub downloadBinary {
my ($storePath, $destPath) = @_;
foreach my $cache (@caches) {
my $info = getCachedInfoFrom($storePath, $cache);
unless (defined $info) {
next if negativeHit($storePath, $cache);
my $request = addRequest($storePath, infoUrl($cache->{url}, $storePath));
processRequests;
$info = processNARInfo($storePath, $cache, $request);
}
next unless defined $info;
my $decompressor;
if ($info->{compression} eq "bzip2") { $decompressor = "$Nix::Config::bzip2 -d"; }
elsif ($info->{compression} eq "xz") { $decompressor = "$Nix::Config::xz -d"; }
else {
print STDERR "unknown compression method $info->{compression}\n";
next;
}
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
print STDERR "\n*** Downloading $url to $storePath...\n";
checkURL $url;
if (system("$Nix::Config::curl --fail --location --insecure '$url' | $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n";
next;
}
# Tell Nix about the expected hash so it can verify it.
print "$info->{narHash}\n";
print STDERR "\n";
return;
}
print STDERR "could not download $storePath from any binary cache\n";
}
initCache();
if ($ARGV[0] eq "--query") {
while (<STDIN>) {
getAvailableCaches;
chomp;
my ($cmd, @args) = split " ", $_;
if ($cmd eq "have") {
print STDERR "checking binary caches for existence of @args\n" if $debug;
printSubstitutablePaths(@args);
print "\n";
}
elsif ($cmd eq "info") {
print STDERR "checking binary caches for info on @args\n" if $debug;
printInfoParallel(@args);
print "\n";
}
else { die "unknown command `$cmd'"; }
flush STDOUT;
}
}
elsif ($ARGV[0] eq "--substitute") {
my $storePath = $ARGV[1] or die;
my $destPath = $ARGV[2] or die;
getAvailableCaches;
downloadBinary($storePath, $destPath);
}
else {
die;
}
+71 -84
View File
@@ -4,6 +4,7 @@ use strict;
use Nix::Config;
use Nix::Manifest;
use Nix::Store;
use Nix::Utils;
use POSIX qw(strftime);
use File::Temp qw(tempdir);
@@ -15,6 +16,9 @@ my $logFile = "$Nix::Config::logDir/downloads";
# estimating the expected download size.
my $fast = 1;
# --insecure is fine because Nix verifies the hash of the result.
my $curl = "$Nix::Config::curl --fail --location --insecure";
# Open the manifest cache and update it if necessary.
my $dbh = updateManifestDB();
@@ -38,7 +42,7 @@ sub parseHash {
# given path.
sub computeSmallestDownload {
my $targetPath = shift;
# Build a graph of all store paths that might contribute to the
# construction of $targetPath, and the special node "start". The
# edges are either patch operations, or downloads of full NAR
@@ -93,7 +97,7 @@ sub computeSmallestDownload {
my $patchList = $dbh->selectall_arrayref(
"select * from Patches where storePath = ?",
{ Slice => {} }, $u);
foreach my $patch (@{$patchList}) {
if (isValidPath($patch->{basePath})) {
my ($baseHashAlgo, $baseHash) = parseHash $patch->{baseHash};
@@ -106,7 +110,7 @@ sub computeSmallestDownload {
$hash =~ s/.*://;
$hashCache->{$baseHashAlgo}->{$patch->{basePath}} = $hash;
}
next if $hash ne $baseHash;
}
push @queue, $patch->{basePath};
@@ -117,7 +121,7 @@ sub computeSmallestDownload {
my $narFileList = $dbh->selectall_arrayref(
"select * from NARs where storePath = ?",
{ Slice => {} }, $u);
foreach my $narFile (@{$narFileList}) {
# !!! how to handle files whose size is not known in advance?
# For now, assume some arbitrary size (1 GB).
@@ -173,58 +177,56 @@ sub computeSmallestDownload {
if ($ARGV[0] eq "--query") {
while (<STDIN>) {
my $cmd = $_; chomp $cmd;
chomp;
my ($cmd, @args) = split " ", $_;
if ($cmd eq "have") {
my $storePath = <STDIN>; chomp $storePath;
print STDOUT (
scalar @{$dbh->selectcol_arrayref("select 1 from NARs where storePath = ?", {}, $storePath)} > 0
? "1\n" : "0\n");
foreach my $storePath (@args) {
print "$storePath\n" if scalar @{$dbh->selectcol_arrayref("select 1 from NARs where storePath = ?", {}, $storePath)} > 0;
}
print "\n";
}
elsif ($cmd eq "info") {
my $storePath = <STDIN>; chomp $storePath;
foreach my $storePath (@args) {
my $infos = $dbh->selectall_arrayref(
"select * from NARs where storePath = ?",
{ Slice => {} }, $storePath);
my $info;
if (scalar @{$infos} > 0) {
$info = @{$infos}[0];
}
else {
print "0\n";
next; # not an error
}
my $infos = $dbh->selectall_arrayref(
"select * from NARs where storePath = ?",
{ Slice => {} }, $storePath);
print "1\n";
print "$info->{deriver}\n";
my @references = split " ", $info->{refs};
print scalar @references, "\n";
print "$_\n" foreach @references;
next unless scalar @{$infos} > 0;
my $info = @{$infos}[0];
my @path = computeSmallestDownload $storePath;
print "$storePath\n";
print "$info->{deriver}\n";
my @references = split " ", $info->{refs};
print scalar @references, "\n";
print "$_\n" foreach @references;
my $downloadSize = 0;
while (scalar @path > 0) {
my $edge = pop @path;
my $u = $edge->{start};
my $v = $edge->{end};
if ($edge->{type} eq "patch") {
$downloadSize += $edge->{info}->{size} || 0;
}
elsif ($edge->{type} eq "narfile") {
$downloadSize += $edge->{info}->{size} || 0;
my @path = computeSmallestDownload $storePath;
my $downloadSize = 0;
while (scalar @path > 0) {
my $edge = pop @path;
my $u = $edge->{start};
my $v = $edge->{end};
if ($edge->{type} eq "patch") {
$downloadSize += $edge->{info}->{size} || 0;
}
elsif ($edge->{type} eq "narfile") {
$downloadSize += $edge->{info}->{size} || 0;
}
}
print "$downloadSize\n";
my $narSize = $info->{narSize} || 0;
print "$narSize\n";
}
print "$downloadSize\n";
my $narSize = $info->{narSize} || 0;
print "$narSize\n";
print "\n";
}
else { die "unknown command `$cmd'"; }
}
@@ -236,8 +238,9 @@ elsif ($ARGV[0] ne "--substitute") {
}
die unless scalar @ARGV == 2;
die unless scalar @ARGV == 3;
my $targetPath = $ARGV[1];
my $destPath = $ARGV[2];
$fast = 0;
@@ -273,16 +276,6 @@ $dbh->disconnect;
my $curStep = 1;
my $maxStep = scalar @path;
sub downloadFile {
my $url = shift;
$ENV{"PRINT_PATH"} = 1;
$ENV{"QUIET"} = 1;
my ($hash, $path) = `$Nix::Config::binDir/nix-prefetch-url '$url'`;
die "download of `$url' failed" . ($! ? ": $!" : "") . "\n" unless $? == 0;
chomp $path;
return $path;
}
my $finalNarHash;
while (scalar @path > 0) {
@@ -314,13 +307,16 @@ while (scalar @path > 0) {
# Download the patch.
print STDERR " downloading patch...\n";
my $patchPath = downloadFile "$patch->{url}";
my $patchPath = "$tmpDir/patch";
checkURL $patch->{url};
system("$curl '$patch->{url}' -o $patchPath") == 0
or die "cannot download patch `$patch->{url}'\n";
# Apply the patch to the NAR archive produced in step 1 (for
# the already present path) or a later step (for patch sequences).
print STDERR " applying patch...\n";
system("$Nix::Config::libexecDir/bspatch $tmpNar $tmpNar2 $patchPath") == 0
or die "cannot apply patch `$patchPath' to $tmpNar";
or die "cannot apply patch `$patchPath' to $tmpNar\n";
if ($curStep < $maxStep) {
# The archive will be used as the base of the next patch.
@@ -329,8 +325,8 @@ while (scalar @path > 0) {
# This was the last patch. Unpack the final NAR archive
# into the target path.
print STDERR " unpacking patched archive...\n";
system("$Nix::Config::binDir/nix-store --restore $v < $tmpNar2") == 0
or die "cannot unpack $tmpNar2 into `$v'";
system("$Nix::Config::binDir/nix-store --restore $destPath < $tmpNar2") == 0
or die "cannot unpack $tmpNar2 to `$v'\n";
}
$finalNarHash = $patch->{narHash};
@@ -338,24 +334,26 @@ while (scalar @path > 0) {
elsif ($edge->{type} eq "narfile") {
my $narFile = $edge->{info};
print STDERR "downloading `$narFile->{url}' into `$v'\n";
print STDERR "downloading `$narFile->{url}' to `$v'\n";
my $size = $narFile->{size} || -1;
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
# Download the archive.
print STDERR " downloading archive...\n";
my $narFilePath = downloadFile "$narFile->{url}";
checkURL $narFile->{url};
my $decompressor =
$narFile->{compressionType} eq "bzip2" ? "$Nix::Config::bzip2 -d" :
$narFile->{compressionType} eq "xz" ? "$Nix::Config::xz -d" :
die "unknown compression type `$narFile->{compressionType}'";
if ($curStep < $maxStep) {
# The archive will be used a base to a patch.
system("$Nix::Config::bzip2 -d < '$narFilePath' > $tmpNar") == 0
or die "cannot unpack `$narFilePath' into `$v'";
system("$curl '$narFile->{url}' | $decompressor > $tmpNar") == 0
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
} else {
# Unpack the archive into the target path.
print STDERR " unpacking archive...\n";
system("$Nix::Config::bzip2 -d < '$narFilePath' | $Nix::Config::binDir/nix-store --restore '$v'") == 0
or die "cannot unpack `$narFilePath' into `$v'";
# Unpack the archive to the target path.
system("$curl '$narFile->{url}' | $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
}
$finalNarHash = $narFile->{narHash};
@@ -365,21 +363,10 @@ while (scalar @path > 0) {
}
# Make sure that the hash declared in the manifest matches what we
# downloaded and unpacked.
if (defined $finalNarHash) {
my ($hashAlgo, $hash) = parseHash $finalNarHash;
# The hash in the manifest can be either in base-16 or base-32.
# Handle both.
my $hash2 = hashPath($hashAlgo, $hashAlgo eq "sha256" && length($hash) != 64, $targetPath);
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2\n"
if $hash ne $hash2;
} else {
die "cannot check integrity of the downloaded path since its hash is not known\n";
}
# Tell Nix about the expected hash so it can verify it.
die "cannot check integrity of the downloaded path since its hash is not known\n"
unless defined $finalNarHash;
print "$finalNarHash\n";
print STDERR "\n";
+34 -29
View File
@@ -33,29 +33,12 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
my $arg = $ARGV[$n];
if ($arg eq "--help") {
print STDERR <<EOF;
Usage: nix-build [OPTION]... [FILE]...
exec "man nix-build" or die;
}
`nix-build' builds the given Nix expressions (which
default to ./default.nix if none are given). A symlink called
`result' is placed in the current directory.
Flags:
--add-drv-link: create a symlink `derivation' to the store derivation
--drv-link NAME: create symlink NAME instead of `derivation'
--no-out-link: do not create the `result' symlink
--out-link / -o NAME: create symlink NAME instead of `result'
--attr / -A ATTR: select a specific attribute from the Nix expression
--run-env: build dependencies of the specified derivation, then start a
shell with the environment of the derivation
--command: command to run with `--run-env'
--exclude: regexp specifying dependencies to be excluded by `--run-env'
Any additional flags are passed to `nix-store'.
EOF
elsif ($arg eq "--version") {
print "nix-build (Nix) $Nix::Config::version\n";
exit 0;
# '` hack
}
elsif ($arg eq "--add-drv-link") {
@@ -114,26 +97,30 @@ EOF
push @buildArgs, "--dry-run";
$dryRun = 1;
}
elsif ($arg eq "--show-trace") {
push @instArgs, $arg;
}
elsif ($arg eq "-") {
@exprs = ("-");
}
elsif ($arg eq "--verbose" or substr($arg, 0, 2) eq "-v") {
push @buildArgs, $arg;
push @instArgs, $arg;
$verbose = 1;
}
elsif ($arg eq "--quiet") {
elsif ($arg eq "--quiet" || $arg eq "--repair") {
push @buildArgs, $arg;
push @instArgs, $arg;
}
elsif ($arg eq "--run-env") {
$runEnv = 1;
}
elsif ($arg eq "--command") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
@@ -194,15 +181,33 @@ foreach my $expr (@exprs) {
die;
}
# Ugly hackery to make "nix-build -A foo.all" produce symlinks
# ./result, ./result-dev, and so on, rather than ./result,
# ./result-2-dev, and so on. This combines multiple derivation
# paths into one "/nix/store/drv-path!out1,out2,..." argument.
my $prevDrvPath = "";
my @drvPaths2;
foreach my $drvPath (@drvPaths) {
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
my $p = $drvPath; my $output = "out";
if ($drvPath =~ /(.*)!(.*)/) {
$p = $1; $output = $2;
} else {
$p = $drvPath;
}
my $target = readlink $p or die "cannot read symlink `$p'";
print STDERR "derivation is $target\n" if $verbose;
if ($target eq $prevDrvPath) {
push @drvPaths2, (pop @drvPaths2) . "," . $output;
} else {
push @drvPaths2, $target . "!" . $output;
$prevDrvPath = $target;
}
}
# Build.
my @outPaths;
$pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
@buildArgs, @drvPaths;
@buildArgs, @drvPaths2;
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
if (!close OUTPATHS) {
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
+46 -37
View File
@@ -4,6 +4,9 @@ use strict;
use File::Basename;
use File::Path qw(mkpath);
use Nix::Config;
use Nix::Manifest;
Nix::Config::readConfig;
my $manifestDir = $Nix::Config::manifestDir;
@@ -22,7 +25,7 @@ my $nixDefExpr = "$home/.nix-defexpr";
my $userName = getpwuid($<) or die "cannot figure out user name";
my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels";
mkpath(dirname $profile, 0, 0755);
my %channels;
@@ -65,11 +68,13 @@ sub addChannel {
sub removeChannel {
my ($name) = @_;
readChannels;
my $url = $channels{$name};
deleteOldManifests($url . "/MANIFEST", undef) if defined $url;
delete $channels{$name};
writeChannels;
system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0
or die "cannot remove channel `$name'";
or die "cannot remove channel `$name'\n";
}
@@ -77,20 +82,14 @@ sub removeChannel {
# channels.
sub update {
my @channelNames = @_;
readChannels;
# Create the manifests directory if it doesn't exist.
mkdir $manifestDir, 0755 unless -e $manifestDir;
# Do we have write permission to the manifests directory?
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
# Download each channel.
my $exprs = "";
foreach my $name (keys %channels) {
next if scalar @channelNames > 0 && ! grep { $_ eq $name } @{channelNames};
my $url = $channels{$name};
my $origUrl = "$url/MANIFEST";
@@ -101,17 +100,34 @@ sub update {
die "$0: unable to check `$url'\n" if $? != 0;
$headers =~ s/\r//g;
$url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m;
# Pull the channel manifest.
$ENV{'NIX_ORIG_URL'} = $origUrl;
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
or die "cannot pull manifest from `$url'\n";
# Check if the channel advertises a binary cache.
my $binaryCacheURL = `$Nix::Config::curl --silent '$url'/binary-cache-url`;
my $extraAttrs = "";
my $getManifest = ($Nix::Config::config{"force-manifest"} // "false") eq "true";
if ($? == 0 && $binaryCacheURL ne "") {
$extraAttrs .= "binaryCacheURL = \"$binaryCacheURL\"; ";
deleteOldManifests($origUrl, undef);
} else {
$getManifest = 1;
}
if ($getManifest) {
# No binary cache, so pull the channel manifest.
mkdir $manifestDir, 0755 unless -e $manifestDir;
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
$ENV{'NIX_ORIG_URL'} = $origUrl;
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
or die "cannot pull manifest from `$url'\n";
}
# Download the channel tarball.
my $fullURL = "$url/nixexprs.tar.bz2";
my $fullURL = "$url/nixexprs.tar.xz";
system("$Nix::Config::curl --fail --silent --head '$fullURL' > /dev/null") == 0 or
$fullURL = "$url/nixexprs.tar.bz2";
print STDERR "downloading Nix expressions from `$fullURL'...\n";
my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
die "cannot fetch `$fullURL'" if $? != 0;
die "cannot fetch `$fullURL'\n" if $? != 0;
chomp $path;
# If the URL contains a version number, append it to the name
@@ -120,7 +136,7 @@ sub update {
my $cname = $name;
$cname .= $1 if basename($url) =~ /(-\d.*)$/;
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }' ";
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; $extraAttrs }' ";
}
# Unpack the channel tarballs into the Nix store and install them
@@ -139,26 +155,14 @@ sub update {
}
sub usageError {
print STDERR <<EOF;
Usage:
nix-channel --add URL [CHANNEL-NAME]
nix-channel --remove CHANNEL-NAME
nix-channel --list
nix-channel --update [CHANNEL-NAME...]
EOF
exit 1;
}
usageError if scalar @ARGV == 0;
die "$0: argument expected\n" if scalar @ARGV == 0;
while (scalar @ARGV) {
my $arg = shift @ARGV;
if ($arg eq "--add") {
usageError if scalar @ARGV < 1 || scalar @ARGV > 2;
die "$0: `--add' requires one or two arguments\n" if scalar @ARGV < 1 || scalar @ARGV > 2;
my $url = shift @ARGV;
my $name = shift @ARGV;
unless (defined $name) {
@@ -171,13 +175,13 @@ while (scalar @ARGV) {
}
if ($arg eq "--remove") {
usageError if scalar @ARGV != 1;
die "$0: `--remove' requires one argument\n" if scalar @ARGV != 1;
removeChannel(shift @ARGV);
last;
}
if ($arg eq "--list") {
usageError if scalar @ARGV != 0;
die "$0: `--list' requires one argument\n" if scalar @ARGV != 0;
readChannels;
foreach my $name (keys %channels) {
print "$name $channels{$name}\n";
@@ -189,12 +193,17 @@ while (scalar @ARGV) {
update(@ARGV);
last;
}
elsif ($arg eq "--help") {
usageError;
exec "man nix-channel" or die;
}
elsif ($arg eq "--version") {
print "nix-channel (Nix) $Nix::Config::version\n";
exit 0;
}
else {
die "unknown argument `$arg'; try `--help'";
die "unknown argument `$arg'; try `--help'\n";
}
}
+8 -3
View File
@@ -9,10 +9,15 @@ my $profilesDir = "@localstatedir@/nix/profiles";
# Process the command line arguments.
my @args = ();
my $removeOld = 0;
my $dryRun = 0;
for my $arg (@ARGV) {
if ($arg eq "--delete-old" || $arg eq "-d") {
$removeOld = 1;
} elsif ($arg eq "--dry-run") {
$dryRun = 1;
} elsif ($arg eq "--help") {
exec "man nix-collect-garbage" or die;
} else {
push @args, $arg;
}
@@ -35,13 +40,13 @@ sub removeOldGenerations {
$name = $dir . "/" . $name;
if (-l $name && (readlink($name) =~ /link/)) {
print STDERR "removing old generations of profile $name\n";
system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", "old");
system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", "old", $dryRun ? "--dry-run" : ());
}
elsif (! -l $name && -d $name) {
removeOldGenerations $name;
}
}
closedir $dh or die;
}
@@ -49,4 +54,4 @@ removeOldGenerations $profilesDir if $removeOld;
# Run the actual garbage collector.
exec "$Nix::Config::binDir/nix-store", "--gc", @args;
exec "$Nix::Config::binDir/nix-store", "--gc", @args unless $dryRun;
+19 -13
View File
@@ -17,19 +17,14 @@ EOF
# Get the target host.
my $sshHost;
my $sign = 0;
my $compressor = "";
my $decompressor = "";
my $progressViewer = "";
my $toMode = 1;
my $includeOutputs = 0;
my $dryRun = 0;
my $useSubstitutes = 0;
# !!! Copied from nix-pack-closure, should put this in a module.
@@ -37,8 +32,11 @@ my @storePaths = ();
while (@ARGV) {
my $arg = shift @ARGV;
if ($arg eq "--sign") {
if ($arg eq "--help") {
exec "man nix-copy-closure" or die;
}
elsif ($arg eq "--sign") {
$sign = 1;
}
elsif ($arg eq "--gzip") {
@@ -68,6 +66,9 @@ while (@ARGV) {
elsif ($arg eq "--dry-run") {
$dryRun = 1;
}
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
$useSubstitutes = 1;
}
elsif (!defined $sshHost) {
$sshHost = $arg;
}
@@ -81,7 +82,9 @@ openSSHConnection $sshHost or die "$0: unable to start SSH\n";
if ($toMode) { # Copy TO the remote machine.
Nix::CopyClosure::copyTo($sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer);
Nix::CopyClosure::copyTo(
$sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor,
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes);
}
else { # Copy FROM the remote machine.
@@ -92,7 +95,7 @@ else { # Copy FROM the remote machine.
my $extraOpts = $includeOutputs ? "--include-outputs" : "";
my $pid = open(READ,
"set -f; ssh @sshOpts $sshHost nix-store --query --requisites $extraOpts @storePaths|") or die;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
@@ -104,10 +107,13 @@ else { # Copy FROM the remote machine.
# Export the store paths on the remote machine and import them locally.
if (scalar @missing > 0) {
print STDERR "copying ", scalar @missing, " missing paths from $sshHost...\n";
$compressor = "| $compressor" if $compressor ne "";
$decompressor = "$decompressor |" if $decompressor ne "";
$progressViewer = "$progressViewer |" if $progressViewer ne "";
unless ($dryRun) {
if ($useSubstitutes) {
system "$Nix::Config::binDir/nix-store -r --ignore-unknown @missing";
}
$compressor = "| $compressor" if $compressor ne "";
$decompressor = "$decompressor |" if $decompressor ne "";
$progressViewer = "$progressViewer |" if $progressViewer ne "";
my $extraOpts = $sign ? "--sign" : "";
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $progressViewer $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0
or die "copying store paths from remote machine `$sshHost' failed: $?";
+24 -32
View File
@@ -3,27 +3,11 @@
use strict;
use File::Temp qw(tempdir);
use Nix::Config;
sub usageError {
print STDERR <<EOF;
Usage: nix-install-package (FILE | --url URL)
Install a Nix Package (.nixpkg) either directly from FILE or by
downloading it from URL.
Flags:
--profile / -p LINK: install into the specified profile
--non-interactive: don't run inside a new terminal
EOF
; # '
exit 1;
}
use Nix::Utils;
# Parse the command line arguments.
my @args = @ARGV;
usageError if scalar @args == 0;
my $source;
my $fromURL = 0;
@@ -33,14 +17,14 @@ my $interactive = 1;
while (scalar @args) {
my $arg = shift @args;
if ($arg eq "--help") {
usageError;
exec "man nix-install-package" or die;
}
elsif ($arg eq "--url") {
$fromURL = 1;
}
elsif ($arg eq "--profile" || $arg eq "-p") {
my $profile = shift @args;
usageError if !defined $profile;
die "$0: `--profile' requires an argument\n" if !defined $profile;
push @extraNixEnvArgs, "-p", $profile;
}
elsif ($arg eq "--non-interactive") {
@@ -51,7 +35,7 @@ while (scalar @args) {
}
}
usageError unless defined $source;
die "$0: please specify a .nixpkg file or URL\n" unless defined $source;
# Re-execute in a terminal, if necessary, so that if we're executed
@@ -72,7 +56,7 @@ my $tmpDir = tempdir("nix-install-package.XXXXXX", CLEANUP => 1, TMPDIR => 1)
sub barf {
my $msg = shift;
print "$msg\n";
print "\nInstallation failed: $msg\n";
<STDIN> if $interactive;
exit 1;
}
@@ -92,7 +76,6 @@ open PKGFILE, "<$pkgFile" or barf "cannot open `$pkgFile': $!";
my $contents = <PKGFILE>;
close PKGFILE;
my $urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+ )";
my $nameRE = "(?: [A-Za-z0-9\+\-\.\_\?\=]+ )"; # see checkStoreName()
my $systemRE = "(?: [A-Za-z0-9\+\-\_]+ )";
my $pathRE = "(?: \/ [\/A-Za-z0-9\+\-\.\_\?\=]* )";
@@ -101,7 +84,7 @@ my $pathRE = "(?: \/ [\/A-Za-z0-9\+\-\.\_\?\=]* )";
# store path. We'll let nix-env do that.
$contents =~
/ ^ \s* (\S+) \s+ ($urlRE) \s+ ($nameRE) \s+ ($systemRE) \s+ ($pathRE) \s+ ($pathRE) /x
/ ^ \s* (\S+) \s+ ($Nix::Utils::urlRE) \s+ ($nameRE) \s+ ($systemRE) \s+ ($pathRE) \s+ ($pathRE) ( \s+ ($Nix::Utils::urlRE) )? /x
or barf "invalid package contents";
my $version = $1;
my $manifestURL = $2;
@@ -109,6 +92,7 @@ my $drvName = $3;
my $system = $4;
my $drvPath = $5;
my $outPath = $6;
my $binaryCacheURL = $8;
barf "invalid package version `$version'" unless $version eq "NIXPKG1";
@@ -122,17 +106,25 @@ if ($interactive) {
}
# Store the manifest in the temporary directory so that we don't
# pollute /nix/var/nix/manifests. This also requires that we don't
# use the Nix daemon (because otherwise download-using-manifests won't
# see our NIX_MANIFESTS_DIRS environment variable).
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
$ENV{NIX_REMOTE} = "";
if (defined $binaryCacheURL) {
push @extraNixEnvArgs, "--option", "binary-caches", $binaryCacheURL;
print "\nPulling manifests...\n";
system("$Nix::Config::binDir/nix-pull", $manifestURL) == 0
or barf "nix-pull failed: $?";
} else {
# Store the manifest in the temporary directory so that we don't
# pollute /nix/var/nix/manifests. This also requires that we
# don't use the Nix daemon (because otherwise
# download-using-manifests won't see our NIX_MANIFESTS_DIRS
# environment variable).
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
$ENV{NIX_REMOTE} = "";
print "\nPulling manifests...\n";
system("$Nix::Config::binDir/nix-pull", $manifestURL) == 0
or barf "nix-pull failed: $?";
}
print "\nInstalling package...\n";
+21 -19
View File
@@ -6,12 +6,30 @@ use File::Temp qw(tempdir);
use File::stat;
use Nix::Store;
use Nix::Config;
use Nix::Utils;
my $url = shift;
my $expHash = shift;
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256";
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256"; # obsolete
my $cacheDir = $ENV{'NIX_DOWNLOAD_CACHE'};
my @args;
my $arg;
while ($arg = shift) {
if ($arg eq "--help") {
exec "man nix-prefetch-url" or die;
} elsif ($arg eq "--type") {
$hashType = shift;
die "$0: `$arg' requires an argument\n" unless defined $hashType;
} elsif (substr($arg, 0, 1) eq "-") {
die "$0: unknown flag `$arg'\n";
} else {
push @args, $arg;
}
}
my $url = $args[0];
my $expHash = $args[1];
if (!defined $url || $url eq "") {
print STDERR <<EOF
Usage: nix-prefetch-url URL [EXPECTED-HASH]
@@ -20,22 +38,6 @@ EOF
exit 1;
}
sub writeFile {
my ($fn, $s) = @_;
open TMP, ">$fn" or die;
print TMP "$s" or die;
close TMP or die;
}
sub readFile {
local $/ = undef;
my ($fn) = @_;
open TMP, "<$fn" or die;
my $s = <TMP>;
close TMP or die;
return $s;
}
my $tmpDir = tempdir("nix-prefetch-url.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
+11 -44
View File
@@ -1,50 +1,17 @@
export NIX_USER_PROFILE_DIR=@localstatedir@/nix/profiles/per-user/$USER
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
if test -n "$HOME"; then
NIX_LINK="$HOME/.nix-profile"
# Set up the per-user profile.
mkdir -m 0755 -p $NIX_USER_PROFILE_DIR
if test "$(stat --printf '%u' $NIX_USER_PROFILE_DIR)" != "$(id -u)"; then
echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
fi
if ! test -L $HOME/.nix-profile; then
echo "creating $HOME/.nix-profile" >&2
if test "$USER" != root; then
@coreutils@/ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
else
# Root installs in the system-wide profile by default.
@coreutils@/ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
# Set the default profile.
if ! [ -L "$NIX_LINK" ]; then
echo "creating $NIX_LINK" >&2
_NIX_DEF_LINK=@localstatedir@/nix/profiles/default
@coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK"
fi
fi
export PATH="$HOME/.nix-profile/bin:$PATH"
# Subscribe the root user to the NixOS channel by default.
if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
echo "http://nixos.org/releases/nixos/channels/nixos-unstable nixos" > $HOME/.nix-channels
fi
# Create the per-user garbage collector roots directory.
NIX_USER_GCROOTS_DIR=@localstatedir@/nix/gcroots/per-user/$USER
mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
fi
# Set up a default Nix expression from which to install stuff.
if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
echo "creating $HOME/.nix-defexpr" >&2
rm -f $HOME/.nix-defexpr
mkdir $HOME/.nix-defexpr
if [ "$USER" != root ]; then
@coreutils@/ln -s @localstatedir@/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
# Subscribe the root user to the Nixpkgs channel by default.
if [ ! -e $HOME/.nix-channels ]; then
echo "http://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
fi
fi
# Set up secure multi-user builds: non-root users build through the
# Nix daemon.
if test "$USER" != root; then
export NIX_REMOTE=daemon
else
unset NIX_REMOTE
export PATH=$NIX_LINK/bin:$PATH
fi
+6 -15
View File
@@ -76,31 +76,22 @@ sub processURL {
open URL, ">$urlFile" or die "cannot create `$urlFile'";
print URL $origUrl;
close URL;
my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
unlink $finalPath if -e $finalPath;
symlink("$manifest", "$finalPath")
or die "cannot link `$finalPath to `$manifest'";
# Delete all old manifests downloaded from this URL.
for my $urlFile2 (glob "$manifestDir/*.url") {
next if $urlFile eq $urlFile2;
open URL, "<$urlFile2" or die;
my $url2 = <URL>;
chomp $url2;
close URL;
next unless $origUrl eq $url2;
my $base = $urlFile2; $base =~ s/.url$//;
unlink "${base}.url";
unlink "${base}.nixmanifest";
}
deleteOldManifests($origUrl, $urlFile);
}
while (@ARGV) {
my $url = shift @ARGV;
if ($url eq "--skip-wrong-store") {
if ($url eq "--help") {
exec "man nix-pull" or die;
} elsif ($url eq "--skip-wrong-store") {
# No-op, no longer supported.
} else {
processURL $url;
+187 -195
View File
@@ -1,85 +1,83 @@
#! @perl@ -w @perlFlags@
use strict;
use File::Basename;
use File::Temp qw(tempdir);
use File::Path qw(mkpath);
use File::stat;
use File::Copy;
use Nix::Config;
use Nix::Store;
use Nix::Manifest;
my $hashAlgo = "sha256";
use Nix::Utils;
my $tmpDir = tempdir("nix-push.XXXXXX", CLEANUP => 1, TMPDIR => 1)
or die "cannot create a temporary directory";
my $nixExpr = "$tmpDir/create-nars.nix";
my $manifest = "$tmpDir/MANIFEST";
my $curl = "$Nix::Config::curl --fail --silent";
my $extraCurlFlags = ${ENV{'CURL_FLAGS'}};
$curl = "$curl $extraCurlFlags" if defined $extraCurlFlags;
# Parse the command line.
my $localCopy;
my $localArchivesDir;
my $localManifestFile;
my $compressionType = "xz";
my $force = 0;
my $destDir;
my $writeManifest = 0;
my $manifestPath;
my $archivesURL;
my $link = 0;
my @roots;
my $targetArchivesUrl;
for (my $n = 0; $n < scalar @ARGV; $n++) {
my $arg = $ARGV[$n];
my $archivesPutURL;
my $archivesGetURL;
my $manifestPutURL;
sub showSyntax {
print STDERR <<EOF
Usage: nix-push --copy ARCHIVES_DIR MANIFEST_FILE PATHS...
or: nix-push ARCHIVES_PUT_URL ARCHIVES_GET_URL MANIFEST_PUT_URL PATHS...
`nix-push' copies or uploads the closure of PATHS to the given
destination.
EOF
; # `
exit 1;
}
showSyntax if scalar @ARGV < 1;
if ($ARGV[0] eq "--copy") {
showSyntax if scalar @ARGV < 3;
$localCopy = 1;
shift @ARGV;
$localArchivesDir = shift @ARGV;
$localManifestFile = shift @ARGV;
if ($ARGV[0] eq "--target") {
shift @ARGV;
$targetArchivesUrl = shift @ARGV;
}
else {
$targetArchivesUrl = "file://$localArchivesDir";
if ($arg eq "--help") {
exec "man nix-push" or die;
} elsif ($arg eq "--bzip2") {
$compressionType = "bzip2";
} elsif ($arg eq "--force") {
$force = 1;
} elsif ($arg eq "--dest") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
$destDir = $ARGV[$n];
mkpath($destDir, 0, 0755);
} elsif ($arg eq "--manifest") {
$writeManifest = 1;
} elsif ($arg eq "--manifest-path") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
$manifestPath = $ARGV[$n];
$writeManifest = 1;
mkpath(dirname($manifestPath), 0, 0755);
} elsif ($arg eq "--url-prefix") {
$n++;
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
$archivesURL = $ARGV[$n];
} elsif ($arg eq "--link") {
$link = 1;
} elsif (substr($arg, 0, 1) eq "-") {
die "$0: unknown flag `$arg'\n";
} else {
push @roots, $arg;
}
}
else {
showSyntax if scalar @ARGV < 3;
$localCopy = 0;
$archivesPutURL = shift @ARGV;
$archivesGetURL = shift @ARGV;
$manifestPutURL = shift @ARGV;
}
die "$0: please specify a destination directory\n" if !defined $destDir;
$archivesURL = "file://$destDir" unless defined $archivesURL;
# From the given store paths, determine the set of requisite store
# paths, i.e, the paths required to realise them.
my %storePaths;
foreach my $path (@ARGV) {
die unless $path =~ /^\//;
# Get all paths referenced by the normalisation of the given
foreach my $path (@roots) {
# Get all paths referenced by the normalisation of the given
# Nix expression.
my $pid = open(READ,
"$Nix::Config::binDir/nix-store --query --requisites --force-realise " .
"--include-outputs '$path'|") or die;
while (<READ>) {
chomp;
die "bad: $_" unless /^\//;
@@ -92,19 +90,50 @@ foreach my $path (@ARGV) {
my @storePaths = keys %storePaths;
# For each path, create a Nix expression that turns the path into
# a Nix archive.
# Don't create archives for files that are already in the binary cache.
my @storePaths2;
my %narFiles;
foreach my $storePath (@storePaths) {
my $pathHash = substr(basename($storePath), 0, 32);
my $narInfoFile = "$destDir/$pathHash.narinfo";
if (-e $narInfoFile) {
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile));
my $narFile = "$destDir/$narInfo->{url}";
if (-e $narFile) {
print STDERR "skipping existing $storePath\n";
# Add the NAR info to $narFiles if we're writing a
# manifest.
$narFiles{$storePath} = [
{ url => ("$archivesURL/" . basename $narInfo->{url})
, hash => $narInfo->{fileHash}
, size => $narInfo->{fileSize}
, compressionType => $narInfo->{compression}
, narHash => $narInfo->{narHash}
, narSize => $narInfo->{narSize}
, references => join(" ", map { "$Nix::Config::storeDir/$_" } @{$narInfo->{refs}})
, deriver => $narInfo->{deriver} ? "$Nix::Config::storeDir/$narInfo->{deriver}" : undef
}
] if $writeManifest;
next;
}
}
push @storePaths2, $storePath;
}
# Create a list of Nix derivations that turn each path into a Nix
# archive.
open NIX, ">$nixExpr";
print NIX "[";
foreach my $storePath (@storePaths) {
foreach my $storePath (@storePaths2) {
die unless ($storePath =~ /\/[0-9a-z]{32}[^\"\\\$]*$/);
# Construct a Nix expression that creates a Nix archive.
my $nixexpr =
my $nixexpr =
"(import <nix/nar.nix> " .
"{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"$hashAlgo\"; }) ";
"{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"sha256\"; compressionType = \"$compressionType\"; }) ";
print NIX $nixexpr;
}
@@ -112,172 +141,135 @@ print NIX "]";
close NIX;
# Instantiate store derivations from the Nix expression.
my @storeExprs;
print STDERR "instantiating store derivations...\n";
my $pid = open(READ, "$Nix::Config::binDir/nix-instantiate $nixExpr|")
or die "cannot run nix-instantiate";
# Build the Nix expression.
print STDERR "building compressed archives...\n";
my @narPaths;
my $pid = open(READ, "$Nix::Config::binDir/nix-build $nixExpr -o $tmpDir/result |")
or die "cannot run nix-build";
while (<READ>) {
chomp;
die unless /^\//;
push @storeExprs, $_;
push @narPaths, $_;
}
close READ or die "nix-instantiate failed: $?";
close READ or die "nix-build failed: $?";
# Build the derivations.
print STDERR "creating archives...\n";
my @narPaths;
my @tmp = @storeExprs;
while (scalar @tmp > 0) {
my $n = scalar @tmp;
if ($n > 256) { $n = 256 };
my @tmp2 = @tmp[0..$n - 1];
@tmp = @tmp[$n..scalar @tmp - 1];
my $pid = open(READ, "$Nix::Config::binDir/nix-store --realise @tmp2|")
or die "cannot run nix-store";
while (<READ>) {
chomp;
die unless (/^\//);
push @narPaths, "$_";
}
close READ or die "nix-store failed: $?";
# Write the cache info file.
my $cacheInfoFile = "$destDir/nix-cache-info";
if (! -e $cacheInfoFile) {
open FILE, ">$cacheInfoFile" or die "cannot create $cacheInfoFile: $!";
print FILE "StoreDir: $Nix::Config::storeDir\n";
print FILE "WantMassQuery: 0\n"; # by default, don't hit this cache for "nix-env -qas"
close FILE;
}
# Create the manifest.
print STDERR "creating manifest...\n";
# Copy the archives and the corresponding NAR info files.
print STDERR "copying archives...\n";
my %narFiles;
my %patches;
my $totalNarSize = 0;
my $totalCompressedSize = 0;
my @narArchives;
for (my $n = 0; $n < scalar @storePaths; $n++) {
my $storePath = $storePaths[$n];
for (my $n = 0; $n < scalar @storePaths2; $n++) {
my $storePath = $storePaths2[$n];
my $narDir = $narPaths[$n];
$storePath =~ /\/([^\/]*)$/;
my $basename = $1;
defined $basename or die;
my $baseName = basename $storePath;
open HASH, "$narDir/narbz2-hash" or die "cannot open narbz2-hash";
my $narbz2Hash = <HASH>;
chomp $narbz2Hash;
$narbz2Hash =~ /^[0-9a-z]+$/ or die "invalid hash";
close HASH;
my $narName = "$narbz2Hash.nar.bz2";
my $narFile = "$narDir/$narName";
(-f $narFile) or die "narfile for $storePath not found";
push @narArchives, $narFile;
my $narbz2Size = stat($narFile)->size;
my $references = `$Nix::Config::binDir/nix-store --query --references '$storePath'`;
die "cannot query references for `$storePath'" if $? != 0;
$references = join(" ", split(" ", $references));
my $deriver = `$Nix::Config::binDir/nix-store --query --deriver '$storePath'`;
die "cannot query deriver for `$storePath'" if $? != 0;
chomp $deriver;
$deriver = "" if $deriver eq "unknown-deriver";
my $narHash = `$Nix::Config::binDir/nix-store --query --hash '$storePath'`;
die "cannot query hash for `$storePath'" if $? != 0;
chomp $narHash;
# Get info about the store path.
my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($storePath, 1);
# In some exceptional cases (such as VM tests that use the Nix
# store of the host), the database doesn't contain the hash. So
# compute it.
if ($narHash =~ /^sha256:0*$/) {
$narHash = `$Nix::Config::binDir/nix-hash --type sha256 --base32 '$storePath'`;
die "cannot hash `$storePath'" if $? != 0;
my $nar = "$tmpDir/nar";
system("$Nix::Config::binDir/nix-store --dump $storePath > $nar") == 0
or die "cannot dump $storePath\n";
$narHash = `$Nix::Config::binDir/nix-hash --type sha256 --base32 --flat $nar`;
die "cannot hash `$nar'" if $? != 0;
chomp $narHash;
$narHash = "sha256:$narHash";
$narSize = stat("$nar")->size;
unlink $nar or die;
}
my $narSize = `$Nix::Config::binDir/nix-store --query --size '$storePath'`;
die "cannot query size for `$storePath'" if $? != 0;
chomp $narSize;
$totalNarSize += $narSize;
my $url;
if ($localCopy) {
$url = "$targetArchivesUrl/$narName";
} else {
$url = "$archivesGetURL/$narName";
# Get info about the compressed NAR.
open HASH, "$narDir/nar-compressed-hash" or die "cannot open nar-compressed-hash";
my $compressedHash = <HASH>;
chomp $compressedHash;
$compressedHash =~ /^[0-9a-z]+$/ or die "invalid hash";
close HASH;
my $narName = "$compressedHash.nar." . ($compressionType eq "xz" ? "xz" : "bz2");
my $narFile = "$narDir/$narName";
(-f $narFile) or die "NAR file for $storePath not found";
my $compressedSize = stat($narFile)->size;
$totalCompressedSize += $compressedSize;
printf STDERR "%s [%.2f MiB, %.1f%%]\n", $storePath,
$compressedSize / (1024 * 1024), $compressedSize / $narSize * 100;
# Copy the compressed NAR.
my $dst = "$destDir/$narName";
if (! -f $dst) {
my $tmp = "$destDir/.tmp.$$.$narName";
if ($link) {
link($narFile, $tmp) or die "cannot link $tmp to $narFile: $!\n";
} else {
copy($narFile, $tmp) or die "cannot copy $narFile to $tmp: $!\n";
}
rename($tmp, $dst) or die "cannot rename $tmp to $dst: $!\n";
}
# Write the info file.
my $info;
$info .= "StorePath: $storePath\n";
$info .= "URL: $narName\n";
$info .= "Compression: $compressionType\n";
$info .= "FileHash: sha256:$compressedHash\n";
$info .= "FileSize: $compressedSize\n";
$info .= "NarHash: $narHash\n";
$info .= "NarSize: $narSize\n";
$info .= "References: " . join(" ", map { basename $_ } @{$refs}) . "\n";
if (defined $deriver) {
$info .= "Deriver: " . basename $deriver . "\n";
if (isValidPath($deriver)) {
my $drv = derivationFromPath($deriver);
$info .= "System: $drv->{platform}\n";
}
}
my $pathHash = substr(basename($storePath), 0, 32);
$dst = "$destDir/$pathHash.narinfo";
if ($force || ! -f $dst) {
my $tmp = "$destDir/.tmp.$$.$pathHash.narinfo";
open INFO, ">$tmp" or die;
print INFO "$info" or die;
close INFO or die;
rename($tmp, $dst) or die "cannot rename $tmp to $dst: $!\n";
}
$narFiles{$storePath} = [
{ url => $url
, hash => "$hashAlgo:$narbz2Hash"
, size => $narbz2Size
{ url => "$archivesURL/$narName"
, hash => "sha256:$compressedHash"
, size => $compressedSize
, compressionType => $compressionType
, narHash => "$narHash"
, narSize => $narSize
, references => $references
, references => join(" ", @{$refs})
, deriver => $deriver
}
];
] if $writeManifest;
}
writeManifest $manifest, \%narFiles, \%patches;
printf STDERR "total compressed size %.2f MiB, %.1f%%\n",
$totalCompressedSize / (1024 * 1024), $totalCompressedSize / ($totalNarSize || 1) * 100;
sub copyFile {
my $src = shift;
my $dst = shift;
my $tmp = "$dst.tmp.$$";
system("@coreutils@/cp", $src, $tmp) == 0 or die "cannot copy file";
rename($tmp, $dst) or die "cannot rename file: $!";
}
# Upload/copy the archives.
print STDERR "uploading/copying archives...\n";
sub archiveExists {
my $name = shift;
print STDERR " HEAD on $archivesGetURL/$name\n";
return system("$curl --head $archivesGetURL/$name > /dev/null") == 0;
}
foreach my $narArchive (@narArchives) {
$narArchive =~ /\/([^\/]*)$/;
my $basename = $1;
if ($localCopy) {
# Since nix-push creates $dst atomically, if it exists we
# don't have to copy again.
my $dst = "$localArchivesDir/$basename";
if (! -f "$localArchivesDir/$basename") {
print STDERR " $narArchive\n";
copyFile $narArchive, $dst;
}
}
else {
if (!archiveExists("$basename")) {
print STDERR " $narArchive\n";
system("$curl --show-error --upload-file " .
"'$narArchive' '$archivesPutURL/$basename' > /dev/null") == 0 or
die "curl failed on $narArchive: $?";
}
}
}
# Upload the manifest.
print STDERR "uploading manifest...\n";
if ($localCopy) {
copyFile $manifest, $localManifestFile;
copyFile "$manifest.bz2", "$localManifestFile.bz2";
} else {
system("$curl --show-error --upload-file " .
"'$manifest' '$manifestPutURL' > /dev/null") == 0 or
die "curl failed on $manifest: $?";
system("$curl --show-error --upload-file " .
"'$manifest'.bz2 '$manifestPutURL'.bz2 > /dev/null") == 0 or
die "curl failed on $manifest: $?";
}
# Optionally write a manifest.
writeManifest($manifestPath // "$destDir/MANIFEST", \%narFiles, \()) if $writeManifest;
+2 -2
View File
@@ -1,3 +1,3 @@
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
SUBDIRS = boost libutil libstore libmain nix-store nix-hash \
libexpr nix-instantiate nix-env nix-daemon nix-setuid-helper \
nix-log2xml bsdiff-4.3
-6
View File
@@ -1,6 +0,0 @@
noinst_PROGRAMS = bin2c
bin2c_SOURCES = bin2c.c
bin2c$(EXEEXT): bin2c.c
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) -o bin2c bin2c.c
-23
View File
@@ -1,23 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
void print(const char *format, ...)
{
va_list ap;
va_start(ap, format);
if (vprintf(format, ap) < 0) abort();
va_end(ap);
}
int main(int argc, char * * argv)
{
int c;
if (argc != 2) abort();
print("static unsigned char %s[] = { ", argv[1]);
while ((c = getchar()) != EOF) {
print("0x%02x, ", (unsigned char) c);
}
print("0 };\n");
return 0;
}
+1 -1
View File
@@ -10,7 +10,7 @@ namespace nix {
void findAlongAttrPath(EvalState & state, const string & attrPath,
Bindings & autoArgs, Expr * e, Value & v)
{
Strings tokens = tokenizeString(attrPath, ".");
Strings tokens = tokenizeString<Strings>(attrPath, ".");
Error attrError =
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
+88 -20
View File
@@ -138,12 +138,18 @@ EvalState::EvalState()
, sName(symbols.create("name"))
, sSystem(symbols.create("system"))
, sOverrides(symbols.create("__overrides"))
, sOutputs(symbols.create("outputs"))
, sOutputName(symbols.create("outputName"))
, sIgnoreNulls(symbols.create("__ignoreNulls"))
, baseEnv(allocEnv(128))
, baseEnvDispl(0)
, staticBaseEnv(false, 0)
, repair(false)
{
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
nrListConcats = nrPrimOpCalls = nrFunctionCalls = 0;
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
#if HAVE_BOEHMGC
static bool gcInitialised = true;
@@ -177,9 +183,9 @@ EvalState::EvalState()
/* Initialise the Nix expression search path. */
searchPathInsertionPoint = searchPath.end();
Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":");
Strings paths = tokenizeString<Strings>(getEnv("NIX_PATH", ""), ":");
foreach (Strings::iterator, i, paths) addToSearchPath(*i);
addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
searchPathInsertionPoint = searchPath.begin();
createBaseEnv();
@@ -300,8 +306,10 @@ inline Value * EvalState::lookupVar(Env * env, const VarRef & var)
if (var.fromWith) {
while (1) {
Bindings::iterator j = env->values[0]->attrs->find(var.name);
if (j != env->values[0]->attrs->end())
if (j != env->values[0]->attrs->end()) {
if (countCalls && j->pos) attrSelects[*j->pos]++;
return j->value;
}
if (env->prevWith == 0)
throwEvalError("undefined variable `%1%'", var.name);
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
@@ -344,7 +352,7 @@ void EvalState::mkList(Value & v, unsigned int length)
{
v.type = tList;
v.list.length = length;
v.list.elems = (Value * *) GC_MALLOC(length * sizeof(Value *));
v.list.elems = length ? (Value * *) GC_MALLOC(length * sizeof(Value *)) : 0;
nrListElems += length;
}
@@ -619,8 +627,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}
vAttrs = j->value;
pos = j->pos;
if (state.countCalls && pos) state.attrSelects[*pos]++;
}
state.forceValue(*vAttrs);
} catch (Error & e) {
@@ -700,6 +710,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
vArgs[n--] = arg->primOpApp.right;
/* And call the primop. */
nrPrimOpCalls++;
if (countCalls) primOpCalls[primOp->primOp->name]++;
try {
primOp->primOp->fun(*this, vArgs, v);
} catch (Error & e) {
@@ -716,7 +728,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
}
if (fun.type != tLambda)
throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
throwTypeError("attempt to call something which is not a function but %1%",
showType(fun));
unsigned int size =
@@ -760,6 +772,9 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
}
nrFunctionCalls++;
if (countCalls) functionCalls[fun.lambda.fun->pos]++;
try {
fun.lambda.fun->body->eval(*this, env2, v);
} catch (Error & e) {
@@ -778,20 +793,20 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
return;
}
Value actualArgs;
mkAttrs(actualArgs, fun.lambda.fun->formals->formals.size());
Value * actualArgs = allocValue();
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
Bindings::iterator j = args.find(i->name);
if (j != args.end())
actualArgs.attrs->push_back(*j);
actualArgs->attrs->push_back(*j);
else if (!i->def)
throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
}
actualArgs.attrs->sort();
actualArgs->attrs->sort();
callFunction(fun, actualArgs, res);
callFunction(fun, *actualArgs, res);
}
@@ -902,14 +917,36 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
{
Value v1; e1->eval(state, env, v1);
state.forceList(v1);
Value v2; e2->eval(state, env, v2);
state.forceList(v2);
state.mkList(v, v1.list.length + v2.list.length);
for (unsigned int n = 0; n < v1.list.length; ++n)
v.list.elems[n] = v1.list.elems[n];
for (unsigned int n = 0; n < v2.list.length; ++n)
v.list.elems[n + v1.list.length] = v2.list.elems[n];
Value * lists[2] = { &v1, &v2 };
state.concatLists(v, 2, lists);
}
void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists)
{
nrListConcats++;
Value * nonEmpty = 0;
unsigned int len = 0;
for (unsigned int n = 0; n < nrLists; ++n) {
forceList(*lists[n]);
unsigned int l = lists[n]->list.length;
len += l;
if (l) nonEmpty = lists[n];
}
if (nonEmpty && len == nonEmpty->list.length) {
v = *nonEmpty;
return;
}
mkList(v, len);
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
unsigned int l = lists[n]->list.length;
memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *));
pos += l;
}
}
@@ -932,7 +969,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
isPath = vStr.type == tPath;
first = false;
}
s << state.coerceToString(vStr, context, false, !isPath);
}
@@ -1058,9 +1095,9 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (srcToStore[path] != "")
dstPath = srcToStore[path];
else {
dstPath = readOnlyMode
dstPath = settings.readOnlyMode
? computeStorePathForPath(path).first
: store->addToStore(path);
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
srcToStore[path] = dstPath;
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
% path % dstPath);
@@ -1207,6 +1244,7 @@ void EvalState::printStats()
% nrEnvs % (nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *)));
printMsg(v, format(" list elements: %1% (%2% bytes)")
% nrListElems % (nrListElems * sizeof(Value *)));
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
printMsg(v, format(" values allocated: %1% (%2% bytes)")
% nrValues % (nrValues * sizeof(Value)));
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
@@ -1216,6 +1254,36 @@ void EvalState::printStats()
printMsg(v, format(" number of thunks: %1%") % nrThunks);
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
printMsg(v, format(" number of primop calls: %1%") % nrPrimOpCalls);
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
if (countCalls) {
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
std::multimap<unsigned int, Symbol> primOpCalls_;
foreach (PrimOpCalls::iterator, i, primOpCalls)
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
printMsg(v, format("%1$10d %2%") % i->first % i->second);
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
typedef std::multimap<unsigned int, Pos> FunctionCalls_;
std::multimap<unsigned int, Pos> functionCalls_;
foreach (FunctionCalls::iterator, i, functionCalls)
functionCalls_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
printMsg(v, format("%1$10d %2%") % i->first % i->second);
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
typedef std::multimap<unsigned int, Pos> AttrSelects_;
std::multimap<unsigned int, Pos> attrSelects_;
foreach (AttrSelects::iterator, i, attrSelects)
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
printMsg(v, format("%1$10d %2%") % i->first % i->second);
}
}
+26 -4
View File
@@ -93,7 +93,11 @@ public:
SymbolTable symbols;
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName,
sSystem, sOverrides;
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls;
/* If set, force copying files to the Nix store even if they
already exist there. */
bool repair;
private:
SrcToStore srcToStore;
@@ -232,11 +236,13 @@ public:
void mkAttrs(Value & v, unsigned int expected);
void mkThunk_(Value & v, Expr * expr);
void concatLists(Value & v, unsigned int nrLists, Value * * lists);
/* Print statistics. */
void printStats();
private:
unsigned long nrEnvs;
unsigned long nrValuesInEnvs;
unsigned long nrValues;
@@ -244,9 +250,25 @@ private:
unsigned long nrAttrsets;
unsigned long nrOpUpdates;
unsigned long nrOpUpdateValuesCopied;
friend class RecursionCounter;
unsigned long nrListConcats;
unsigned long nrPrimOpCalls;
unsigned long nrFunctionCalls;
bool countCalls;
typedef std::map<Symbol, unsigned int> PrimOpCalls;
PrimOpCalls primOpCalls;
typedef std::map<Pos, unsigned int> FunctionCalls;
FunctionCalls functionCalls;
typedef std::map<Pos, unsigned int> AttrSelects;
AttrSelects attrSelects;
friend class ExprOpUpdate;
friend class ExprOpConcatLists;
friend class ExprSelect;
friend void prim_getAttr(EvalState & state, Value * * args, Value & v);
};
+64 -19
View File
@@ -28,12 +28,53 @@ string DrvInfo::queryOutPath(EvalState & state) const
}
DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
{
if (outputs.empty()) {
/* Get the outputs list. */
Bindings::iterator i = attrs->find(state.sOutputs);
if (i == attrs->end())
outputs["out"] = queryOutPath(state);
else {
state.forceList(*i->value);
/* For each output... */
for (unsigned int j = 0; j < i->value->list.length; ++j) {
/* Evaluate the corresponding attribute set. */
string name = state.forceStringNoCtx(*i->value->list.elems[j]);
Bindings::iterator out = attrs->find(state.symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error?
state.forceAttrs(*out->value);
/* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state.sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context;
outputs[name] = state.coerceToPath(*outPath->value, context);
}
}
}
return outputs;
}
string DrvInfo::queryOutputName(EvalState & state) const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state.sOutputName);
(string &) outputName = i != attrs->end() ? state.forceStringNoCtx(*i->value) : "";
}
return outputName;
}
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
{
if (metaInfoRead) return meta;
(bool &) metaInfoRead = true;
Bindings::iterator a = attrs->find(state.sMeta);
if (a == attrs->end()) return meta; /* fine, empty meta information */
@@ -84,7 +125,8 @@ typedef set<Bindings *> Done;
makes sense for the caller to recursively search for derivations in
`v'. */
static bool getDerivation(EvalState & state, Value & v,
const string & attrPath, DrvInfos & drvs, Done & done)
const string & attrPath, DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
try {
state.forceValue(v);
@@ -96,7 +138,7 @@ static bool getDerivation(EvalState & state, Value & v,
done.insert(v.attrs);
DrvInfo drv;
Bindings::iterator i = v.attrs->find(state.sName);
/* !!! We really would like to have a decent back trace here. */
if (i == v.attrs->end()) throw TypeError("derivation name missing");
@@ -114,18 +156,20 @@ static bool getDerivation(EvalState & state, Value & v,
drvs.push_back(drv);
return false;
} catch (AssertionError & e) {
return false;
if (ignoreAssertionFailures) return false;
throw;
}
}
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
bool ignoreAssertionFailures)
{
Done done;
DrvInfos drvs;
getDerivation(state, v, "", drvs, done);
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
if (drvs.size() != 1) return false;
drv = drvs.front();
return true;
@@ -140,15 +184,16 @@ static string addToPath(const string & s1, const string & s2)
static void getDerivations(EvalState & state, Value & vIn,
const string & pathPrefix, Bindings & autoArgs,
DrvInfos & drvs, Done & done)
DrvInfos & drvs, Done & done,
bool ignoreAssertionFailures)
{
Value v;
state.autoCallFunction(autoArgs, vIn, v);
/* Process the expression. */
DrvInfo drv;
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
else if (v.type == tAttrs) {
@@ -171,8 +216,8 @@ static void getDerivations(EvalState & state, Value & vIn,
string pathPrefix2 = addToPath(pathPrefix, i->first);
Value & v2(*v.attrs->find(i->second)->value);
if (combineChannels)
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
/* If the value of this attribute is itself an
attribute set, should we recurse into it? => Only
if it has a `recurseForDerivations = true'
@@ -180,7 +225,7 @@ static void getDerivations(EvalState & state, Value & vIn,
if (v2.type == tAttrs) {
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
if (j != v2.attrs->end() && state.forceBool(*j->value))
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
}
@@ -191,8 +236,8 @@ static void getDerivations(EvalState & state, Value & vIn,
startNest(nest, lvlDebug,
format("evaluating list element"));
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
@@ -201,11 +246,11 @@ static void getDerivations(EvalState & state, Value & vIn,
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs)
Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures)
{
Done done;
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
+20 -6
View File
@@ -25,13 +25,20 @@ typedef std::map<string, MetaValue> MetaInfo;
struct DrvInfo
{
public:
typedef std::map<string, Path> Outputs;
private:
string drvPath;
string outPath;
string outputName;
Outputs outputs;
bool metaInfoRead;
MetaInfo meta;
bool failed; // set if we get an AssertionError
public:
string name;
string attrPath; /* path towards the derivation */
@@ -40,10 +47,12 @@ public:
/* !!! make this private */
Bindings * attrs;
DrvInfo() : metaInfoRead(false), attrs(0) { };
DrvInfo() : metaInfoRead(false), failed(false), attrs(0) { };
string queryDrvPath(EvalState & state) const;
string queryOutPath(EvalState & state) const;
string queryOutputName(EvalState & state) const;
Outputs queryOutputs(EvalState & state);
MetaInfo queryMetaInfo(EvalState & state) const;
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
@@ -51,13 +60,16 @@ public:
{
drvPath = s;
}
void setOutPath(const string & s)
{
outPath = s;
}
void setMetaInfo(const MetaInfo & meta);
void setFailed() { failed = true; };
bool hasFailed() { return failed; };
};
@@ -70,10 +82,12 @@ typedef list<DrvInfo> DrvInfos;
/* If value `v' denotes a derivation, store information about the
derivation in `drv' and return true. Otherwise, return false. */
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
bool ignoreAssertionFailures);
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
Bindings & autoArgs, DrvInfos & drvs);
Bindings & autoArgs, DrvInfos & drvs,
bool ignoreAssertionFailures);
}
+1 -1
View File
@@ -78,7 +78,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
%}
ID [a-zA-Z\_][a-zA-Z0-9\_\']*
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-9]+
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
+9
View File
@@ -27,6 +27,15 @@ struct Pos
Pos() : line(0), column(0) { };
Pos(const string & file, unsigned int line, unsigned int column)
: file(file), line(line), column(column) { };
bool operator < (const Pos & p2) const
{
int d = file.compare(p2.file);
if (d < 0) return true;
if (d > 0) return false;
if (line < p2.line) return true;
if (line > p2.line) return false;
return column < p2.column;
}
};
extern Pos noPos;
+1 -1
View File
@@ -203,7 +203,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
es2->push_back(new ExprString(symbols.create(s2)));
}
return new ExprConcatStrings(es2);
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(es2);
}
+150 -26
View File
@@ -51,6 +51,12 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
% path % ctx);
if (isDerivation(ctx))
try {
/* For performance, prefetch all substitute info. */
PathSet willBuild, willSubstitute, unknown;
unsigned long long downloadSize, narSize;
queryMissing(*store, singleton<PathSet>(ctx),
willBuild, willSubstitute, unknown, downloadSize, narSize);
/* !!! If using a substitute, we only need to fetch
the selected output of this derivation. */
store->buildPaths(singleton<PathSet>(ctx));
@@ -59,7 +65,31 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
}
}
state.evalFile(path, v);
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
Derivation drv = parseDerivation(readFile(path));
Value & w = *state.allocValue();
state.mkAttrs(w, 1 + drv.outputs.size());
mkString(*state.allocAttr(w, state.sDrvPath), path, singleton<PathSet>("=" + path));
state.mkList(*state.allocAttr(w, state.symbols.create("outputs")), drv.outputs.size());
unsigned int outputs_index = 0;
Value * outputsVal = w.attrs->find(state.symbols.create("outputs"))->value;
foreach (DerivationOutputs::iterator, i, drv.outputs) {
mkString(*state.allocAttr(w, state.symbols.create(i->first)),
i->second.path, singleton<PathSet>("!" + i->first + "!" + path));
mkString(*(outputsVal->list.elems[outputs_index++] = state.allocValue()),
i->first);
}
w.attrs->sort();
Value fun;
state.mkThunk_(fun,
state.parseExprFromFile(state.findFile("nix/imported-drv-to-derivation.nix")));
state.forceFunction(fun);
mkApp(v, fun, w);
state.forceAttrs(v);
} else {
state.evalFile(path, v);
}
}
@@ -280,16 +310,22 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
throw EvalError("required attribute `name' missing");
string drvName;
Pos & posDrvName(*attr->pos);
try {
try {
drvName = state.forceStringNoCtx(*attr->value);
} catch (Error & e) {
e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName);
throw;
}
/* Check whether null attributes should be ignored. */
bool ignoreNulls = false;
attr = args[0]->attrs->find(state.sIgnoreNulls);
if (attr != args[0]->attrs->end())
ignoreNulls = state.forceBool(*attr->value);
/* Build the derivation expression by processing the attributes. */
Derivation drv;
PathSet context;
string outputHash, outputHashAlgo;
@@ -299,11 +335,17 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
outputs.insert("out");
foreach (Bindings::iterator, i, *args[0]->attrs) {
if (i->name == state.sIgnoreNulls) continue;
string key = i->name;
startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
try {
if (ignoreNulls) {
state.forceValue(*i->value);
if (i->value->type == tNull) continue;
}
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
if (key == "args") {
@@ -328,12 +370,12 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
else if (key == "outputHash") outputHash = s;
else if (key == "outputHashAlgo") outputHashAlgo = s;
else if (key == "outputHashMode") {
if (s == "recursive") outputHashRecursive = true;
if (s == "recursive") outputHashRecursive = true;
else if (s == "flat") outputHashRecursive = false;
else throw EvalError(format("invalid value `%1%' for `outputHashMode' attribute") % s);
}
else if (key == "outputs") {
Strings tmp = tokenizeString(s);
Strings tmp = tokenizeString<Strings>(s);
outputs.clear();
foreach (Strings::iterator, j, tmp) {
if (outputs.find(*j) != outputs.end())
@@ -360,13 +402,13 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
throw;
}
}
/* Everything in the context of the strings in the derivation
attributes should be added as dependencies of the resulting
derivation. */
foreach (PathSet::iterator, i, context) {
Path path = *i;
/* Paths marked with `=' denote that the path of a derivation
is explicitly passed to the builder. Since that allows the
builder to gain access to every path in the dependency
@@ -403,7 +445,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
else
drv.inputSrcs.insert(path);
}
/* Do we have all required attributes? */
if (drv.builder == "")
throw EvalError("required attribute `builder' missing");
@@ -420,14 +462,14 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
/* Handle fixed-output derivations. */
if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error("multiple outputs are not supported in fixed-output derivations");
HashType ht = parseHashType(outputHashAlgo);
if (ht == htUnknown)
throw EvalError(format("unknown hash algorithm `%1%'") % outputHashAlgo);
Hash h = parseHash16or32(ht, outputHash);
outputHash = printHash(h);
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
Path outPath = makeFixedOutputPath(outputHashRecursive, ht, h, drvName);
drv.env["out"] = outPath;
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, outputHash);
@@ -447,7 +489,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
/* Use the masked derivation expression to compute the output
path. */
Hash h = hashDerivationModulo(*store, drv);
foreach (DerivationOutputs::iterator, i, drv.outputs)
if (i->second.path == "") {
Path outPath = makeOutputPath(i->first, h, drvName);
@@ -457,7 +499,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
}
/* Write the resulting term into the Nix store directory. */
Path drvPath = writeDerivation(*store, drv, drvName);
Path drvPath = writeDerivation(*store, drv, drvName, state.repair);
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
% drvName % drvPath);
@@ -593,9 +635,9 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
refs.insert(path);
}
Path storePath = readOnlyMode
Path storePath = settings.readOnlyMode
? computeStorePathForText(name, contents, refs)
: store->addTextToStore(name, contents, refs);
: store->addTextToStore(name, contents, refs, state.repair);
/* Note: we don't need to add `context' to the context of the
result, since `storePath' itself has references to the paths
@@ -657,9 +699,9 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v)
FilterFromExpr filter(state, *args[0]);
Path dstPath = readOnlyMode
Path dstPath = settings.readOnlyMode
? computeStorePathForPath(path, true, htSHA256, filter).first
: store->addToStore(path, true, htSHA256, filter);
: store->addToStore(path, true, htSHA256, filter, state.repair);
mkString(v, dstPath, singleton<PathSet>(dstPath));
}
@@ -689,7 +731,7 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
/* Dynamic version of the `.' operator. */
static void prim_getAttr(EvalState & state, Value * * args, Value & v)
void prim_getAttr(EvalState & state, Value * * args, Value & v)
{
string attr = state.forceStringNoCtx(*args[0]);
state.forceAttrs(*args[1]);
@@ -698,6 +740,7 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v)
if (i == args[1]->attrs->end())
throw EvalError(format("attribute `%1%' missing") % attr);
// !!! add to stack trace?
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
state.forceValue(*i->value);
v = *i->value;
}
@@ -843,19 +886,33 @@ static void prim_isList(EvalState & state, Value * * args, Value & v)
}
static void elemAt(EvalState & state, Value & list, int n, Value & v)
{
state.forceList(list);
if (n < 0 || n >= list.list.length)
throw Error(format("list index %1% is out of bounds") % n);
state.forceValue(*list.list.elems[n]);
v = *list.list.elems[n];
}
/* Return the n-1'th element of a list. */
static void prim_elemAt(EvalState & state, Value * * args, Value & v)
{
elemAt(state, *args[0], state.forceInt(*args[1]), v);
}
/* Return the first element of a list. */
static void prim_head(EvalState & state, Value * * args, Value & v)
{
state.forceList(*args[0]);
if (args[0]->list.length == 0)
throw Error("`head' called on an empty list");
state.forceValue(*args[0]->list.elems[0]);
v = *args[0]->list.elems[0];
elemAt(state, *args[0], 0, v);
}
/* Return a list consisting of everything but the the first element of
a list. */
a list. Warning: this function takes O(n) time, so you probably
don't want to use it! */
static void prim_tail(EvalState & state, Value * * args, Value & v)
{
state.forceList(*args[0]);
@@ -881,6 +938,59 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
}
/* Filter a list using a predicate; that is, return a list containing
every element from the list for which the predicate function
returns true. */
static void prim_filter(EvalState & state, Value * * args, Value & v)
{
state.forceFunction(*args[0]);
state.forceList(*args[1]);
// FIXME: putting this on the stack is risky.
Value * vs[args[1]->list.length];
unsigned int k = 0;
bool same = true;
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
Value res;
state.callFunction(*args[0], *args[1]->list.elems[n], res);
if (state.forceBool(res))
vs[k++] = args[1]->list.elems[n];
else
same = false;
}
if (same)
v = *args[1];
else {
state.mkList(v, k);
for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n];
}
}
/* Return true if a list contains a given element. */
static void prim_elem(EvalState & state, Value * * args, Value & v)
{
bool res = false;
state.forceList(*args[1]);
for (unsigned int n = 0; n < args[1]->list.length; ++n)
if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
res = true;
break;
}
mkBool(v, res);
}
/* Concatenate a list of lists. */
static void prim_concatLists(EvalState & state, Value * * args, Value & v)
{
state.forceList(*args[0]);
state.concatLists(v, args[0]->list.length, args[0]->list.elems);
}
/* Return the length of a list. This is an O(1) time operation. */
static void prim_length(EvalState & state, Value * * args, Value & v)
{
@@ -1049,9 +1159,19 @@ void EvalState::createBaseEnv()
mkInt(v, time(0));
addConstant("__currentTime", v);
mkString(v, thisSystem.c_str());
mkString(v, settings.thisSystem.c_str());
addConstant("__currentSystem", v);
mkString(v, nixVersion.c_str());
addConstant("__nixVersion", v);
/* Language version. This should be increased every time a new
language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ?
primOp' to check. */
mkInt(v, 1);
addConstant("__langVersion", v);
// Miscellaneous
addPrimOp("import", 1, prim_import);
addPrimOp("isNull", 1, prim_isNull);
@@ -1092,11 +1212,15 @@ void EvalState::createBaseEnv()
// Lists
addPrimOp("__isList", 1, prim_isList);
addPrimOp("__elemAt", 2, prim_elemAt);
addPrimOp("__head", 1, prim_head);
addPrimOp("__tail", 1, prim_tail);
addPrimOp("map", 2, prim_map);
addPrimOp("__filter", 2, prim_filter);
addPrimOp("__elem", 2, prim_elem);
addPrimOp("__concatLists", 1, prim_concatLists);
addPrimOp("__length", 1, prim_length);
// Integer arithmetic
addPrimOp("__add", 2, prim_add);
addPrimOp("__sub", 2, prim_sub);
-1
View File
@@ -7,6 +7,5 @@ libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@
pkginclude_HEADERS = shared.hh
AM_CXXFLAGS = \
-DNIX_VERSION=\"$(VERSION)\" \
-I$(srcdir)/.. -I$(srcdir)/../libutil \
-I$(srcdir)/../libstore
+51 -92
View File
@@ -33,20 +33,10 @@ static void sigintHandler(int signo)
}
Path makeRootName(const Path & gcRoot, int & counter)
{
counter++;
if (counter == 1)
return gcRoot;
else
return (format("%1%-%2%") % gcRoot % counter).str();
}
void printGCWarning()
{
static bool haveWarned = false;
warnOnce(haveWarned,
warnOnce(haveWarned,
"you did not specify `--add-root'; "
"the result might be removed by the garbage collector");
}
@@ -57,7 +47,14 @@ void printMissing(StoreAPI & store, const PathSet & paths)
unsigned long long downloadSize, narSize;
PathSet willBuild, willSubstitute, unknown;
queryMissing(store, paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
}
void printMissing(const PathSet & willBuild,
const PathSet & willSubstitute, const PathSet & unknown,
unsigned long long downloadSize, unsigned long long narSize)
{
if (!willBuild.empty()) {
printMsg(lvlInfo, format("these derivations will be built:"));
foreach (PathSet::iterator, i, willBuild)
@@ -74,7 +71,7 @@ void printMissing(StoreAPI & store, const PathSet & paths)
if (!unknown.empty()) {
printMsg(lvlInfo, format("don't know how to build these paths%1%:")
% (readOnlyMode ? " (may be caused by read-only store access)" : ""));
% (settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
foreach (PathSet::iterator, i, unknown)
printMsg(lvlInfo, format(" %1%") % *i);
}
@@ -93,12 +90,22 @@ static void setLogType(string lt)
static bool showTrace = false;
string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
++i;
if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
return *i;
}
/* Initialize and reorder arguments, then call the actual argument
processor. */
static void initAndRun(int argc, char * * argv)
{
setDefaultsFromEnvironment();
settings.processEnvironment();
settings.loadConfFile();
/* Catch SIGINT. */
struct sigaction act;
act.sa_handler = sigintHandler;
@@ -125,8 +132,7 @@ static void initAndRun(int argc, char * * argv)
/* There is no privacy in the Nix system ;-) At least not for
now. In particular, store objects should be readable by
everybody. This prevents nasty surprises when using a shared
store (with the setuid() hack). */
everybody. */
umask(0022);
/* Process the NIX_LOG_TYPE environment variable. */
@@ -137,7 +143,7 @@ static void initAndRun(int argc, char * * argv)
Strings args, remaining;
while (argc--) args.push_back(*argv++);
args.erase(args.begin());
/* 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) {
@@ -156,46 +162,45 @@ static void initAndRun(int argc, char * * argv)
remaining.clear();
/* Process default options. */
int verbosityDelta = 0;
int verbosityDelta = lvlInfo;
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
string arg = *i;
if (arg == "--verbose" || arg == "-v") verbosityDelta++;
else if (arg == "--quiet") verbosityDelta--;
else if (arg == "--log-type") {
++i;
if (i == args.end()) throw UsageError("`--log-type' requires an argument");
setLogType(*i);
string s = getArg(arg, i, args.end());
setLogType(s);
}
else if (arg == "--no-build-output" || arg == "-Q")
buildVerbosity = lvlVomit;
settings.buildVerbosity = lvlVomit;
else if (arg == "--print-build-trace")
printBuildTrace = true;
settings.printBuildTrace = true;
else if (arg == "--help") {
printHelp();
return;
}
else if (arg == "--version") {
std::cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << std::endl;
std::cout << format("%1% (Nix) %2%") % programId % nixVersion << std::endl;
return;
}
else if (arg == "--keep-failed" || arg == "-K")
keepFailed = true;
settings.keepFailed = true;
else if (arg == "--keep-going" || arg == "-k")
keepGoing = true;
settings.keepGoing = true;
else if (arg == "--fallback")
tryFallback = true;
settings.set("build-fallback", "true");
else if (arg == "--max-jobs" || arg == "-j")
maxBuildJobs = getIntArg<unsigned int>(arg, i, args.end());
settings.set("build-max-jobs", getArg(arg, i, args.end()));
else if (arg == "--cores")
buildCores = getIntArg<unsigned int>(arg, i, args.end());
settings.set("build-cores", getArg(arg, i, args.end()));
else if (arg == "--readonly-mode")
readOnlyMode = true;
settings.readOnlyMode = true;
else if (arg == "--max-silent-time")
maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
settings.set("build-max-silent-time", getArg(arg, i, args.end()));
else if (arg == "--timeout")
buildTimeout = getIntArg<unsigned int>(arg, i, args.end());
settings.set("build-timeout", getArg(arg, i, args.end()));
else if (arg == "--no-build-hook")
useBuildHook = false;
settings.useBuildHook = false;
else if (arg == "--show-trace")
showTrace = true;
else if (arg == "--option") {
@@ -203,14 +208,15 @@ static void initAndRun(int argc, char * * argv)
string name = *i;
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
string value = *i;
overrideSetting(name, tokenizeString(value));
settings.set(name, value);
}
else remaining.push_back(arg);
}
verbosityDelta += queryIntSetting("verbosity", lvlInfo);
verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta);
settings.update();
run(remaining);
/* Close the Nix database. */
@@ -218,56 +224,6 @@ static void initAndRun(int argc, char * * argv)
}
bool setuidMode = false;
static void setuidInit()
{
/* Don't do anything if this is not a setuid binary. */
if (getuid() == geteuid() && getgid() == getegid()) return;
uid_t nixUid = geteuid();
gid_t nixGid = getegid();
setuidCleanup();
/* Don't trust the current directory. */
if (chdir("/") == -1) abort();
/* Set the real (and preferably also the save) uid/gid to the
effective uid/gid. This matters mostly when we're not using
build-users (bad!), since some builders (like Perl) complain
when real != effective.
On systems where setresuid is unavailable, we can't drop the
saved uid/gid. This means that we could go back to the
original real uid (i.e., the uid of the caller). That's not
really a problem, except maybe when we execute a builder and
we're not using build-users. In that case, the builder may be
able to switch to the uid of the caller and possibly do bad
stuff. But note that when not using build-users, the builder
could also modify the Nix executables (say, replace them by a
Trojan horse), so the problem is already there. */
#if HAVE_SETRESUID
if (setresuid(nixUid, nixUid, nixUid)) abort();
if (setresgid(nixGid, nixGid, nixGid)) abort();
#elif HAVE_SETREUID
/* Note: doesn't set saved uid/gid! */
fprintf(stderr, "warning: cannot set saved uid\n");
if (setreuid(nixUid, nixUid)) abort();
if (setregid(nixGid, nixGid)) abort();
#else
/* Note: doesn't set real and saved uid/gid! */
fprintf(stderr, "warning: cannot set real and saved uids\n");
if (setuid(nixUid)) abort();
if (setgid(nixGid)) abort();
#endif
setuidMode = true;
}
/* Called when the Boehm GC runs out of memory. */
static void * oomHandler(size_t requested)
{
@@ -276,6 +232,14 @@ static void * oomHandler(size_t requested)
}
void showManPage(const string & name)
{
string cmd = "man " + name;
if (system(cmd.c_str()) != 0)
throw Error(format("command `%1%' failed") % cmd);
}
int exitCode = 0;
char * * argvSaved = 0;
@@ -290,11 +254,6 @@ int main(int argc, char * * argv)
argvSaved = argv;
/* If we're setuid, then we need to take some security precautions
right away. */
if (argc == 0) abort();
setuidInit();
/* Turn on buffering for cerr. */
#if HAVE_PUBSETBUF
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
@@ -323,7 +282,7 @@ int main(int argc, char * * argv)
throw;
}
} catch (UsageError & e) {
printMsg(lvlError,
printMsg(lvlError,
format(
"error: %1%\n"
"Try `%2% --help' for more information.")
+6 -3
View File
@@ -26,11 +26,14 @@ MakeError(UsageError, nix::Error);
class StoreAPI;
/* Ugh. No better place to put this. */
Path makeRootName(const Path & gcRoot, int & counter);
void printGCWarning();
void printMissing(StoreAPI & store, const PathSet & paths);
void printMissing(const PathSet & willBuild,
const PathSet & willSubstitute, const PathSet & unknown,
unsigned long long downloadSize, unsigned long long narSize);
template<class N> N getIntArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
@@ -42,8 +45,8 @@ template<class N> N getIntArg(const string & opt,
return n;
}
/* Whether we're running setuid. */
extern bool setuidMode;
/* Show the manual page for the specified program. */
void showManPage(const string & name);
extern volatile ::sig_atomic_t blockInt;
+2 -1
View File
@@ -23,10 +23,11 @@ AM_CXXFLAGS = -Wall \
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
-DNIX_BIN_DIR=\"$(bindir)\" \
-DNIX_VERSION=\"$(VERSION)\" \
-I$(srcdir)/.. -I$(srcdir)/../libutil \
-I$(srcdir)/../libstore
local-store.lo: schema.sql.hh
%.sql.hh: %.sql
../bin2c/bin2c schema < $< > $@ || (rm $@ && exit 1)
sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $< > $@ || (rm $@ && exit 1)
+656 -368
View File
File diff suppressed because it is too large Load Diff
+36 -13
View File
@@ -12,7 +12,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
{
recursive = false;
string algo = hashAlgo;
if (string(algo, 0, 2) == "r:") {
recursive = true;
algo = string(algo, 2);
@@ -21,13 +21,13 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
hashType = parseHashType(algo);
if (hashType == htUnknown)
throw Error(format("unknown hash algorithm `%1%'") % algo);
hash = parseHash(hashType, this->hash);
}
Path writeDerivation(StoreAPI & store,
const Derivation & drv, const string & name)
const Derivation & drv, const string & name, bool repair)
{
PathSet references;
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
@@ -38,9 +38,9 @@ Path writeDerivation(StoreAPI & store,
held during a garbage collection). */
string suffix = name + drvExtension;
string contents = unparseDerivation(drv);
return readOnlyMode
return settings.readOnlyMode
? computeStorePathForText(suffix, contents, references)
: store.addTextToStore(suffix, contents, references);
: store.addTextToStore(suffix, contents, references, repair);
}
@@ -51,7 +51,7 @@ static Path parsePath(std::istream & str)
throw Error(format("bad path `%1%' in derivation") % s);
return s;
}
static StringSet parseStrings(std::istream & str, bool arePaths)
{
@@ -60,7 +60,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
res.insert(arePaths ? parsePath(str) : parseString(str));
return res;
}
Derivation parseDerivation(const string & s)
{
@@ -106,7 +106,7 @@ Derivation parseDerivation(const string & s)
expect(str, ")");
drv.env[name] = value;
}
expect(str, ")");
return drv;
}
@@ -165,7 +165,7 @@ string unparseDerivation(const Derivation & drv)
s += "],";
printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
s += ','; printString(s, drv.platform);
s += ','; printString(s, drv.builder);
s += ','; printStrings(s, drv.args.begin(), drv.args.end());
@@ -178,9 +178,9 @@ string unparseDerivation(const Derivation & drv)
s += ','; printString(s, i->second);
s += ')';
}
s += "])";
return s;
}
@@ -190,7 +190,7 @@ bool isDerivation(const string & fileName)
return hasSuffix(fileName, drvExtension);
}
bool isFixedOutputDrv(const Derivation & drv)
{
return drv.outputs.size() == 1 &&
@@ -247,9 +247,32 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
inputs2[printHash(h)] = i->second;
}
drv.inputDrvs = inputs2;
return hashString(htSHA256, unparseDerivation(drv));
}
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s)
{
size_t n = s.find("!");
return n == s.npos
? DrvPathWithOutputs(s, std::set<string>())
: DrvPathWithOutputs(string(s, 0, n), tokenizeString<std::set<string> >(string(s, n + 1), ","));
}
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs)
{
return outputs.empty()
? drvPath
: drvPath + "!" + concatStringsSep(",", outputs);
}
bool wantOutput(const string & output, const std::set<string> & wanted)
{
return wanted.empty() || wanted.find(output) != wanted.end();
}
}
+14 -3
View File
@@ -1,10 +1,10 @@
#pragma once
#include <map>
#include "types.hh"
#include "hash.hh"
#include <map>
namespace nix {
@@ -57,7 +57,7 @@ class StoreAPI;
/* Write a derivation to the Nix store, and return its path. */
Path writeDerivation(StoreAPI & store,
const Derivation & drv, const string & name);
const Derivation & drv, const string & name, bool repair = false);
/* Parse a derivation. */
Derivation parseDerivation(const string & s);
@@ -79,4 +79,15 @@ typedef std::map<Path, Hash> DrvHashes;
extern DrvHashes drvHashes;
/* Split a string specifying a derivation and a set of outputs
(/nix/store/hash-foo!out1,out2,...) into the derivation path and
the outputs. */
typedef std::pair<string, std::set<string> > DrvPathWithOutputs;
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s);
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs);
bool wantOutput(const string & output, const std::set<string> & wanted);
}
+198 -137
View File
@@ -34,10 +34,10 @@ static const int defaultGcLevel = 1000;
int LocalStore::openGCLock(LockType lockType)
{
Path fnGCLock = (format("%1%/%2%")
% nixStateDir % gcLockName).str();
% settings.nixStateDir % gcLockName).str();
debug(format("acquiring global GC lock `%1%'") % fnGCLock);
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
if (fdGCLock == -1)
throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
@@ -51,7 +51,7 @@ int LocalStore::openGCLock(LockType lockType)
/* !!! Restrict read permission on the GC root. Otherwise any
process that can open the file for reading can DoS the
collector. */
return fdGCLock.borrow();
}
@@ -85,7 +85,7 @@ void LocalStore::addIndirectRoot(const Path & path)
{
string hash = printHash32(hashString(htSHA1, path));
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
% nixStateDir % gcRootsDir % hash).str());
% settings.nixStateDir % gcRootsDir % hash).str());
createSymlink(realRoot, path);
}
@@ -113,15 +113,15 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
else {
if (!allowOutsideRootsDir) {
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
throw Error(format(
"path `%1%' is not a valid garbage collector root; "
"it's not in the directory `%2%'")
% gcRoot % rootsDir);
}
createSymlink(gcRoot, storePath);
}
@@ -130,10 +130,10 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
Instead of reading all the roots, it would be more efficient to
check if the root is in a directory in or linked from the
gcroots directory. */
if (queryBoolSetting("gc-check-reachability", false)) {
if (settings.checkRootReachability) {
Roots roots = store.findRoots();
if (roots.find(gcRoot) == roots.end())
printMsg(lvlError,
printMsg(lvlError,
format(
"warning: `%1%' is not in a directory where the garbage collector looks for roots; "
"therefore, `%2%' might be removed by the garbage collector")
@@ -144,7 +144,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
progress. This prevents the set of permanent roots from
increasing while a GC is in progress. */
store.syncWithGC();
return gcRoot;
}
@@ -160,23 +160,23 @@ void LocalStore::addTempRoot(const Path & path)
if (fdTempRoots == -1) {
while (1) {
Path dir = (format("%1%/%2%") % nixStateDir % tempRootsDir).str();
Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
createDirs(dir);
fnTempRoots = (format("%1%/%2%")
% dir % getpid()).str();
AutoCloseFD fdGCLock = openGCLock(ltRead);
if (pathExists(fnTempRoots))
/* It *must* be stale, since there can be no two
processes with the same pid. */
unlink(fnTempRoots.c_str());
fdTempRoots = openLockFile(fnTempRoots, true);
fdTempRoots = openLockFile(fnTempRoots, true);
fdGCLock.close();
debug(format("acquiring read lock on `%1%'") % fnTempRoots);
lockFile(fdTempRoots, ltRead, true);
@@ -186,7 +186,7 @@ void LocalStore::addTempRoot(const Path & path)
if (fstat(fdTempRoots, &st) == -1)
throw SysError(format("statting `%1%'") % fnTempRoots);
if (st.st_size == 0) break;
/* The garbage collector deleted this file before we could
get a lock. (It won't delete the file after we get a
lock.) Try again. */
@@ -218,7 +218,7 @@ void removeTempRoots()
/* Automatically clean up the temporary roots file when we exit. */
struct RemoveTempRoots
struct RemoveTempRoots
{
~RemoveTempRoots()
{
@@ -238,10 +238,10 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
/* Read the `temproots' directory for per-process temporary root
files. */
Strings tempRootFiles = readDirectory(
(format("%1%/%2%") % nixStateDir % tempRootsDir).str());
(format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
foreach (Strings::iterator, i, tempRootFiles) {
Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % *i).str();
debug(format("reading temporary root file `%1%'") % path);
FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
@@ -295,7 +295,7 @@ static void findRoots(StoreAPI & store, const Path & path,
bool recurseSymlinks, bool deleteStale, Roots & roots)
{
try {
struct stat st;
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path);
@@ -315,7 +315,7 @@ static void findRoots(StoreAPI & store, const Path & path,
debug(format("found root `%1%' in `%2%'")
% target % path);
Path storePath = toStorePath(target);
if (store.isValidPath(storePath))
if (store.isValidPath(storePath))
roots[path] = storePath;
else
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
@@ -350,7 +350,7 @@ static void findRoots(StoreAPI & store, const Path & path,
static Roots findRoots(StoreAPI & store, bool deleteStale)
{
Roots roots;
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
findRoots(store, rootsDir, true, deleteStale, roots);
return roots;
}
@@ -365,16 +365,16 @@ Roots LocalStore::findRoots()
static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
{
Path rootFinder = getEnv("NIX_ROOT_FINDER",
nixLibexecDir + "/nix/find-runtime-roots.pl");
settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
if (rootFinder.empty()) return;
debug(format("executing `%1%' to find additional roots") % rootFinder);
string result = runProgram(rootFinder);
Strings paths = tokenizeString(result, "\n");
Strings paths = tokenizeString<Strings>(result, "\n");
foreach (Strings::iterator, i, paths) {
if (isInStore(*i)) {
Path path = toStorePath(*i);
@@ -425,10 +425,9 @@ bool LocalStore::isActiveTempFile(const GCState & state,
void LocalStore::deleteGarbage(GCState & state, const Path & path)
{
printMsg(lvlInfo, format("deleting `%1%'") % path);
unsigned long long bytesFreed, blocksFreed;
deletePathWrapped(path, bytesFreed, blocksFreed);
unsigned long long bytesFreed;
deletePathWrapped(path, bytesFreed);
state.results.bytesFreed += bytesFreed;
state.results.blocksFreed += blocksFreed;
}
@@ -436,6 +435,8 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
{
checkInterrupt();
if (path == linksDir) return true;
struct stat st;
if (lstat(path.c_str(), &st)) {
if (errno == ENOENT) return true;
@@ -447,67 +448,44 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
if (state.roots.find(path) != state.roots.end()) {
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
goto isLive;
}
/* If gc-keep-outputs and gc-keep-derivations are both set, we can
have cycles in the liveness graph, so we need to treat such
strongly connected components as a single unit (paths). That
is, we can delete the elements of paths only if all referrers
of paths are garbage. */
PathSet paths, referrers;
Paths pathsSorted;
if (isValidPath(path)) {
/* Recursively try to delete the referrers of this path. If
any referrer can't be deleted, then this path can't be
deleted either. */
PathSet referrers;
queryReferrers(path, referrers);
foreach (PathSet::iterator, i, referrers)
if (*i != path && !tryToDelete(state, *i)) {
printMsg(lvlDebug, format("cannot delete `%1%' because it has live referrers") % path);
goto isLive;
/* Add derivers and outputs of path to paths. */
PathSet todo;
todo.insert(path);
while (!todo.empty()) {
Path p = *todo.begin();
assertStorePath(p);
todo.erase(p);
if (paths.find(p) != paths.end()) continue;
paths.insert(p);
/* If gc-keep-derivations is set and this is a derivation,
then don't delete the derivation if any of the outputs
are live. */
if (state.gcKeepDerivations && isDerivation(p)) {
PathSet outputs = queryDerivationOutputs(p);
foreach (PathSet::iterator, i, outputs)
if (isValidPath(*i)) todo.insert(*i);
}
/* If gc-keep-derivations is set and this is a derivation,
then don't delete the derivation if any of the outputs are
live. */
if (state.gcKeepDerivations && isDerivation(path)) {
PathSet outputs = queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (!tryToDelete(state, *i)) {
printMsg(lvlDebug, format("cannot delete derivation `%1%' because its output `%2%' is alive") % path % *i);
goto isLive;
}
}
/* If gc-keep-derivations and gc-keep-outputs are both set,
it's possible that the path has already been deleted (due
to the recursion below), so bail out. */
if (!pathExists(path)) return true;
/* If gc-keep-outputs is set, then don't delete this path if
there are derivers of this path that are not garbage. */
if (state.gcKeepOutputs) {
PathSet derivers = queryValidDerivers(path);
foreach (PathSet::iterator, deriver, derivers) {
/* Break an infinite recursion if gc-keep-derivations
and gc-keep-outputs are both set by tentatively
assuming that this path is garbage. This is a safe
assumption because at this point, the only thing
that can prevent it from being garbage is the
deriver. Since tryToDelete() works "upwards"
through the dependency graph, it won't encouter
this path except in the call to tryToDelete() in
the gc-keep-derivation branch. */
state.deleted.insert(path);
if (!tryToDelete(state, *deriver)) {
state.deleted.erase(path);
printMsg(lvlDebug, format("cannot delete `%1%' because its deriver `%2%' is alive") % path % *deriver);
goto isLive;
}
/* If gc-keep-outputs is set, then don't delete this path
if there are derivers of this path that are not
garbage. */
if (state.gcKeepOutputs) {
PathSet derivers = queryValidDerivers(p);
foreach (PathSet::iterator, i, derivers) todo.insert(*i);
}
}
}
else {
/* A lock file belonging to a path that we're building right
now isn't garbage. */
if (isActiveTempFile(state, path, ".lock")) return false;
@@ -516,66 +494,140 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
currently being built. */
if (isActiveTempFile(state, path, ".chroot")) return false;
paths.insert(path);
}
/* The path is garbage, so delete it. */
if (shouldDelete(state.options.action)) {
/* If it's a valid path that's not a regular file or symlink,
invalidate it, rename it, and schedule it for deletion.
The renaming is to ensure that later (when we're not
holding the global GC lock) we can delete the path without
being afraid that the path has become alive again.
Otherwise delete it right away. */
if (isValidPath(path)) {
if (S_ISDIR(st.st_mode)) {
printMsg(lvlInfo, format("invalidating `%1%'") % path);
// Estimate the amount freed using the narSize field.
state.bytesInvalidated += queryPathInfo(path).narSize;
invalidatePathChecked(path);
makeMutable(path.c_str());
// Mac OS X cannot rename directories if they are read-only.
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % path);
Path tmp = (format("%1%-gc-%2%") % path % getpid()).str();
if (rename(path.c_str(), tmp.c_str()))
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
state.invalidated.insert(tmp);
} else {
invalidatePathChecked(path);
deleteGarbage(state, path);
}
} else
deleteGarbage(state, path);
if (state.options.maxFreed && state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
throw GCLimitReached();
/* Check if any path in paths is a root. */
foreach (PathSet::iterator, i, paths)
if (state.roots.find(*i) != state.roots.end()) {
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % *i);
goto isLive;
}
} else
printMsg(lvlTalkative, format("would delete `%1%'") % path);
state.deleted.insert(path);
if (state.options.action != GCOptions::gcReturnLive)
state.results.paths.insert(path);
/* Recursively try to delete the referrers of this strongly
connected component. If any referrer can't be deleted, then
these paths can't be deleted either. */
foreach (PathSet::iterator, i, paths)
if (isValidPath(*i)) queryReferrers(*i, referrers);
foreach (PathSet::iterator, i, referrers)
if (paths.find(*i) == paths.end() && !tryToDelete(state, *i)) {
printMsg(lvlDebug, format("cannot delete `%1%' because it has live referrers") % *i);
goto isLive;
}
/* The paths are garbage, so delete them. */
pathsSorted = topoSortPaths(*this, paths);
foreach (Paths::iterator, i, pathsSorted) {
if (shouldDelete(state.options.action)) {
/* If it's a valid path that's not a regular file or
symlink, invalidate it, rename it, and schedule it for
deletion. The renaming is to ensure that later (when
we're not holding the global GC lock) we can delete the
path without being afraid that the path has become
alive again. Otherwise delete it right away. */
if (isValidPath(*i)) {
if (S_ISDIR(st.st_mode)) {
printMsg(lvlInfo, format("invalidating `%1%'") % *i);
// Estimate the amount freed using the narSize field.
state.bytesInvalidated += queryPathInfo(*i).narSize;
invalidatePathChecked(*i);
makeMutable(i->c_str());
// Mac OS X cannot rename directories if they are read-only.
if (chmod(i->c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making `%1%' writable") % *i);
Path tmp = (format("%1%-gc-%2%") % *i % getpid()).str();
if (rename(i->c_str(), tmp.c_str()))
throw SysError(format("unable to rename `%1%' to `%2%'") % *i % tmp);
state.invalidated.insert(tmp);
} else {
invalidatePathChecked(*i);
deleteGarbage(state, *i);
}
} else
deleteGarbage(state, *i);
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
throw GCLimitReached();
}
} else
printMsg(lvlTalkative, format("would delete `%1%'") % *i);
state.deleted.insert(*i);
if (state.options.action != GCOptions::gcReturnLive)
state.results.paths.insert(*i);
}
return true;
isLive:
state.live.insert(path);
if (state.options.action == GCOptions::gcReturnLive)
state.results.paths.insert(path);
foreach (PathSet::iterator, i, paths) {
state.live.insert(*i);
if (state.options.action == GCOptions::gcReturnLive)
state.results.paths.insert(*i);
}
return false;
}
/* Unlink all files in /nix/store/.links that have a link count of 1,
which indicates that there are no other links and so they can be
safely deleted. FIXME: race condition with optimisePath(): we
might see a link count of 1 just before optimisePath() increases
the link count. */
void LocalStore::removeUnusedLinks(const GCState & state)
{
AutoCloseDir dir = opendir(linksDir.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
long long actualSize = 0, unsharedSize = 0;
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir)) {
checkInterrupt();
string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = linksDir + "/" + name;
struct stat st;
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path);
if (st.st_nlink != 1) {
unsigned long long size = st.st_blocks * 512ULL;
actualSize += size;
unsharedSize += (st.st_nlink - 1) * size;
continue;
}
printMsg(lvlTalkative, format("deleting unused link `%1%'") % path);
if (unlink(path.c_str()) == -1)
throw SysError(format("deleting `%1%'") % path);
state.results.bytesFreed += st.st_blocks * 512;
}
struct stat st;
if (stat(linksDir.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % linksDir);
long long overhead = st.st_blocks * 512ULL;
printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB")
% ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0)));
}
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
{
GCState state(results);
state.options = options;
state.gcKeepOutputs = queryBoolSetting("gc-keep-outputs", false);
state.gcKeepDerivations = queryBoolSetting("gc-keep-derivations", true);
state.options = options;
state.gcKeepOutputs = settings.gcKeepOutputs;
state.gcKeepDerivations = settings.gcKeepDerivations;
/* Using `--ignore-liveness' with `--delete' can have unintended
consequences if `gc-keep-outputs' or `gc-keep-derivations' are
@@ -585,7 +637,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
state.gcKeepOutputs = false;
state.gcKeepDerivations = false;
}
/* Acquire the global GC root. This prevents
a) New roots from being added.
b) Processes from creating new temporary root files. */
@@ -626,18 +678,18 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (!tryToDelete(state, *i))
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
}
} else {
} else if (options.maxFreed > 0) {
if (shouldDelete(state.options.action))
printMsg(lvlError, format("deleting garbage..."));
else
printMsg(lvlError, format("determining live/dead paths..."));
try {
AutoCloseDir dir = opendir(nixStore.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % nixStore);
AutoCloseDir dir = opendir(settings.nixStore.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
/* Read the store and immediately delete all paths that
aren't valid. When using --max-freed etc., deleting
@@ -651,14 +703,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
checkInterrupt();
string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = nixStore + "/" + name;
Path path = settings.nixStore + "/" + name;
if (isValidPath(path))
entries.push_back(path);
else
tryToDelete(state, path);
}
dir.close();
dir.close();
/* Now delete the unreachable valid paths. Randomise the
order in which we delete entries to make the collector
@@ -682,6 +734,15 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
released. */
foreach (PathSet::iterator, i, state.invalidated)
deleteGarbage(state, *i);
/* Clean up the links directory. */
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
printMsg(lvlError, format("deleting unused links..."));
removeUnusedLinks(state);
}
/* While we're at it, vacuum the database. */
if (options.action == GCOptions::gcDeleteDead) vacuumDB();
}
+129 -98
View File
@@ -10,47 +10,67 @@
namespace nix {
string nixStore = "/UNINIT";
string nixDataDir = "/UNINIT";
string nixLogDir = "/UNINIT";
string nixStateDir = "/UNINIT";
string nixDBPath = "/UNINIT";
string nixConfDir = "/UNINIT";
string nixLibexecDir = "/UNINIT";
string nixBinDir = "/UNINIT";
bool keepFailed = false;
bool keepGoing = false;
bool tryFallback = false;
Verbosity buildVerbosity = lvlError;
unsigned int maxBuildJobs = 1;
unsigned int buildCores = 1;
bool readOnlyMode = false;
string thisSystem = "unset";
time_t maxSilentTime = 0;
time_t buildTimeout = 0;
Paths substituters;
bool useBuildHook = true;
bool printBuildTrace = false;
Settings settings;
static bool settingsRead = false;
static std::map<string, Strings> settings;
/* Overriden settings. */
std::map<string, Strings> settingsCmdline;
string & at(Strings & ss, unsigned int n)
Settings::Settings()
{
Strings::iterator i = ss.begin();
advance(i, n);
return *i;
keepFailed = false;
keepGoing = false;
tryFallback = false;
buildVerbosity = lvlError;
maxBuildJobs = 1;
buildCores = 1;
readOnlyMode = false;
thisSystem = SYSTEM;
maxSilentTime = 0;
buildTimeout = 0;
useBuildHook = true;
printBuildTrace = false;
reservedSize = 1024 * 1024;
fsyncMetadata = true;
useSQLiteWAL = true;
syncBeforeRegistering = false;
useSubstitutes = true;
useChroot = false;
dirsInChroot.insert("/dev");
dirsInChroot.insert("/dev/pts");
impersonateLinux26 = false;
keepLog = true;
compressLog = true;
cacheFailure = false;
pollInterval = 5;
checkRootReachability = false;
gcKeepOutputs = false;
gcKeepDerivations = true;
autoOptimiseStore = false;
envKeepDerivations = false;
}
static void readSettings()
void Settings::processEnvironment()
{
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
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));
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
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));
string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") {
if (getEnv("NIX_OTHER_STORES") != "")
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
} else
substituters = tokenizeString<Strings>(subs, ":");
}
void Settings::loadConfFile()
{
Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str();
if (!pathExists(settingsFile)) return;
@@ -68,105 +88,116 @@ static void readSettings()
if (hash != string::npos)
line = string(line, 0, hash);
Strings tokens = tokenizeString(line);
vector<string> tokens = tokenizeString<vector<string> >(line);
if (tokens.empty()) continue;
if (tokens.size() < 2 || at(tokens, 1) != "=")
if (tokens.size() < 2 || tokens[1] != "=")
throw Error(format("illegal configuration line `%1%' in `%2%'") % line % settingsFile);
string name = at(tokens, 0);
string name = tokens[0];
Strings::iterator i = tokens.begin();
vector<string>::iterator i = tokens.begin();
advance(i, 2);
settings[name] = Strings(i, tokens.end());
settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
};
settings.insert(settingsCmdline.begin(), settingsCmdline.end());
settingsRead = true;
}
Strings querySetting(const string & name, const Strings & def)
void Settings::set(const string & name, const string & value)
{
if (!settingsRead) readSettings();
std::map<string, Strings>::iterator i = settings.find(name);
return i == settings.end() ? def : i->second;
settings[name] = value;
overrides[name] = value;
}
string querySetting(const string & name, const string & def)
void Settings::update()
{
Strings defs;
defs.push_back(def);
Strings value = querySetting(name, defs);
if (value.size() != 1)
throw Error(format("configuration option `%1%' should not be a list") % name);
return value.front();
get(tryFallback, "build-fallback");
get(maxBuildJobs, "build-max-jobs");
get(buildCores, "build-cores");
get(thisSystem, "system");
get(maxSilentTime, "build-max-silent-time");
get(buildTimeout, "build-timeout");
get(reservedSize, "gc-reserved-space");
get(fsyncMetadata, "fsync-metadata");
get(useSQLiteWAL, "use-sqlite-wal");
get(syncBeforeRegistering, "sync-before-registering");
get(useSubstitutes, "build-use-substitutes");
get(buildUsersGroup, "build-users-group");
get(useChroot, "build-use-chroot");
get(dirsInChroot, "build-chroot-dirs");
get(impersonateLinux26, "build-impersonate-linux-26");
get(keepLog, "build-keep-log");
get(compressLog, "build-compress-log");
get(cacheFailure, "build-cache-failure");
get(pollInterval, "build-poll-interval");
get(checkRootReachability, "gc-check-reachability");
get(gcKeepOutputs, "gc-keep-outputs");
get(gcKeepDerivations, "gc-keep-derivations");
get(autoOptimiseStore, "auto-optimise-store");
get(envKeepDerivations, "env-keep-derivations");
}
bool queryBoolSetting(const string & name, bool def)
void Settings::get(string & res, const string & name)
{
string v = querySetting(name, def ? "true" : "false");
if (v == "true") return true;
else if (v == "false") return false;
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
res = i->second;
}
void Settings::get(bool & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
if (i->second == "true") res = true;
else if (i->second == "false") res = false;
else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
% name % v);
% name % i->second);
}
unsigned int queryIntSetting(const string & name, unsigned int def)
void Settings::get(PathSet & res, const string & name)
{
int n;
if (!string2Int(querySetting(name, int2String(def)), n) || n < 0)
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
res.clear();
Strings ss = tokenizeString<Strings>(i->second);
res.insert(ss.begin(), ss.end());
}
template<class N> void Settings::get(N & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
if (!string2Int(i->second, res))
throw Error(format("configuration setting `%1%' should have an integer value") % name);
return n;
}
void overrideSetting(const string & name, const Strings & value)
string Settings::pack()
{
if (settingsRead) settings[name] = value;
settingsCmdline[name] = value;
string s;
foreach (SettingsMap::iterator, i, settings) {
if (i->first.find('\n') != string::npos ||
i->first.find('=') != string::npos ||
i->second.find('\n') != string::npos)
throw Error("illegal option name/value");
s += i->first; s += '='; s += i->second; s += '\n';
}
return s;
}
void reloadSettings()
Settings::SettingsMap Settings::getOverrides()
{
settingsRead = false;
settings.clear();
return overrides;
}
void setDefaultsFromEnvironment()
{
/* Setup Nix paths. */
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
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));
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
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));
const string nixVersion = NIX_VERSION;
string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") {
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
} else
substituters = tokenizeString(subs, ":");
/* Get some settings from the configuration file. */
thisSystem = querySetting("system", SYSTEM);
maxBuildJobs = queryIntSetting("build-max-jobs", 1);
buildCores = queryIntSetting("build-cores", 1);
maxSilentTime = queryIntSetting("build-max-silent-time", 0);
buildTimeout = queryIntSetting("build-timeout", 0);
}
}
+173 -96
View File
@@ -2,118 +2,195 @@
#include "types.hh"
#include <map>
#include <sys/types.h>
namespace nix {
/* Path names. */
struct Settings {
/* nixStore is the directory where we generally store atomic and
derived files. */
extern string nixStore;
typedef std::map<string, string> SettingsMap;
extern string nixDataDir; /* !!! fix */
Settings();
/* nixLogDir is the directory where we log various operations. */
extern string nixLogDir;
void processEnvironment();
/* nixStateDir is the directory where state is stored. */
extern string nixStateDir;
void loadConfFile();
/* nixDBPath is the path name of our Berkeley DB environment. */
extern string nixDBPath;
void set(const string & name, const string & value);
/* nixConfDir is the directory where configuration files are
stored. */
extern string nixConfDir;
void update();
/* nixLibexecDir is the directory where internal helper programs are
stored. */
extern string nixLibexecDir;
string pack();
/* nixBinDir is the directory where the main programs are stored. */
extern string nixBinDir;
SettingsMap getOverrides();
/* The directory where we store sources and derived files. */
Path nixStore;
Path nixDataDir; /* !!! fix */
/* The directory where we log various operations. */
Path nixLogDir;
/* The directory where state is stored. */
Path nixStateDir;
/* The directory where we keep the SQLite database. */
Path nixDBPath;
/* The directory where configuration files are stored. */
Path nixConfDir;
/* The directory where internal helper programs are stored. */
Path nixLibexecDir;
/* The directory where the main programs are stored. */
Path nixBinDir;
/* Whether to keep temporary directories of failed builds. */
bool keepFailed;
/* Whether to keep building subgoals when a sibling (another
subgoal of the same goal) fails. */
bool keepGoing;
/* Whether, if we cannot realise the known closure corresponding
to a derivation, we should try to normalise the derivation
instead. */
bool tryFallback;
/* Verbosity level for build output. */
Verbosity buildVerbosity;
/* Maximum number of parallel build jobs. 0 means unlimited. */
unsigned int maxBuildJobs;
/* Number of CPU cores to utilize in parallel within a build,
i.e. by passing this number to Make via '-j'. 0 means that the
number of actual CPU cores on the local host ought to be
auto-detected. */
unsigned int buildCores;
/* Read-only mode. Don't copy stuff to the store, don't change
the database. */
bool readOnlyMode;
/* The canonical system name, as returned by config.guess. */
string thisSystem;
/* The maximum time in seconds that a builer can go without
producing any output on stdout/stderr before it is killed. 0
means infinity. */
time_t maxSilentTime;
/* The maximum duration in seconds that a builder can run. 0
means infinity. */
time_t buildTimeout;
/* The substituters. There are programs that can somehow realise
a store path without building, e.g., by downloading it or
copying it from a CD. */
Paths substituters;
/* Whether to use build hooks (for distributed builds). Sometimes
users want to disable this from the command-line. */
bool useBuildHook;
/* Whether buildDerivations() should print out lines on stderr in
a fixed format to allow its progress to be monitored. Each
line starts with a "@". The following are defined:
@ build-started <drvpath> <outpath> <system> <logfile>
@ build-failed <drvpath> <outpath> <exitcode> <error text>
@ build-succeeded <drvpath> <outpath>
@ substituter-started <outpath> <substituter>
@ substituter-failed <outpath> <exitcode> <error text>
@ substituter-succeeded <outpath>
Best combined with --no-build-output, otherwise stderr might
conceivably contain lines in this format printed by the
builders. */
bool printBuildTrace;
/* Amount of reserved space for the garbage collector
(/nix/var/nix/db/reserved). */
off_t reservedSize;
/* Whether SQLite should use fsync. */
bool fsyncMetadata;
/* Whether SQLite should use WAL mode. */
bool useSQLiteWAL;
/* Whether to call sync() before registering a path as valid. */
bool syncBeforeRegistering;
/* Whether to use substitutes. */
bool useSubstitutes;
/* The Unix group that contains the build users. */
string buildUsersGroup;
/* Whether to build in chroot. */
bool useChroot;
/* The directories from the host filesystem to be included in the
chroot. */
PathSet dirsInChroot;
/* Whether to impersonate a Linux 2.6 machine on newer kernels. */
bool impersonateLinux26;
/* Whether to store build logs. */
bool keepLog;
/* Whether to compress logs. */
bool compressLog;
/* Whether to cache build failures. */
bool cacheFailure;
/* How often (in seconds) to poll for locks. */
unsigned int pollInterval;
/* Whether to check if new GC roots can in fact be found by the
garbage collector. */
bool checkRootReachability;
/* Whether the garbage collector should keep outputs of live
derivations. */
bool gcKeepOutputs;
/* Whether the garbage collector should keep derivers of live
paths. */
bool gcKeepDerivations;
/* Whether to automatically replace files with identical contents
with hard links. */
bool autoOptimiseStore;
/* Whether to add derivations as a dependency of user environments
(to prevent them from being GCed). */
bool envKeepDerivations;
private:
SettingsMap settings, overrides;
void get(string & res, const string & name);
void get(bool & res, const string & name);
void get(PathSet & res, const string & name);
template<class N> void get(N & res, const string & name);
};
/* Misc. global flags. */
/* Whether to keep temporary directories of failed builds. */
extern bool keepFailed;
/* Whether to keep building subgoals when a sibling (another subgoal
of the same goal) fails. */
extern bool keepGoing;
/* Whether, if we cannot realise the known closure corresponding to a
derivation, we should try to normalise the derivation instead. */
extern bool tryFallback;
/* Verbosity level for build output. */
extern Verbosity buildVerbosity;
/* Maximum number of parallel build jobs. 0 means unlimited. */
extern unsigned int maxBuildJobs;
/* Number of CPU cores to utilize in parallel within a build, i.e. by passing
this number to Make via '-j'. 0 means that the number of actual CPU cores on
the local host ought to be auto-detected. */
extern unsigned int buildCores;
/* Read-only mode. Don't copy stuff to the store, don't change the
database. */
extern bool readOnlyMode;
/* The canonical system name, as returned by config.guess. */
extern string thisSystem;
/* The maximum time in seconds that a builer can go without producing
any output on stdout/stderr before it is killed. 0 means
infinity. */
extern time_t maxSilentTime;
/* The maximum duration in seconds that a builder can run. 0 means
infinity. */
extern time_t buildTimeout;
/* The substituters. There are programs that can somehow realise a
store path without building, e.g., by downloading it or copying it
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;
/* Whether buildDerivations() should print out lines on stderr in a
fixed format to allow its progress to be monitored. Each line
starts with a "@". The following are defined:
@ build-started <drvpath> <outpath> <system> <logfile>
@ build-failed <drvpath> <outpath> <exitcode> <error text>
@ build-succeeded <drvpath> <outpath>
@ substituter-started <outpath> <substituter>
@ substituter-failed <outpath> <exitcode> <error text>
@ substituter-succeeded <outpath>
Best combined with --no-build-output, otherwise stderr might
conceivably contain lines in this format printed by the builders.
*/
extern bool printBuildTrace;
// FIXME: don't use a global variable.
extern Settings settings;
Strings querySetting(const string & name, const Strings & def);
string querySetting(const string & name, const string & def);
bool queryBoolSetting(const string & name, bool def);
unsigned int queryIntSetting(const string & name, unsigned int def);
void overrideSetting(const string & name, const Strings & value);
void reloadSettings();
void setDefaultsFromEnvironment();
extern const string nixVersion;
}
File diff suppressed because it is too large Load Diff
+71 -47
View File
@@ -45,7 +45,7 @@ struct OptimiseStats
struct RunningSubstituter
{
Pid pid;
AutoCloseFD to, from;
AutoCloseFD to, from, error;
};
@@ -75,17 +75,16 @@ struct SQLiteStmt
void bind64(long long value);
void bind();
};
class LocalStore : public StoreAPI
{
private:
bool substitutablePathsLoaded;
PathSet substitutablePaths;
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
RunningSubstituters runningSubstituters;
Path linksDir;
public:
/* Initialise the local store, upgrading the schema if
@@ -93,13 +92,15 @@ public:
LocalStore(bool reserveSpace = true);
~LocalStore();
/* Implementations of abstract store API methods. */
bool isValidPath(const Path & path);
PathSet queryValidPaths();
PathSet queryValidPaths(const PathSet & paths);
PathSet queryAllValidPaths();
ValidPathInfo queryPathInfo(const Path & path);
Hash queryPathHash(const Path & path);
@@ -119,46 +120,44 @@ public:
PathSet queryDerivationOutputs(const Path & path);
StringSet queryDerivationOutputNames(const Path & path);
Path queryPathFromHashPart(const string & hashPart);
PathSet querySubstitutablePaths();
bool hasSubstitutes(const Path & path);
bool querySubstitutablePathInfo(const Path & path,
SubstitutablePathInfo & info);
bool querySubstitutablePathInfo(const Path & substituter,
const Path & path, SubstitutablePathInfo & info);
Path queryPathFromHashPart(const string & hashPart);
PathSet querySubstitutablePaths(const PathSet & paths);
void querySubstitutablePathInfos(const Path & substituter,
PathSet & paths, SubstitutablePathInfos & infos);
void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos);
Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter);
PathFilter & filter = defaultPathFilter, bool repair = false);
/* Like addToStore(), but the contents of the path are contained
in `dump', which is either a NAR serialisation (if recursive ==
true) or simply the contents of a regular file (if recursive ==
false). */
Path addToStoreFromDump(const string & dump, const string & name,
bool recursive = true, HashType hashAlgo = htSHA256);
bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
Path addTextToStore(const string & name, const string & s,
const PathSet & references);
const PathSet & references, bool repair = false);
void exportPath(const Path & path, bool sign,
Sink & sink);
Paths importPaths(bool requireSignature, Source & source);
void buildPaths(const PathSet & paths);
void buildPaths(const PathSet & paths, bool repair = false);
void ensurePath(const Path & path);
void addTempRoot(const Path & path);
void addIndirectRoot(const Path & path);
void syncWithGC();
Roots findRoots();
@@ -167,10 +166,14 @@ public:
/* Optimise the disk space usage of the Nix store by hard-linking
files with the same contents. */
void optimiseStore(bool dryRun, OptimiseStats & stats);
void optimiseStore(OptimiseStats & stats);
/* Check the integrity of the Nix store. */
void verifyStore(bool checkContents);
/* Optimise a single store path. */
void optimisePath(const Path & path);
/* Check the integrity of the Nix store. Returns true if errors
remain. */
bool verifyStore(bool checkContents, bool repair);
/* Register the validity of a path, i.e., that `path' exists, that
the paths referenced by it exists, and in the case of an output
@@ -193,6 +196,20 @@ public:
void clearFailedPaths(const PathSet & paths);
void vacuumDB();
/* Repair the contents of the given path by redownloading it using
a substituter (if available). */
void repairPath(const Path & path);
/* Check whether the given valid path exists and has the right
contents. */
bool pathContentsGood(const Path & path);
void markContentsGood(const Path & path);
void setSubstituterEnv();
private:
Path schemaPath;
@@ -220,27 +237,34 @@ private:
SQLiteStmt stmtQueryDerivationOutputs;
SQLiteStmt stmtQueryPathFromHashPart;
/* Cache for pathContentsGood(). */
std::map<Path, bool> pathContentsGoodCache;
bool didSetSubstituterEnv;
int getSchema();
void openDB(bool create);
void makeStoreWritable();
unsigned long long queryValidPathId(const Path & path);
unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
void addReference(unsigned long long referrer, unsigned long long reference);
void appendReferrer(const Path & from, const Path & to, bool lock);
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
void invalidatePath(const Path & path);
/* Delete a path from the Nix store. */
void invalidatePathChecked(const Path & path);
void verifyPath(const Path & path, const PathSet & store,
PathSet & done, PathSet & validPaths);
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
void updatePathInfo(const ValidPathInfo & info);
@@ -251,22 +275,26 @@ private:
struct GCState;
void deleteGarbage(GCState & state, const Path & path);
bool tryToDelete(GCState & state, const Path & path);
bool isActiveTempFile(const GCState & state,
const Path & path, const string & suffix);
int openGCLock(LockType lockType);
void removeUnusedLinks(const GCState & state);
void startSubstituter(const Path & substituter,
RunningSubstituter & runningSubstituter);
Path createTempDirInStore();
Path importPath(bool requireSignature, Source & source);
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
void optimisePath_(OptimiseStats & stats, const Path & path);
};
@@ -284,9 +312,6 @@ void canonicalisePathMetaData(const Path & path, bool recurse);
MakeError(PathInUse, Error);
/* Whether we are in build users mode. */
bool haveBuildUsers();
/* Whether we are root. */
bool amPrivileged();
@@ -295,9 +320,8 @@ void getOwnership(const Path & path);
/* Like deletePath(), but changes the ownership of `path' using the
setuid wrapper if necessary (and possible). */
void deletePathWrapped(const Path & path,
unsigned long long & bytesFreed, unsigned long long & blocksFreed);
void deletePathWrapped(const Path & path, unsigned long long & bytesFreed);
void deletePathWrapped(const Path & path);
}
+98 -37
View File
@@ -52,68 +52,129 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
unsigned long long & downloadSize, unsigned long long & narSize)
{
downloadSize = narSize = 0;
PathSet todo(targets.begin(), targets.end()), done;
/* Getting substitute info has high latency when using the binary
cache substituter. Thus it's essential to do substitute
queries in parallel as much as possible. To accomplish this
we do the following:
- For all paths still to be processed (todo), we add all
paths for which we need info to the set query. For an
unbuilt derivation this is the output paths; otherwise, it's
the path itself.
- We get info about all paths in query in parallel.
- We process the results and add new items to todo if
necessary. E.g. if a path is substitutable, then we need to
get info on its references.
- Repeat until todo is empty.
*/
while (!todo.empty()) {
Path p = *(todo.begin());
todo.erase(p);
if (done.find(p) != done.end()) continue;
done.insert(p);
if (isDerivation(p)) {
if (!store.isValidPath(p)) {
unknown.insert(p);
continue;
PathSet query, todoDrv, todoNonDrv;
foreach (PathSet::iterator, i, todo) {
if (done.find(*i) != done.end()) continue;
done.insert(*i);
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
if (isDerivation(i2.first)) {
if (!store.isValidPath(i2.first)) {
// FIXME: we could try to substitute p.
unknown.insert(*i);
continue;
}
Derivation drv = derivationFromPath(store, i2.first);
PathSet invalid;
foreach (DerivationOutputs::iterator, j, drv.outputs)
if (wantOutput(j->first, i2.second)
&& !store.isValidPath(j->second.path))
invalid.insert(j->second.path);
if (invalid.empty()) continue;
todoDrv.insert(*i);
if (settings.useSubstitutes) query.insert(invalid.begin(), invalid.end());
}
Derivation drv = derivationFromPath(store, p);
bool mustBuild = false;
foreach (DerivationOutputs::iterator, i, drv.outputs)
if (!store.isValidPath(i->second.path) &&
!(queryBoolSetting("build-use-substitutes", true) && store.hasSubstitutes(i->second.path)))
mustBuild = true;
if (mustBuild) {
willBuild.insert(p);
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
foreach (DerivationInputs::iterator, i, drv.inputDrvs)
todo.insert(i->first);
} else
foreach (DerivationOutputs::iterator, i, drv.outputs)
todo.insert(i->second.path);
else {
if (store.isValidPath(*i)) continue;
query.insert(*i);
todoNonDrv.insert(*i);
}
}
else {
if (store.isValidPath(p)) continue;
SubstitutablePathInfo info;
if (store.querySubstitutablePathInfo(p, info)) {
willSubstitute.insert(p);
downloadSize += info.downloadSize;
narSize += info.narSize;
todo.insert(info.references.begin(), info.references.end());
todo.clear();
SubstitutablePathInfos infos;
store.querySubstitutablePathInfos(query, infos);
foreach (PathSet::iterator, i, todoDrv) {
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
// FIXME: cache this
Derivation drv = derivationFromPath(store, i2.first);
PathSet outputs;
bool mustBuild = false;
if (settings.useSubstitutes) {
foreach (DerivationOutputs::iterator, j, drv.outputs) {
if (!wantOutput(j->first, i2.second)) continue;
if (!store.isValidPath(j->second.path)) {
if (infos.find(j->second.path) == infos.end())
mustBuild = true;
else
outputs.insert(j->second.path);
}
}
} else
unknown.insert(p);
mustBuild = true;
if (mustBuild) {
willBuild.insert(i2.first);
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
foreach (DerivationInputs::iterator, j, drv.inputDrvs)
todo.insert(makeDrvPathWithOutputs(j->first, j->second));
} else
todoNonDrv.insert(outputs.begin(), outputs.end());
}
foreach (PathSet::iterator, i, todoNonDrv) {
done.insert(*i);
SubstitutablePathInfos::iterator info = infos.find(*i);
if (info != infos.end()) {
willSubstitute.insert(*i);
downloadSize += info->second.downloadSize;
narSize += info->second.narSize;
todo.insert(info->second.references.begin(), info->second.references.end());
} else
unknown.insert(*i);
}
}
}
static void dfsVisit(StoreAPI & store, const PathSet & paths,
const Path & path, PathSet & visited, Paths & sorted,
PathSet & parents)
{
if (parents.find(path) != parents.end())
throw BuildError(format("cycle detected in the references of `%1%'") % path);
if (visited.find(path) != visited.end()) return;
visited.insert(path);
parents.insert(path);
PathSet references;
if (store.isValidPath(path))
store.queryReferences(path, references);
foreach (PathSet::iterator, i, references)
/* Don't traverse into paths that don't exist. That can
happen due to substitutes for non-existent paths. */
+118 -121
View File
@@ -1,6 +1,9 @@
#include "config.h"
#include "util.hh"
#include "local-store.hh"
#include "immutable.hh"
#include "globals.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -12,14 +15,11 @@
namespace nix {
typedef std::map<Hash, std::pair<Path, ino_t> > HashToPath;
static void makeWritable(const Path & path)
{
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);
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("changing writability of `%1%'") % path);
@@ -33,8 +33,7 @@ struct MakeReadOnly
~MakeReadOnly()
{
try {
/* This will make the path read-only (and restore the
immutable bit on platforms that support it). */
/* This will make the path read-only. */
if (path != "") canonicalisePathMetaData(path, false);
} catch (...) {
ignoreException();
@@ -43,150 +42,148 @@ struct MakeReadOnly
};
struct MakeImmutable
{
Path path;
MakeImmutable(const Path & path) : path(path) { }
~MakeImmutable() { makeImmutable(path); }
};
static void hashAndLink(bool dryRun, HashToPath & hashToPath,
OptimiseStats & stats, const Path & path)
void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
{
checkInterrupt();
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);
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
foreach (Strings::iterator, i, names)
optimisePath_(stats, path + "/" + *i);
return;
}
/* We can hard link regular files and maybe symlinks. */
if (!S_ISREG(st.st_mode)
#if CAN_LINK_SYMLINK
&& !S_ISLNK(st.st_mode)
#endif
) return;
/* Sometimes SNAFUs can cause files in the Nix store to be
modified, in particular when running programs as root under
NixOS (example: $fontconfig/var/cache being modified). Skip
those files. */
those files. FIXME: check the modification time. */
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path);
return;
}
/* We can hard link regular files and symlinks. */
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
/* Hash the file. Note that hashPath() returns the hash over the
NAR serialisation, which includes the execute bit on the file.
Thus, executable and non-executable files with the same
contents *won't* be linked (which is good because otherwise the
permissions would be screwed up).
/* Hash the file. Note that hashPath() returns the hash over
the NAR serialisation, which includes the execute bit on
the file. Thus, executable and non-executable files with
the same contents *won't* be linked (which is good because
otherwise the permissions would be screwed up).
Also note that if `path' is a symlink, then we're hashing the
contents of the symlink (i.e. the result of readlink()), not
the contents of the target (which may not even exist). */
Hash hash = hashPath(htSHA256, path).first;
stats.totalFiles++;
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
Also note that if `path' is a symlink, then we're hashing
the contents of the symlink (i.e. the result of
readlink()), not the contents of the target (which may not
even exist). */
Hash hash = hashPath(htSHA256, path).first;
stats.totalFiles++;
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
/* Check if this is a known hash. */
Path linkPath = linksDir + "/" + printHash32(hash);
std::pair<Path, ino_t> prevPath = hashToPath[hash];
if (prevPath.first == "") {
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
return;
}
/* Yes! We've seen a file with the same contents. Replace
the current file with a hard link to that file. */
stats.sameContents++;
if (prevPath.second == st.st_ino) {
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % prevPath.first);
return;
}
if (!dryRun) {
printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % prevPath.first);
Path tempLink = (format("%1%.tmp-%2%-%3%")
% path % getpid() % rand()).str();
/* Make the containing directory writable, but only if
it's not the store itself (we don't want or need to
mess with its permissions). */
bool mustToggle = !isStorePath(path);
if (mustToggle) makeWritable(dirOf(path));
/* When we're done, make the directory read-only again and
reset its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
/* If prevPath is immutable, we can't create hard links
to it, so make it mutable first (and make it immutable
again when we're done). We also have to make path
mutable, otherwise rename() will fail to delete it. */
makeMutable(prevPath.first);
MakeImmutable mk1(prevPath.first);
makeMutable(path);
MakeImmutable mk2(path);
if (link(prevPath.first.c_str(), tempLink.c_str()) == -1) {
if (errno == EMLINK) {
/* Too many links to the same file (>= 32000 on
most file systems). This is likely to happen
with empty files. Just start over, creating
links to the current file. */
printMsg(lvlInfo, format("`%1%' has maximum number of links") % prevPath.first);
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
return;
}
throw SysError(format("cannot link `%1%' to `%2%'")
% tempLink % prevPath.first);
}
/* Atomically replace the old file with the new hard link. */
if (rename(tempLink.c_str(), path.c_str()) == -1) {
if (errno == EMLINK) {
/* Some filesystems generate too many links on the
rename, rather than on the original link.
(Probably it temporarily increases the st_nlink
field before decreasing it again.) */
printMsg(lvlInfo, format("`%1%' has maximum number of links") % prevPath.first);
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
/* Unlink the temp link. */
if (unlink(tempLink.c_str()) == -1)
printMsg(lvlError, format("unable to unlink `%1%'") % tempLink);
return;
}
throw SysError(format("cannot rename `%1%' to `%2%'")
% tempLink % path);
}
} else
printMsg(lvlTalkative, format("would link `%1%' to `%2%'") % path % prevPath.first);
stats.filesLinked++;
stats.bytesFreed += st.st_size;
stats.blocksFreed += st.st_blocks;
if (!pathExists(linkPath)) {
/* Nope, create a hard link in the links directory. */
makeMutable(path);
if (link(path.c_str(), linkPath.c_str()) == 0) return;
if (errno != EEXIST)
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
/* Fall through if another process created linkPath before
we did. */
}
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
foreach (Strings::iterator, i, names)
hashAndLink(dryRun, hashToPath, stats, path + "/" + *i);
/* Yes! We've seen a file with the same contents. Replace the
current file with a hard link to that file. */
struct stat stLink;
if (lstat(linkPath.c_str(), &stLink))
throw SysError(format("getting attributes of path `%1%'") % linkPath);
stats.sameContents++;
if (st.st_ino == stLink.st_ino) {
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath);
return;
}
printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath);
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
permissions). */
bool mustToggle = !isStorePath(path);
if (mustToggle) makeWritable(dirOf(path));
/* When we're done, make the directory read-only again and reset
its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
/* If linkPath is immutable, we can't create hard links to it,
so make it mutable first. We also have to make path mutable,
otherwise rename() will fail to delete it. */
makeMutable(path);
makeMutable(linkPath);
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% settings.nixStore % getpid() % rand()).str();
if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
if (errno == EMLINK) {
/* Too many links to the same file (>= 32000 on most file
systems). This is likely to happen with empty files.
Just shrug and ignore. */
if (st.st_size)
printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
return;
}
throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath);
}
/* Atomically replace the old file with the new hard link. */
if (rename(tempLink.c_str(), path.c_str()) == -1) {
if (unlink(tempLink.c_str()) == -1)
printMsg(lvlError, format("unable to unlink `%1%'") % tempLink);
if (errno == EMLINK) {
/* Some filesystems generate too many links on the rename,
rather than on the original link. (Probably it
temporarily increases the st_nlink field before
decreasing it again.) */
if (st.st_size)
printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
return;
}
throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path);
}
stats.filesLinked++;
stats.bytesFreed += st.st_size;
stats.blocksFreed += st.st_blocks;
}
void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats)
void LocalStore::optimiseStore(OptimiseStats & stats)
{
HashToPath hashToPath;
PathSet paths = queryValidPaths();
PathSet paths = queryAllValidPaths();
foreach (PathSet::iterator, i, paths) {
addTempRoot(*i);
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
hashAndLink(dryRun, hashToPath, stats, *i);
optimisePath_(stats, *i);
}
}
void LocalStore::optimisePath(const Path & path)
{
OptimiseStats stats;
if (settings.autoOptimiseStore) optimisePath_(stats, path);
}
}
+122 -104
View File
@@ -50,17 +50,13 @@ void RemoteStore::openConnection(bool reserveSpace)
string remoteMode = getEnv("NIX_REMOTE");
if (remoteMode == "slave")
/* Fork off a setuid worker to do the privileged work. */
forkSlave();
else if (remoteMode == "daemon")
if (remoteMode == "daemon")
/* Connect to a daemon that does the privileged work for
us. */
connectToDaemon();
connectToDaemon();
else
throw Error(format("invalid setting for NIX_REMOTE, `%1%'")
% remoteMode);
throw Error(format("invalid setting for NIX_REMOTE, `%1%'") % remoteMode);
from.fd = fdSocket;
to.fd = fdSocket;
@@ -88,61 +84,14 @@ void RemoteStore::openConnection(bool reserveSpace)
}
void RemoteStore::forkSlave()
{
int sockets[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1)
throw SysError("cannot create sockets");
fdSocket = sockets[0];
AutoCloseFD fdChild = sockets[1];
/* Start the worker. */
Path worker = getEnv("NIX_WORKER");
if (worker == "")
worker = nixBinDir + "/nix-worker";
child = fork();
switch (child) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
if (dup2(fdChild, STDOUT_FILENO) == -1)
throw SysError("dupping write side");
if (dup2(fdChild, STDIN_FILENO) == -1)
throw SysError("dupping read side");
close(fdSocket);
close(fdChild);
execlp(worker.c_str(), worker.c_str(), "--slave", NULL);
throw SysError(format("executing `%1%'") % worker);
} catch (std::exception & e) {
std::cerr << format("child error: %1%\n") % e.what();
}
quickExit(1);
}
fdChild.close();
}
void RemoteStore::connectToDaemon()
{
fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
if (fdSocket == -1)
throw SysError("cannot create Unix domain socket");
closeOnExec(fdSocket);
string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH;
/* Urgh, sockaddr_un allows path names of only 108 characters. So
chdir to the socket directory so that we can pass a relative
@@ -150,16 +99,16 @@ void RemoteStore::connectToDaemon()
applications... */
AutoCloseFD fdPrevDir = open(".", O_RDONLY);
if (fdPrevDir == -1) throw SysError("couldn't open current directory");
chdir(dirOf(socketPath).c_str());
chdir(dirOf(socketPath).c_str());
Path socketPathRel = "./" + baseNameOf(socketPath);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
if (socketPathRel.size() >= sizeof(addr.sun_path))
throw Error(format("socket path `%1%' is too long") % socketPathRel);
using namespace std;
strcpy(addr.sun_path, socketPathRel.c_str());
if (connect(fdSocket, (struct sockaddr *) &addr, sizeof(addr)) == -1)
throw SysError(format("cannot connect to daemon at `%1%'") % socketPath);
@@ -184,24 +133,34 @@ RemoteStore::~RemoteStore()
void RemoteStore::setOptions()
{
writeInt(wopSetOptions, to);
writeInt(keepFailed, to);
writeInt(keepGoing, to);
writeInt(tryFallback, to);
writeInt(settings.keepFailed, to);
writeInt(settings.keepGoing, to);
writeInt(settings.tryFallback, to);
writeInt(verbosity, to);
writeInt(maxBuildJobs, to);
writeInt(maxSilentTime, to);
writeInt(settings.maxBuildJobs, to);
writeInt(settings.maxSilentTime, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
writeInt(useBuildHook, to);
writeInt(settings.useBuildHook, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
writeInt(buildVerbosity, to);
writeInt(settings.buildVerbosity, to);
writeInt(logType, to);
writeInt(printBuildTrace, to);
writeInt(settings.printBuildTrace, to);
}
if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
writeInt(buildCores, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
writeInt(queryBoolSetting("build-use-substitutes", true), to);
writeInt(settings.buildCores, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
writeInt(settings.useSubstitutes, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) {
Settings::SettingsMap overrides = settings.getOverrides();
writeInt(overrides.size(), to);
foreach (Settings::SettingsMap::iterator, i, overrides) {
writeString(i->first, to);
writeString(i->second, to);
}
}
processStderr();
}
@@ -217,42 +176,96 @@ bool RemoteStore::isValidPath(const Path & path)
}
PathSet RemoteStore::queryValidPaths()
PathSet RemoteStore::queryValidPaths(const PathSet & paths)
{
openConnection();
writeInt(wopQueryValidPaths, to);
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
PathSet res;
foreach (PathSet::const_iterator, i, paths)
if (isValidPath(*i)) res.insert(*i);
return res;
} else {
writeInt(wopQueryValidPaths, to);
writeStrings(paths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
}
PathSet RemoteStore::queryAllValidPaths()
{
openConnection();
writeInt(wopQueryAllValidPaths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
bool RemoteStore::hasSubstitutes(const Path & path)
PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths)
{
openConnection();
writeInt(wopHasSubstitutes, to);
writeString(path, to);
processStderr();
unsigned int reply = readInt(from);
return reply != 0;
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
PathSet res;
foreach (PathSet::const_iterator, i, paths) {
writeInt(wopHasSubstitutes, to);
writeString(*i, to);
processStderr();
if (readInt(from)) res.insert(*i);
}
return res;
} else {
writeInt(wopQuerySubstitutablePaths, to);
writeStrings(paths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
}
bool RemoteStore::querySubstitutablePathInfo(const Path & path,
SubstitutablePathInfo & info)
void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos)
{
if (paths.empty()) return;
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return false;
writeInt(wopQuerySubstitutablePathInfo, to);
writeString(path, to);
processStderr();
unsigned int reply = readInt(from);
if (reply == 0) return false;
info.deriver = readString(from);
if (info.deriver != "") assertStorePath(info.deriver);
info.references = readStorePaths<PathSet>(from);
info.downloadSize = readLongLong(from);
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
return true;
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return;
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
foreach (PathSet::const_iterator, i, paths) {
SubstitutablePathInfo info;
writeInt(wopQuerySubstitutablePathInfo, to);
writeString(*i, to);
processStderr();
unsigned int reply = readInt(from);
if (reply == 0) continue;
info.deriver = readString(from);
if (info.deriver != "") assertStorePath(info.deriver);
info.references = readStorePaths<PathSet>(from);
info.downloadSize = readLongLong(from);
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
infos[*i] = info;
}
} else {
writeInt(wopQuerySubstitutablePathInfos, to);
writeStrings(paths, to);
processStderr();
unsigned int count = readInt(from);
for (unsigned int n = 0; n < count; n++) {
Path path = readStorePath(from);
SubstitutablePathInfo & info(infos[path]);
info.deriver = readString(from);
if (info.deriver != "") assertStorePath(info.deriver);
info.references = readStorePaths<PathSet>(from);
info.downloadSize = readLongLong(from);
info.narSize = readLongLong(from);
}
}
}
@@ -354,10 +367,12 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart)
Path RemoteStore::addToStore(const Path & _srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter)
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
openConnection();
Path srcPath(absPath(_srcPath));
writeInt(wopAddToStore, to);
@@ -373,14 +388,16 @@ Path RemoteStore::addToStore(const Path & _srcPath,
Path RemoteStore::addTextToStore(const string & name, const string & s,
const PathSet & references)
const PathSet & references, bool repair)
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
openConnection();
writeInt(wopAddTextToStore, to);
writeString(name, to);
writeString(s, to);
writeStrings(references, to);
processStderr();
return readStorePath(from);
}
@@ -409,8 +426,9 @@ Paths RemoteStore::importPaths(bool requireSignature, Source & source)
}
void RemoteStore::buildPaths(const PathSet & drvPaths)
void RemoteStore::buildPaths(const PathSet & drvPaths, bool repair)
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
openConnection();
writeInt(wopBuildPaths, to);
writeStrings(drvPaths, to);
@@ -477,7 +495,7 @@ Roots RemoteStore::findRoots()
void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
{
openConnection(false);
writeInt(wopCollectGarbage, to);
writeInt(options.action, to);
writeStrings(options.pathsToDelete, to);
@@ -489,12 +507,12 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
writeInt(0, to);
writeInt(0, to);
}
processStderr();
results.paths = readStrings<PathSet>(from);
results.bytesFreed = readLongLong(from);
results.blocksFreed = readLongLong(from);
readLongLong(from); // obsolete
}
@@ -538,7 +556,7 @@ void RemoteStore::processStderr(Sink * sink, Source * source)
}
else {
string s = readString(from);
writeToStderr((const unsigned char *) s.data(), s.size());
writeToStderr(s);
}
}
if (msg == STDERR_ERROR) {
+9 -9
View File
@@ -26,7 +26,9 @@ public:
bool isValidPath(const Path & path);
PathSet queryValidPaths();
PathSet queryValidPaths(const PathSet & paths);
PathSet queryAllValidPaths();
ValidPathInfo queryPathInfo(const Path & path);
@@ -44,24 +46,24 @@ public:
Path queryPathFromHashPart(const string & hashPart);
bool hasSubstitutes(const Path & path);
PathSet querySubstitutablePaths(const PathSet & paths);
bool querySubstitutablePathInfo(const Path & path,
SubstitutablePathInfo & info);
void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos);
Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter);
PathFilter & filter = defaultPathFilter, bool repair = false);
Path addTextToStore(const string & name, const string & s,
const PathSet & references);
const PathSet & references, bool repair = false);
void exportPath(const Path & path, bool sign,
Sink & sink);
Paths importPaths(bool requireSignature, Source & source);
void buildPaths(const PathSet & paths);
void buildPaths(const PathSet & paths, bool repair = false);
void ensurePath(const Path & path);
@@ -91,8 +93,6 @@ private:
void processStderr(Sink * sink = 0, Source * source = 0);
void forkSlave();
void connectToDaemon();
void setOptions();
+10 -10
View File
@@ -2,7 +2,7 @@
#include "globals.hh"
#include "util.hh"
#include <limits.h>
#include <climits>
namespace nix {
@@ -12,23 +12,23 @@ GCOptions::GCOptions()
{
action = gcDeleteDead;
ignoreLiveness = false;
maxFreed = 0;
maxFreed = ULLONG_MAX;
}
bool isInStore(const Path & path)
{
return path[0] == '/'
&& string(path, 0, nixStore.size()) == nixStore
&& path.size() >= nixStore.size() + 2
&& path[nixStore.size()] == '/';
&& string(path, 0, settings.nixStore.size()) == settings.nixStore
&& path.size() >= settings.nixStore.size() + 2
&& path[settings.nixStore.size()] == '/';
}
bool isStorePath(const Path & path)
{
return isInStore(path)
&& path.find('/', nixStore.size() + 1) == Path::npos;
&& path.find('/', settings.nixStore.size() + 1) == Path::npos;
}
@@ -43,7 +43,7 @@ Path toStorePath(const Path & path)
{
if (!isInStore(path))
throw Error(format("path `%1%' is not in the Nix store") % path);
Path::size_type slash = path.find('/', nixStore.size() + 1);
Path::size_type slash = path.find('/', settings.nixStore.size() + 1);
if (slash == Path::npos)
return path;
else
@@ -74,7 +74,7 @@ Path followLinksToStorePath(const Path & path)
string storePathToName(const Path & path)
{
assertStorePath(path);
return string(path, nixStore.size() + 34);
return string(path, settings.nixStore.size() + 34);
}
@@ -173,11 +173,11 @@ Path makeStorePath(const string & type,
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":sha256:" + printHash(hash) + ":"
+ nixStore + ":" + name;
+ settings.nixStore + ":" + name;
checkStoreName(name);
return nixStore + "/"
return settings.nixStore + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + name;
}
+22 -21
View File
@@ -48,8 +48,7 @@ struct GCOptions
/* For `gcDeleteSpecific', the paths to delete. */
PathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. 0 means
no limit. */
/* Stop after at least `maxFreed' bytes have been freed. */
unsigned long long maxFreed;
GCOptions();
@@ -66,13 +65,9 @@ struct GCResults
number of bytes that would be or was freed. */
unsigned long long bytesFreed;
/* The number of file system blocks that would be or was freed. */
unsigned long long blocksFreed;
GCResults()
{
bytesFreed = 0;
blocksFreed = 0;
}
};
@@ -85,6 +80,8 @@ struct SubstitutablePathInfo
unsigned long long narSize; /* 0 = unknown */
};
typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos;
struct ValidPathInfo
{
@@ -107,20 +104,23 @@ public:
virtual ~StoreAPI() { }
/* Checks whether a path is valid. */
/* Check whether a path is valid. */
virtual bool isValidPath(const Path & path) = 0;
/* Query the set of valid paths. */
virtual PathSet queryValidPaths() = 0;
/* Query which of the given paths is valid. */
virtual PathSet queryValidPaths(const PathSet & paths) = 0;
/* Query the set of all valid paths. */
virtual PathSet queryAllValidPaths() = 0;
/* Query information about a valid path. */
virtual ValidPathInfo queryPathInfo(const Path & path) = 0;
/* Queries the hash of a valid path. */
/* Query the hash of a valid path. */
virtual Hash queryPathHash(const Path & path) = 0;
/* Queries the set of outgoing FS references for a store path.
The result is not cleared. */
/* Query the set of outgoing FS references for a store path. The
result is not cleared. */
virtual void queryReferences(const Path & path,
PathSet & references) = 0;
@@ -143,13 +143,14 @@ public:
path, or "" if the path doesn't exist. */
virtual Path queryPathFromHashPart(const string & hashPart) = 0;
/* Query whether a path has substitutes. */
virtual bool hasSubstitutes(const Path & path) = 0;
/* Query which of the given paths have substitutes. */
virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0;
/* Query the references, deriver and download size of a
substitutable path. */
virtual bool querySubstitutablePathInfo(const Path & path,
SubstitutablePathInfo & info) = 0;
/* Query substitute info (i.e. references, derivers and download
sizes) of a set of paths. If a path does not have substitute
info, it's omitted from the resulting infos map. */
virtual void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos) = 0;
/* Copy the contents of a path to the store and register the
validity the resulting path. The resulting path is returned.
@@ -157,12 +158,12 @@ public:
libutil/archive.hh). */
virtual Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter) = 0;
PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
/* Like addToStore, but the contents written to the output path is
a regular file containing the given string. */
virtual Path addTextToStore(const string & name, const string & s,
const PathSet & references) = 0;
const PathSet & references, bool repair = false) = 0;
/* Export a store path, that is, create a NAR dump of the store
path and append its references and its deriver. Optionally, a
@@ -183,7 +184,7 @@ public:
output paths can be created by running the builder, after
recursively building any sub-derivations. For inputs that are
not derivations, substitute them. */
virtual void buildPaths(const PathSet & paths) = 0;
virtual void buildPaths(const PathSet & paths, bool repair = false) = 0;
/* Ensure that a path is valid. If it is not currently valid, it
may be made valid by running a substitute (if defined for the
+6 -3
View File
@@ -6,7 +6,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION 0x10b
#define PROTOCOL_VERSION 0x10c
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
@@ -32,13 +32,16 @@ typedef enum {
wopCollectGarbage = 20,
wopQuerySubstitutablePathInfo = 21,
wopQueryDerivationOutputs = 22,
wopQueryValidPaths = 23,
wopQueryAllValidPaths = 23,
wopQueryFailedPaths = 24,
wopClearFailedPaths = 25,
wopQueryPathInfo = 26,
wopImportPaths = 27,
wopQueryDerivationOutputNames = 28,
wopQueryPathFromHashPart = 29,
wopQuerySubstitutablePathInfos = 30,
wopQueryValidPaths = 31,
wopQuerySubstitutablePaths = 32,
} WorkerOp;
@@ -60,5 +63,5 @@ typedef enum {
Path readStorePath(Source & from);
template<class T> T readStorePaths(Source & from);
}
+6
View File
@@ -153,6 +153,12 @@ string printHash32(const Hash & hash)
}
string printHash16or32(const Hash & hash)
{
return hash.type == htMD5 ? printHash(hash) : printHash32(hash);
}
static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
{
unsigned char carry = 0;
+3
View File
@@ -54,6 +54,9 @@ unsigned int hashLength32(const Hash & hash);
/* Convert a hash to a base-32 representation. */
string printHash32(const Hash & hash);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);
/* Parse a base-32 representation of a hash code. */
Hash parseHash32(HashType ht, const string & s);
+2 -20
View File
@@ -16,7 +16,7 @@
namespace nix {
void changeMutable(const Path & path, bool mut)
void makeMutable(const Path & path)
{
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
@@ -38,30 +38,12 @@ void changeMutable(const Path & path, bool mut)
/* Silently ignore errors getting/setting the immutable flag so
that we work correctly on filesystems that don't support it. */
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
old = flags;
if (mut) flags &= ~FS_IMMUTABLE_FL;
else flags |= FS_IMMUTABLE_FL;
flags &= ~FS_IMMUTABLE_FL;
if (old == flags) return;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
#endif
}
void makeImmutable(const Path & path)
{
changeMutable(path, false);
}
void makeMutable(const Path & path)
{
changeMutable(path, true);
}
}
-6
View File
@@ -4,12 +4,6 @@
namespace nix {
/* Make the given path immutable, i.e., prevent it from being modified
in any way, even by root. This is a no-op on platforms that do not
support this, or if the calling user is not privileged. On Linux,
this is implemented by doing the equivalent of chattr +i path. */
void makeImmutable(const Path & path);
/* Make the given path mutable. */
void makeMutable(const Path & path);
+2
View File
@@ -1,5 +1,7 @@
#pragma once
#include "config.h"
#include <string>
#include <list>
#include <set>
+96 -59
View File
@@ -224,12 +224,12 @@ string readFile(int fd)
}
string readFile(const Path & path)
string readFile(const Path & path, bool drain)
{
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1)
throw SysError(format("opening file `%1%'") % path);
return readFile(fd);
return drain ? drainFD(fd) : readFile(fd);
}
@@ -253,7 +253,7 @@ string readLine(int fd)
if (errno != EINTR)
throw SysError("reading a line");
} else if (rd == 0)
throw Error("unexpected EOF reading a line");
throw EndOfFile("unexpected EOF reading a line");
else {
if (ch == '\n') return s;
s += ch;
@@ -297,8 +297,7 @@ void computePathSize(const Path & path,
}
static void _deletePath(const Path & path, unsigned long long & bytesFreed,
unsigned long long & blocksFreed)
static void _deletePath(const Path & path, unsigned long long & bytesFreed)
{
checkInterrupt();
@@ -308,10 +307,8 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed,
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) {
bytesFreed += st.st_size;
blocksFreed += st.st_blocks;
}
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
bytesFreed += st.st_blocks * 512;
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
@@ -323,7 +320,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed,
}
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
_deletePath(path + "/" + *i, bytesFreed, blocksFreed);
_deletePath(path + "/" + *i, bytesFreed);
}
if (remove(path.c_str()) == -1)
@@ -333,19 +330,17 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed,
void deletePath(const Path & path)
{
unsigned long long dummy1, dummy2;
deletePath(path, dummy1, dummy2);
unsigned long long dummy;
deletePath(path, dummy);
}
void deletePath(const Path & path, unsigned long long & bytesFreed,
unsigned long long & blocksFreed)
void deletePath(const Path & path, unsigned long long & bytesFreed)
{
startNest(nest, lvlDebug,
format("recursively deleting path `%1%'") % path);
bytesFreed = 0;
blocksFreed = 0;
_deletePath(path, bytesFreed, blocksFreed);
_deletePath(path, bytesFreed);
}
@@ -380,7 +375,7 @@ static Path tempName(Path tmpRoot, const Path & prefix, bool includePid,
Path createTempDir(const Path & tmpRoot, const Path & prefix,
bool includePid, bool useGlobalCounter)
bool includePid, bool useGlobalCounter, mode_t mode)
{
static int globalCounter = 0;
int localCounter = 0;
@@ -389,7 +384,7 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
while (1) {
checkInterrupt();
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
if (mkdir(tmpDir.c_str(), 0777) == 0) {
if (mkdir(tmpDir.c_str(), mode) == 0) {
/* Explicitly set the group of the directory. This is to
work around around problems caused by BSD's group
ownership semantics (directories inherit the group of
@@ -488,16 +483,7 @@ void printMsg_(Verbosity level, const format & f)
else if (logType == ltEscapes && level != lvlInfo)
prefix = "\033[" + escVerbosity(level) + "s";
string s = (format("%1%%2%\n") % prefix % f.str()).str();
try {
writeToStderr((const unsigned char *) s.data(), s.size());
} catch (SysError & e) {
/* Ignore failing writes to stderr if we're in an exception
handler, otherwise throw an exception. We need to ignore
write errors in exception handlers to ensure that cleanup
code runs to completion if the other side of stderr has
been closed unexpectedly. */
if (!std::uncaught_exception()) throw;
}
writeToStderr(s);
}
@@ -510,13 +496,28 @@ void warnOnce(bool & haveWarned, const format & f)
}
void writeToStderr(const string & s)
{
try {
_writeToStderr((const unsigned char *) s.data(), s.size());
} catch (SysError & e) {
/* Ignore failing writes to stderr if we're in an exception
handler, otherwise throw an exception. We need to ignore
write errors in exception handlers to ensure that cleanup
code runs to completion if the other side of stderr has
been closed unexpectedly. */
if (!std::uncaught_exception()) throw;
}
}
static void defaultWriteToStderr(const unsigned char * buf, size_t count)
{
writeFull(STDERR_FILENO, buf, count);
}
void (*writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr;
void (*_writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr;
void readFull(int fd, unsigned char * buf, size_t count)
@@ -657,7 +658,7 @@ void AutoCloseFD::close()
if (fd != -1) {
if (::close(fd) == -1)
/* This should never happen. */
throw SysError("closing file descriptor");
throw SysError(format("closing file descriptor %1%") % fd);
fd = -1;
}
}
@@ -765,8 +766,8 @@ Pid::operator pid_t()
void Pid::kill()
{
if (pid == -1) return;
if (pid == -1 || pid == 0) return;
printMsg(lvlError, format("killing process %1%") % pid);
/* Send the requested signal to the child. If it has its own
@@ -850,11 +851,10 @@ void killUser(uid_t uid)
}
} catch (std::exception & e) {
std::cerr << format("killing processes belonging to uid `%1%': %2%")
% uid % e.what() << std::endl;
quickExit(1);
writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str());
_exit(1);
}
quickExit(0);
_exit(0);
}
/* parent */
@@ -875,14 +875,21 @@ void killUser(uid_t uid)
string runProgram(Path program, bool searchPath, const Strings & args)
{
checkInterrupt();
std::vector<const char *> cargs; /* careful with c_str()! */
cargs.push_back(program.c_str());
for (Strings::const_iterator i = args.begin(); i != args.end(); ++i)
cargs.push_back(i->c_str());
cargs.push_back(0);
/* Create a pipe. */
Pipe pipe;
pipe.create();
/* Fork. */
Pid pid;
pid = fork();
pid = maybeVfork();
switch (pid) {
case -1:
@@ -890,27 +897,19 @@ string runProgram(Path program, bool searchPath, const Strings & args)
case 0: /* child */
try {
pipe.readSide.close();
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
std::vector<const char *> cargs; /* careful with c_str()! */
cargs.push_back(program.c_str());
for (Strings::const_iterator i = args.begin(); i != args.end(); ++i)
cargs.push_back(i->c_str());
cargs.push_back(0);
if (searchPath)
execvp(program.c_str(), (char * *) &cargs[0]);
else
execv(program.c_str(), (char * *) &cargs[0]);
throw SysError(format("executing `%1%'") % program);
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
writeToStderr("error: " + string(e.what()) + "\n");
}
quickExit(1);
_exit(1);
}
/* Parent. */
@@ -949,12 +948,6 @@ void closeOnExec(int fd)
}
void quickExit(int status)
{
_exit(status);
}
void setuidCleanup()
{
/* Don't trust the environment. */
@@ -968,6 +961,13 @@ void setuidCleanup()
}
#if HAVE_VFORK
pid_t (*maybeVfork)() = vfork;
#else
pid_t (*maybeVfork)() = fork;
#endif
//////////////////////////////////////////////////////////////////////
@@ -989,20 +989,24 @@ void _interrupted()
//////////////////////////////////////////////////////////////////////
Strings tokenizeString(const string & s, const string & separators)
template<class C> C tokenizeString(const string & s, const string & separators)
{
Strings result;
C result;
string::size_type pos = s.find_first_not_of(separators, 0);
while (pos != string::npos) {
string::size_type end = s.find_first_of(separators, pos + 1);
if (end == string::npos) end = s.size();
string token(s, pos, end - pos);
result.push_back(token);
result.insert(result.end(), token);
pos = s.find_first_not_of(separators, end);
}
return result;
}
template Strings tokenizeString(const string & s, const string & separators);
template StringSet tokenizeString(const string & s, const string & separators);
template vector<string> tokenizeString(const string & s, const string & separators);
string concatStringsSep(const string & sep, const Strings & ss)
{
@@ -1015,6 +1019,24 @@ string concatStringsSep(const string & sep, const Strings & ss)
}
string concatStringsSep(const string & sep, const StringSet & ss)
{
string s;
foreach (StringSet::const_iterator, i, ss) {
if (s.size() != 0) s += sep;
s += *i;
}
return s;
}
string chomp(const string & s)
{
size_t i = s.find_last_not_of(" \n\r\t");
return i == string::npos ? "" : string(s, 0, i + 1);
}
string statusToString(int status)
{
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
@@ -1096,6 +1118,21 @@ bool endOfList(std::istream & str)
}
string decodeOctalEscaped(const string & s)
{
string r;
for (string::const_iterator i = s.begin(); i != s.end(); ) {
if (*i != '\\') { r += *i++; continue; }
unsigned char c = 0;
++i;
while (i != s.end() && *i >= '0' && *i < '8')
c = c * 8 + (*i++ - '0');
r += c;
}
return r;
}
void ignoreException()
{
try {
@@ -1105,5 +1142,5 @@ void ignoreException()
}
}
}
+24 -10
View File
@@ -17,6 +17,9 @@ namespace nix {
#define foreach(it_type, it, collection) \
for (it_type it = (collection).begin(); it != (collection).end(); ++it)
#define foreach_reverse(it_type, it, collection) \
for (it_type it = (collection).rbegin(); it != (collection).rend(); ++it)
/* Return an environment variable. */
string getEnv(const string & key, const string & def = "");
@@ -60,7 +63,7 @@ Strings readDirectory(const Path & path);
/* Read the contents of a file into a string. */
string readFile(int fd);
string readFile(const Path & path);
string readFile(const Path & path, bool drain = false);
/* Write a string to a file. */
void writeFile(const Path & path, const string & s);
@@ -80,15 +83,14 @@ void computePathSize(const Path & path,
returns the number of bytes and blocks freed. */
void deletePath(const Path & path);
void deletePath(const Path & path, unsigned long long & bytesFreed,
unsigned long long & blocksFreed);
void deletePath(const Path & path, unsigned long long & bytesFreed);
/* Make a path read-only recursively. */
void makePathReadOnly(const Path & path);
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true);
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
/* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */
@@ -146,7 +148,9 @@ void printMsg_(Verbosity level, const format & f);
void warnOnce(bool & haveWarned, const format & f);
extern void (*writeToStderr) (const unsigned char * buf, size_t count);
void writeToStderr(const string & s);
extern void (*_writeToStderr) (const unsigned char * buf, size_t count);
/* Wrappers arount read()/write() that read/write exactly the
@@ -260,14 +264,13 @@ void closeMostFDs(const set<int> & exceptions);
/* Set the close-on-exec flag for the given file descriptor. */
void closeOnExec(int fd);
/* Wrapper around _exit() on Unix and ExitProcess() on Windows. (On
Cygwin, _exit() doesn't seem to do the right thing.) */
void quickExit(int status);
/* Common initialisation for setuid programs: clear the environment,
sanitize file handles 0, 1 and 2. */
void setuidCleanup();
/* Call vfork() if available, otherwise fork(). */
extern pid_t (*maybeVfork)();
/* User interruption. */
@@ -284,12 +287,17 @@ MakeError(Interrupted, BaseError)
/* String tokenizer. */
Strings tokenizeString(const string & s, const string & separators = " \t\n\r");
template<class C> C tokenizeString(const string & s, const string & separators = " \t\n\r");
/* Concatenate the given strings with a separator between the
elements. */
string concatStringsSep(const string & sep, const Strings & ss);
string concatStringsSep(const string & sep, const StringSet & ss);
/* Remove trailing whitespace from a string. */
string chomp(const string & s);
/* Convert the exit status of a child as returned by wait() into an
@@ -326,6 +334,12 @@ string parseString(std::istream & str);
bool endOfList(std::istream & str);
/* Escape a string that contains octal-encoded escape codes such as
used in /etc/fstab and /proc/mounts (e.g. "foo\040bar" decodes to
"foo bar"). */
string decodeOctalEscaped(const string & s);
/* Exception handling in destructors: print an error message, then
ignore the exception. */
void ignoreException();
+4 -4
View File
@@ -9,7 +9,7 @@ namespace nix {
XMLWriter::XMLWriter(bool indent, std::ostream & output)
: output(output), indent(indent)
{
output << "<?xml version='1.0' encoding='utf-8'?>\n";
output << "<?xml version='1.0' encoding='utf-8'?>" << std::endl;
closed = false;
}
@@ -43,7 +43,7 @@ void XMLWriter::openElement(const string & name,
output << "<" << name;
writeAttrs(attrs);
output << ">";
if (indent) output << "\n";
if (indent) output << std::endl;
pendingElems.push_back(name);
}
@@ -53,7 +53,7 @@ void XMLWriter::closeElement()
assert(!pendingElems.empty());
indent_(pendingElems.size() - 1);
output << "</" << pendingElems.back() << ">";
if (indent) output << "\n";
if (indent) output << std::endl;
pendingElems.pop_back();
if (pendingElems.empty()) closed = true;
}
@@ -67,7 +67,7 @@ void XMLWriter::writeEmptyElement(const string & name,
output << "<" << name;
writeAttrs(attrs);
output << " />";
if (indent) output << "\n";
if (indent) output << std::endl;
}
+12
View File
@@ -0,0 +1,12 @@
bin_PROGRAMS = nix-daemon
nix_daemon_SOURCES = nix-daemon.cc
nix_daemon_LDADD = ../libmain/libmain.la ../libstore/libstore.la ../libutil/libutil.la \
../boost/format/libformat.la
AM_CXXFLAGS = \
-I$(srcdir)/.. -I$(srcdir)/../libutil \
-I$(srcdir)/../libstore -I$(srcdir)/../libmain
install-exec-local:
ln -sf nix-daemon $(DESTDIR)$(bindir)/nix-worker
@@ -6,7 +6,6 @@
#include "archive.hh"
#include "globals.hh"
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>
@@ -25,7 +24,7 @@ using namespace nix;
disconnects and immediately kill any ongoing builds. On platforms
that lack it, we only notice the disconnection the next time we try
to write to the client. So if you have a builder that never
generates output on stdout/stderr, the worker will never notice
generates output on stdout/stderr, the daemon will never notice
that the client has disconnected until the builder terminates. */
#ifdef O_ASYNC
#define HAVE_HUP_NOTIFICATION
@@ -95,7 +94,7 @@ static bool isFarSideClosed(int socket)
throw Error("EOF expected (protocol error?)");
else if (rd == -1 && errno != ECONNRESET)
throw SysError("expected connection reset or EOF");
return true;
}
@@ -185,7 +184,7 @@ static void stopWork(bool success = true, const string & msg = "", unsigned int
we're either sending or receiving from the client, so we'll be
notified of client death anyway. */
setSigPollAction(false);
canSendStderr = false;
if (success)
@@ -220,7 +219,7 @@ struct TunnelSource : BufferedSource
so we have to disable the SIGPOLL handler. */
setSigPollAction(false);
canSendStderr = false;
writeInt(STDERR_READ, to);
writeInt(len, to);
to.flush();
@@ -279,7 +278,7 @@ static void performOp(unsigned int clientVersion,
{
switch (op) {
#if 0
#if 0
case wopQuit: {
/* Close the database. */
store.reset((StoreAPI *) 0);
@@ -297,12 +296,30 @@ static void performOp(unsigned int clientVersion,
break;
}
case wopQueryValidPaths: {
PathSet paths = readStorePaths<PathSet>(from);
startWork();
PathSet res = store->queryValidPaths(paths);
stopWork();
writeStrings(res, to);
break;
}
case wopHasSubstitutes: {
Path path = readStorePath(from);
startWork();
bool result = store->hasSubstitutes(path);
PathSet res = store->querySubstitutablePaths(singleton<PathSet>(path));
stopWork();
writeInt(result, to);
writeInt(res.find(path) != res.end(), to);
break;
}
case wopQuerySubstitutablePaths: {
PathSet paths = readStorePaths<PathSet>(from);
startWork();
PathSet res = store->querySubstitutablePaths(paths);
stopWork();
writeStrings(res, to);
break;
}
@@ -373,7 +390,7 @@ static void performOp(unsigned int clientVersion,
SavingSourceAdapter savedNAR(from);
RetrieveRegularNARSink savedRegular;
if (recursive) {
/* Get the entire NAR dump from the client and save it to
a string so that we can pass it to
@@ -382,13 +399,13 @@ static void performOp(unsigned int clientVersion,
parseDump(sink, savedNAR);
} else
parseDump(savedRegular, from);
startWork();
if (!savedRegular.regular) throw Error("regular file expected");
Path path = dynamic_cast<LocalStore *>(store.get())
->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
stopWork();
writeString(path, to);
break;
}
@@ -494,41 +511,45 @@ static void performOp(unsigned int clientVersion,
}
GCResults results;
startWork();
if (options.ignoreLiveness)
throw Error("you are not allowed to ignore liveness");
store->collectGarbage(options, results);
stopWork();
writeStrings(results.paths, to);
writeLongLong(results.bytesFreed, to);
writeLongLong(results.blocksFreed, to);
writeLongLong(0, to); // obsolete
break;
}
case wopSetOptions: {
keepFailed = readInt(from) != 0;
keepGoing = readInt(from) != 0;
tryFallback = readInt(from) != 0;
settings.keepFailed = readInt(from) != 0;
settings.keepGoing = readInt(from) != 0;
settings.tryFallback = readInt(from) != 0;
verbosity = (Verbosity) readInt(from);
maxBuildJobs = readInt(from);
maxSilentTime = readInt(from);
settings.maxBuildJobs = readInt(from);
settings.maxSilentTime = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
useBuildHook = readInt(from) != 0;
settings.useBuildHook = readInt(from) != 0;
if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
buildVerbosity = (Verbosity) readInt(from);
settings.buildVerbosity = (Verbosity) readInt(from);
logType = (LogType) readInt(from);
printBuildTrace = readInt(from) != 0;
settings.printBuildTrace = readInt(from) != 0;
}
if (GET_PROTOCOL_MINOR(clientVersion) >= 6)
buildCores = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 10) {
int x = readInt(from);
Strings ss;
ss.push_back(x == 0 ? "false" : "true");
overrideSetting("build-use-substitutes", ss);
settings.buildCores = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 10)
settings.useSubstitutes = readInt(from) != 0;
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
unsigned int n = readInt(from);
for (unsigned int i = 0; i < n; i++) {
string name = readString(from);
string value = readString(from);
settings.set("untrusted-" + name, value);
}
}
startWork();
stopWork();
@@ -538,23 +559,43 @@ static void performOp(unsigned int clientVersion,
case wopQuerySubstitutablePathInfo: {
Path path = absPath(readString(from));
startWork();
SubstitutablePathInfo info;
bool res = store->querySubstitutablePathInfo(path, info);
SubstitutablePathInfos infos;
store->querySubstitutablePathInfos(singleton<PathSet>(path), infos);
stopWork();
writeInt(res ? 1 : 0, to);
if (res) {
writeString(info.deriver, to);
writeStrings(info.references, to);
writeLongLong(info.downloadSize, to);
SubstitutablePathInfos::iterator i = infos.find(path);
if (i == infos.end())
writeInt(0, to);
else {
writeInt(1, to);
writeString(i->second.deriver, to);
writeStrings(i->second.references, to);
writeLongLong(i->second.downloadSize, to);
if (GET_PROTOCOL_MINOR(clientVersion) >= 7)
writeLongLong(info.narSize, to);
writeLongLong(i->second.narSize, to);
}
break;
}
case wopQueryValidPaths: {
case wopQuerySubstitutablePathInfos: {
PathSet paths = readStorePaths<PathSet>(from);
startWork();
PathSet paths = store->queryValidPaths();
SubstitutablePathInfos infos;
store->querySubstitutablePathInfos(paths, infos);
stopWork();
writeInt(infos.size(), to);
foreach (SubstitutablePathInfos::iterator, i, infos) {
writeString(i->first, to);
writeString(i->second.deriver, to);
writeStrings(i->second.references, to);
writeLongLong(i->second.downloadSize, to);
writeLongLong(i->second.narSize, to);
}
break;
}
case wopQueryAllValidPaths: {
startWork();
PathSet paths = store->queryAllValidPaths();
stopWork();
writeStrings(paths, to);
break;
@@ -599,8 +640,8 @@ static void performOp(unsigned int clientVersion,
static void processConnection()
{
canSendStderr = false;
myPid = getpid();
writeToStderr = tunnelStderr;
myPid = getpid();
_writeToStderr = tunnelStderr;
#ifdef HAVE_HUP_NOTIFICATION
/* Allow us to receive SIGPOLL for events on the client socket. */
@@ -619,6 +660,10 @@ static void processConnection()
to.flush();
unsigned int clientVersion = readInt(from);
bool reserveSpace = true;
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
reserveSpace = readInt(from) != 0;
/* Send startup error messages to the client. */
startWork();
@@ -631,19 +676,15 @@ static void processConnection()
/* Prevent users from doing something very dangerous. */
if (geteuid() == 0 &&
querySetting("build-users-group", "") == "")
throw Error("if you run `nix-worker' as root, then you MUST set `build-users-group'!");
throw Error("if you run `nix-daemon' as root, then you MUST set `build-users-group'!");
#endif
bool reserveSpace = true;
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
reserveSpace = readInt(from) != 0;
/* Open the store. */
store = boost::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
stopWork();
to.flush();
} catch (Error & e) {
stopWork(false, e.msg());
to.flush();
@@ -652,7 +693,7 @@ static void processConnection()
/* Process client requests. */
unsigned int opCount = 0;
while (true) {
WorkerOp op;
try {
@@ -682,7 +723,7 @@ static void processConnection()
assert(!canSendStderr);
};
printMsg(lvlError, format("%1% worker operations") % opCount);
printMsg(lvlError, format("%1% operations") % opCount);
}
@@ -724,13 +765,13 @@ static void daemonLoop()
/* Otherwise, create and bind to a Unix domain socket. */
else {
/* Create and bind to a Unix domain socket. */
fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
if (fdSocket == -1)
throw SysError("cannot create Unix domain socket");
string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH;
createDirs(dirOf(socketPath));
@@ -739,7 +780,7 @@ static void daemonLoop()
relative path name. */
chdir(dirOf(socketPath).c_str());
Path socketPathRel = "./" + baseNameOf(socketPath);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
if (socketPathRel.size() >= sizeof(addr.sun_path))
@@ -764,7 +805,7 @@ static void daemonLoop()
}
closeOnExec(fdSocket);
/* Loop accepting connections. */
while (1) {
@@ -772,7 +813,7 @@ static void daemonLoop()
/* Important: the server process *cannot* open the SQLite
database, because it doesn't like forks very much. */
assert(!store);
/* Accept a connection. */
struct sockaddr_un remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
@@ -781,14 +822,14 @@ static void daemonLoop()
(struct sockaddr *) &remoteAddr, &remoteAddrLen);
checkInterrupt();
if (remote == -1) {
if (errno == EINTR)
continue;
else
throw SysError("accepting connection");
if (errno == EINTR)
continue;
else
throw SysError("accepting connection");
}
closeOnExec(remote);
/* Get the identity of the caller, if possible. */
uid_t clientUid = -1;
pid_t clientPid = -1;
@@ -803,20 +844,20 @@ static void daemonLoop()
#endif
printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid);
/* Fork a child to handle the connection. */
pid_t child;
child = fork();
switch (child) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
/* Background the worker. */
/* Background the daemon. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
@@ -828,18 +869,14 @@ static void daemonLoop()
string processName = int2String(clientPid);
strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
}
/* Since the daemon can be long-running, the
settings may have changed. So force a reload. */
reloadSettings();
/* Handle the connection. */
from.fd = remote;
to.fd = remote;
processConnection();
} catch (std::exception & e) {
std::cerr << format("child error: %1%\n") % e.what();
writeToStderr("child error: " + string(e.what()) + "\n");
}
exit(0);
}
@@ -855,42 +892,22 @@ static void daemonLoop()
void run(Strings args)
{
bool slave = false;
bool daemon = false;
for (Strings::iterator i = args.begin(); i != args.end(); ) {
string arg = *i++;
if (arg == "--slave") slave = true;
if (arg == "--daemon") daemon = true;
if (arg == "--daemon") /* ignored for backwards compatibility */;
}
if (slave) {
/* This prevents us from receiving signals from the terminal
when we're running in setuid mode. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
processConnection();
}
else if (daemon) {
if (setuidMode)
throw Error("daemon cannot be started in setuid mode");
chdir("/");
daemonLoop();
}
else
throw Error("must be run in either --slave or --daemon mode");
chdir("/");
daemonLoop();
}
#include "help.txt.hh"
void printHelp()
{
std::cout << string((char *) helpText);
showManPage("nix-daemon");
}
string programId = "nix-worker";
string programId = "nix-daemon";

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