Compare commits

...
231 Commits
Author SHA1 Message Date
Eelco Dolstra 71a5161365 Implement buildDerivation() via the daemon 2015-09-03 12:56:59 +02:00
Eelco Dolstra 6e06a18d1b Release notes 2015-09-03 11:57:47 +02:00
Eelco Dolstra 73332fd342 Filter build-chroot-dirs entries that conflict with derivation outputs
Fixes https://github.com/NixOS/nixpkgs/issues/9504.

Note that this means we may have a non-functional /bin/sh in the
chroot while rebuilding Bash or one of its dependencies. Ideally those
packages don't rely on /bin/sh though.
2015-09-02 14:54:12 +02:00
Eelco Dolstra d711fe8f5e Remove unused variable 2015-09-02 14:46:04 +02:00
Eelco Dolstra ce97523a7c Fix readDir example
Fixes https://github.com/NixOS/nixos-homepage/issues/46.
2015-09-01 16:53:51 +02:00
Eelco Dolstra e12cf82782 Prevent .chroot from being GC'ed when using LocalStore::buildDerivation()
Fixes #616.
2015-08-24 11:13:31 +02:00
Eelco Dolstra eadb86f447 nix-collect-garbage: Revive --max-freed
Fixes #609.
2015-08-21 13:57:53 +02:00
Eelco Dolstra 1d29db2a9f Merge pull request #608 from Phant0mas/master
Remove unneeded HAVE_UNSHARE.
2015-08-19 13:35:23 +02:00
Manolis Ragkousis 26221e44eb Remove unneeded HAVE_UNSHARE.
* src/libstore/build.cc (CHROOT_ENABLED): Remove HAVE_UNSHARE.
2015-08-19 14:11:15 +03:00
Eelco Dolstra 984c5cdc50 Drop newline in error message 2015-08-07 05:32:17 +02:00
Kirill ElaginandEelco Dolstra 3b0f60e5c2 baseNameOf: Enhance basename compatibility
* If the path ends with a slash, drop it.
* If the remaining path doesn’t contain slashes, just return it.

Fixes #574.
2015-08-07 03:35:02 +02:00
Eelco Dolstra 896428c818 Fix example 2015-08-05 17:36:33 +02:00
Eelco Dolstra 34dfbd9394 Don't include <iostream> before config.h
This breaks the build on 32-bit systems.

http://hydra.nixos.org/build/24373658
2015-08-04 11:12:31 +02:00
Eelco Dolstra 30d19a2bdc Handle debug messages from runChild()
Turns out that "nix-build -vvv" with chroots enabled has been broken
for some time, because some debug message got interpreted as an error.
2015-08-03 18:04:32 +02:00
Iwan AucampandEelco Dolstra 3db950aab7 Removed unnecessary included 2015-08-03 11:48:34 +02:00
Eelco Dolstra 2bac04c5ff Fix stack consumption 2015-07-31 20:28:25 +02:00
Iwan AucampandEelco Dolstra 75837651f1 Output line number on infinite recursion 2015-07-31 20:26:44 +02:00
Eelco Dolstra 76cc8e97a2 Add sort primop 2015-07-28 18:39:39 +02:00
Eelco Dolstra 50807f3dd5 Add primop genList
This can be used to implement functions like ‘imap’ (or for that
matter, ‘map’) without the quadratic complexity incurred by calling
‘++’ repeatedly.
2015-07-28 17:28:35 +02:00
Eelco Dolstra f3dda728a4 Remove unnecessary parentheses 2015-07-26 12:07:14 +02:00
Eelco Dolstra d6d5885c15 Add replaceStrings primop
This is a generalisation of replaceChars in Nixpkgs.
2015-07-24 15:32:24 +02:00
Eelco Dolstra 2e8fd4c5cd Add concatStringsSep as a primop
This fixes the quadratic behaviour of concatStrings/concatStringsSep
in Nixpkgs.
2015-07-24 02:38:09 +02:00
Eelco Dolstra cb4320c1a0 Cleanup 2015-07-23 23:14:07 +02:00
Eelco Dolstra c8bb2371eb Optimize empty sets
This reduces the number of Bindings allocations by about 10%.
2015-07-23 23:11:08 +02:00
Eelco Dolstra 16c9935fa9 Fix fetchurl of executable file
Pointed out by @cstrahan, thanks!
2015-07-23 22:25:04 +02:00
Eelco Dolstra 19eddecc0f Merge branch 'attr-set-hh' of https://github.com/nbp/nix
Conflicts:
	src/libexpr/eval.cc
2015-07-23 22:16:01 +02:00
Eelco Dolstra b83801f8b3 Optimize small lists
The value pointers of lists with 1 or 2 elements are now stored in the
list value itself. In particular, this makes the "concatMap (x: if
cond then [(f x)] else [])" idiom cheaper.
2015-07-23 22:05:09 +02:00
Eelco Dolstra 14be783676 Add primops all and any
These are used thousands of times during NixOS evaluation, so it's
useful to speed them up.
2015-07-23 19:23:11 +02:00
Shea LevyandEelco Dolstra 39e27a04b8 Importing derivations: Add name attribute to make a valid drv 2015-07-23 17:04:07 +02:00
Shea LevyandEelco Dolstra 1ed55234d9 Allow derivations-as-srcs in the context of builtins.toFile files 2015-07-23 17:03:47 +02:00
Eelco Dolstra 61af14a921 Add foldl' primop 2015-07-23 17:03:02 +02:00
Eelco Dolstra 887bb5fa5a --version: Print some config info
Such as whether Nix is built with signed binary cache support, and the
location of the configuration file.
2015-07-23 14:38:00 +02:00
Eelco Dolstra 1993b10d11 Fix Darwin build
Turns out getgrouplist() is not POSIX.

http://hydra.nixos.org/build/23881243
2015-07-21 14:45:24 +02:00
Eelco Dolstra 0a2bee307b Make <nix/fetchurl.nix> a builtin builder
This ensures that 1) the derivation doesn't change when Nix changes;
2) the derivation closure doesn't contain Nix and its dependencies; 3)
we don't have to rely on ugly chroot hacks.
2015-07-20 04:38:46 +02:00
Eelco Dolstra eda2f36c2a Provide more detailed info about build status to hydra-queue-runner
In particular, hydra-queue-runner can now distinguish between remote
build / substitution / already-valid. For instance, if a path already
existed on the remote side, we don't want to store a log file.
2015-07-20 03:20:03 +02:00
Eelco Dolstra ccf31dbc25 nix-copy-closure: Add -v flag
And make exportPath() less spammy by default.
2015-07-20 01:52:07 +02:00
Eelco Dolstra db55940d9e Support systemd log severity prefixes
This is mostly useful for hydra-queue-runner.
2015-07-20 01:39:48 +02:00
Eelco Dolstra b3491c781c More cleanup 2015-07-20 01:16:16 +02:00
Eelco Dolstra 6bd2c7bb38 OCD: foreach -> C++11 ranged for 2015-07-17 20:13:56 +02:00
Eelco Dolstra 1511aa9f48 Allow remote builds without sending the derivation closure
Previously, to build a derivation remotely, we had to copy the entire
closure of the .drv file to the remote machine, even though we only
need the top-level derivation. This is very wasteful: the closure can
contain thousands of store paths, and in some Hydra use cases, include
source paths that are very large (e.g. Git/Mercurial checkouts).

So now there is a new operation, StoreAPI::buildDerivation(), that
performs a build from an in-memory representation of a derivation
(BasicDerivation) rather than from a on-disk .drv file. The only files
that need to be in the Nix store are the sources of the derivation
(drv.inputSrcs), and the needed output paths of the dependencies (as
described by drv.inputDrvs). "nix-store --serve" exposes this
interface.

Note that this is a privileged operation, because you can construct a
derivation that builds any store path whatsoever. Fixing this will
require changing the hashing scheme (i.e., the output paths should be
computed from the other fields in BasicDerivation, allowing them to be
verified without access to other derivations). However, this would be
quite nice because it would allow .drv-free building (e.g. "nix-env
-i" wouldn't have to write any .drv files to disk).

Fixes #173.
2015-07-17 17:57:40 +02:00
Eelco Dolstra f39979c6d3 Make printValue() interruptible
Fixes #572.
2015-07-17 11:33:39 +02:00
Eelco Dolstra 7c9d0a5969 nix-collect-garbage: Handle ENOENT
Don't barf trying to read a link that just got deleted.

Fixes #575.
2015-07-17 11:24:25 +02:00
Jaka HudoklinandEelco Dolstra 5845ffdf13 Add Dockerfile 2015-07-17 11:06:50 +02:00
Nicolas B. Pierron db21cfa688 Move attribute set data structures into their own header file.
This modification moves Attr and Bindings structures into their own header
file which is dedicated to the attribute set representation. The goal of to
isolate pieces of code which are related to the attribute set
representation. Thus future modifications of the attribute set
representation will only have to modify these files, and not every other
file across the evaluator.
2015-07-14 19:23:17 +02:00
Guillaume Maudoux 467977f203 Fix the parsing of "$"'s in strings. 2015-07-03 14:09:58 +02:00
Guillaume Maudoux 65e4dcd69b Fix the hack that resets the scanner state. 2015-07-03 13:53:36 +02:00
Eelco Dolstra dd48c06bb6 Typo 2015-07-02 00:30:16 +02:00
Ludovic CourtèsandEelco Dolstra 9aed117395 Preserve supplementary groups of build users
The following patch is an attempt to address this bug (see
<http://bugs.gnu.org/18994>) by preserving the supplementary groups of
build users in the build environment.

In practice, I would expect that supplementary groups would contain only
one or two groups: the build users group, and possibly the “kvm” group.

[Changed &at(0) to data() and removed tabs - Eelco]
2015-07-01 14:57:48 +02:00
Eelco Dolstra e012c126db Revert "add the manpath to the installer"
This reverts commit 76f985b92d. We
shouldn't mess with $MANPATH, because on some "man" implementations
(like NixOS'), the default value on $MANPATH is derived from $PATH. So
if you set $MANPATH, you lose the default locations.
2015-07-01 13:04:15 +02:00
Eelco Dolstra ff4de4cb27 GC: Handle ENOSPC creating/moving to the trash directory
Issue #564.
2015-06-30 21:41:26 +02:00
Eelco Dolstra 2bc9c84327 Use posix_fallocate to create /nix/var/nix/db/reserved 2015-06-22 15:54:55 +02:00
Eelco Dolstra ba63ec6f39 Make /nix/var/nix/db/reserved bigger
Issue #564.
2015-06-22 15:47:40 +02:00
Eelco Dolstra 65f17cd330 Support URLs in $NIX_PATH
This didn't work (despite claims in the manual), because the colon in
"http://" was parsed as a element separator. So handle "://"
specially.
2015-06-17 16:20:11 +02:00
Eelco Dolstra 0d4d92fcf9 Debian package: Declare runtime dependency on libsodium13
Fixes #558.
2015-06-17 10:33:51 +02:00
Eelco Dolstra a33b4bc01b Bump 2015-06-15 10:22:35 +02:00
Eelco Dolstra 39b3d9698e Typos 2015-06-12 11:02:40 +02:00
Eelco Dolstra c48617671d nix-channel: Fix bogus error message caused by 8a84bd8c8b 2015-06-12 01:56:34 +02:00
Eelco Dolstra d1e4b76a3a Update release notes 2015-06-12 01:11:03 +02:00
Eelco Dolstra b65875f859 Export outputPaths function
This is useful for the new hydra-queue-runner.
2015-06-10 16:17:06 +02:00
Eelco Dolstra f90e9b65d6 Install serve-protocol.hh 2015-06-09 11:14:36 +02:00
Eelco Dolstra 7c4501886d Use std::vector::data() 2015-06-09 10:54:46 +02:00
Eelco Dolstra f2b67fbf2a nix-push: Support -j
Fixes #548.
2015-06-08 14:16:06 +02:00
Eelco Dolstra bf8cc4e9b6 Update cacert locations 2015-06-08 11:40:35 +02:00
Eelco Dolstra 7d148ad543 Fix manual 2015-06-08 11:25:04 +02:00
Eelco Dolstra b64988bb35 Allow substitutes for builds that have preferLocalBuild set
Not substituting builds with "preferLocalBuild = true" was a bad idea,
because it didn't take the cost of dependencies into account. For
instance, if we can't substitute a fetchgit call, then we have to
download/build git and all its dependencies.

Partially reverts 5558652709 and adds a
new derivation attribute "allowSubstitutes" to specify whether a
derivation may be substituted.
2015-06-04 16:30:22 +02:00
Eelco Dolstra b190f771e7 copy-from-other-stores: Use cp 2015-06-04 14:55:40 +02:00
Eelco Dolstra 07d7e7df84 Chown files created for passAsFile
Nixpkgs' writeTextAsFile does this:

  mv "$textPath" "$n"

Since $textPath was owned by root, if $textPath is on the same
filesystem as $n, $n will be owned as root. As a result, the build
result was rejected as having suspicious ownership.

http://hydra.nixos.org/build/22836807
2015-06-04 14:07:43 +02:00
Eelco Dolstra 94378910fb Handle base-16 NarHash fields in signed .narinfo files 2015-06-03 15:33:17 +02:00
Eelco Dolstra a64da5915d Be more robust wrt broken .narinfo files 2015-06-03 15:19:32 +02:00
Eelco Dolstra f0f30f594c Naming 2015-06-03 15:19:26 +02:00
Eelco Dolstra 90aec21d76 Fix tarball test
The tarball cache is stored in $HOME, so the test should set up its
own $HOME.
2015-06-02 13:20:43 +02:00
Eelco Dolstra 898703e006 Build against libsodium on Ubuntu 15.04 and Debian 8 2015-06-02 13:14:31 +02:00
Eelco Dolstra d8ddf994e7 Don't let unprivileged users repair paths 2015-06-02 02:21:54 +02:00
Eelco Dolstra 7106bb0611 Use StoreAPI::verifyStore() 2015-06-02 02:21:15 +02:00
Ludovic CourtèsandEelco Dolstra b755752f76 Add a ‘verifyStore’ RPC
Hello!

The patch below adds a ‘verifyStore’ RPC with the same signature as the
current LocalStore::verifyStore method.

Thanks,
Ludo’.

>From aef46c03ca77eb6344f4892672eb6d9d06432041 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= <ludo@gnu.org>
Date: Mon, 1 Jun 2015 23:17:10 +0200
Subject: [PATCH] Add a 'verifyStore' remote procedure call.
2015-06-02 02:14:24 +02:00
Eelco Dolstra 53dd97bb9d Document setting up signed binary caches 2015-06-01 17:14:16 +02:00
Eelco Dolstra b8b571cfc1 Include the cache.nixos.org signing key by default 2015-06-01 17:14:03 +02:00
Eelco Dolstra bc51175dc0 Add tarball tests 2015-06-01 16:18:23 +02:00
Eelco Dolstra da196ec68f Document tarball downloading 2015-06-01 15:14:44 +02:00
Eelco Dolstra c1323b53e3 Fix fetchurl/fetchTarball 2015-06-01 15:08:09 +02:00
Eelco Dolstra 1c88e100e7 readFile: Check against nul bytes 2015-06-01 15:07:42 +02:00
Eelco Dolstra feca5cb67f Document nix-shell #!-scripts 2015-06-01 13:49:11 +02:00
Eelco Dolstra a80f11bf7b nix-shell: Support multiple "#! nix-shell" lines 2015-06-01 13:48:45 +02:00
Eelco Dolstra 99c2c14f50 Typo 2015-06-01 13:04:10 +02:00
Eelco Dolstra d68941d89d Nix 1.9 release notes 2015-06-01 12:57:43 +02:00
Benjamin StaffinandEelco Dolstra 07c69aa03b Add Debian 8.0 builds
Change-Id: I68a54a0c3f97da2d062f43b638de817fd40f2dcd
2015-05-29 11:54:37 +02:00
Eelco Dolstra b2798902ea Build on Ubuntu 15.04 2015-05-22 13:32:03 +02:00
Eelco Dolstra 920f5fd4dd Fix import-from-derivation in restricted eval mode
This relaxes restricted mode to allow access to anything in the
store. In the future, it would be better to allow access to only paths
that have been constructed in the current evaluation (so a hard-coded
/nix/store/blabla in a Nix expression would still be
rejected). However, note that reading /nix/store itself is still
rejected, so you can't use this so get access to things you don't know
about.
2015-05-22 12:18:23 +02:00
Eelco Dolstra 7a411e01cf Remove dead code 2015-05-22 11:29:22 +02:00
Eelco Dolstra e0a068cb97 nix-shell: Barf if -p and -E are both used
Closes #454, #455.
2015-05-21 17:04:43 +02:00
Eelco Dolstra 4ca5a9dcfd nix-collect-garbage: Don't call nix-env
Also, make sure --delete-older-than doesn't delete the current
generation.
2015-05-21 16:28:30 +02:00
Eelco Dolstra 22b1a8d43f Move profiles.{cc,hh} to libstore 2015-05-21 15:42:59 +02:00
Eelco Dolstra 13493ef97c nix-collect-garbage: Call collectGarbage() internally 2015-05-21 15:21:38 +02:00
Eelco Dolstra 4441e4cc13 nix-collect-garbage: Don't barf on unreadable directories
And don't try to delete generations from unwritable directories.
2015-05-21 15:04:05 +02:00
Eelco Dolstra 8d813fe3e0 nix-collect-garbage: Remove redundant call to getFileType 2015-05-21 14:09:34 +02:00
Eelco Dolstra a1c1bf3a56 Merge branch 'submit/sparse-generation-symlinks' of https://github.com/ctheune/nix 2015-05-21 12:04:54 +02:00
Christian Theune 12a888894b Mis-read Eelko's request to not make this an option: now, let's not make
it an option. :)
2015-05-20 17:29:52 +02:00
Christian Theune ea39c98d41 Implement alternative to lazy generations:
* only the last generation can be lazy
* depend on the '--lazy-generation' flag to be set
2015-05-19 20:03:36 +02:00
Eelco Dolstra 1529db702b Don't install nix-worker symlink
It has been obsolete since Nix 1.2.

Closes #417.
2015-05-19 16:43:33 +02:00
Eelco Dolstra 3fac75bf29 Also remove misc/vim/README.md 2015-05-19 11:01:53 +02:00
Hoang Xuan PhuandEelco Dolstra 46a56ea622 point to https://nixos.org/wiki/Vim_configuration instead 2015-05-19 11:01:41 +02:00
Charles StrahanandEelco Dolstra 9c6328a7bf nix-env: document --set option 2015-05-19 11:00:15 +02:00
Christian Theune 3d83188702 Enable lazy/sparse allocation of generation symlinks: avoid creating
new generations if a generation already exists.

Alternatively or additionally I propose a mode where only the *last* generation will be sparse.
2015-05-18 08:38:49 +02:00
Eelco Dolstra a010c0ae05 Fix "error: deriver of path ‘’ is not known" 2015-05-13 18:03:22 +02:00
Eelco Dolstra 9233ac7c56 Merge pull request #537 from garbas/master
cygwin fixes
2015-05-13 10:30:30 +02:00
Rok Garbas dad754843a cygwin: looks like stdout/stdin are reserved words 2015-05-13 09:37:56 +02:00
Rok Garbas 000de699e9 cygwin: explicitly include required c headers 2015-05-13 09:37:12 +02:00
Shea Levy 71083f9e5e Don't try to map /bin/sh to a store path on non-Linux 2015-05-12 16:36:15 -04:00
Luca Bruno 8972f7c14a nix-collect-garbage: Do not pass an empty argument. Closes #530 2015-05-06 15:17:23 +00:00
Eelco Dolstra 6519f06f39 nix-env/nix-instantiate/nix-build: Support URIs
For instance, you can install Firefox from a specific Nixpkgs revision
like this:

  $ nix-env -f https://github.com/NixOS/nixpkgs/archive/63def04891a0abc328b1b0b3a78ec02c58f48583.tar.gz -iA firefox

Or build a package from the latest nixpkgs-unstable channel:

  $ nix-build https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz -A hello
2015-05-06 14:54:31 +02:00
William A. Kennington IIIandEelco Dolstra 0705d04dfa nix-collect-garbage: Fix deleting old generations
The call to nix-env expects a string which represents how old the
derivations are or just "old" which means any generations other than
the current one in use. Currently nix-collect-garbage passes an empty
string to nix-env when using the -d option. This patch corrects the call
to nix-env such that it follows the old behavior.
2015-05-06 11:02:36 +02:00
Eelco Dolstra 9451ef3731 Allow URLs in the Nix search path
E.g. to install "hello" from the latest Nixpkgs:

  $ nix-build '<nixpkgs>' -A hello -I nixpkgs=https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz

Or to install a specific version of NixOS:

  $ nixos-rebuild switch -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/63def04891a0abc328b1b0b3a78ec02c58f48583.tar.gz
2015-05-05 17:09:42 +02:00
Eelco Dolstra 35d30d67eb Make downloads interruptable 2015-05-05 14:39:48 +02:00
Eelco Dolstra deb8668a0e nix-shell: Fix uninitialized value warning 2015-05-05 14:19:58 +02:00
aszlig 1f795f9f44 tests: Fix wrong channel name in nix-channel.sh.
The $channelName variable passed to the channel builder is the last
portion of the URL and while that works in the previous test for
channels prior to #519, it doesn't work if the last portion is
nixexprs.tar.bz2.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
2015-04-29 03:18:32 +02:00
Shea Levy 96dcc006e9 Merge branch 'nix-channel-tarballs' of git://github.com/copumpkin/nix 2015-04-27 19:07:43 -04:00
Eelco Dolstra 4bbcfaf87f Merge pull request #522 from lethalman/nix-collect-garbage
nix-collect-garbage: translate to C++
2015-04-22 19:36:52 +02:00
Luca Bruno ab2b3d6668 nix-collect-garbage: translate to C++ 2015-04-22 15:08:48 +00:00
Dan Peebles 8a84bd8c8b Support tarballs in nix channel URLs 2015-04-20 00:34:29 -04:00
Shea Levy 4d652875bd Add the pre-build hook.
This hook can be used to set system-specific per-derivation build
settings that don't fit into the derivation model and are too complex or
volatile to be hard-coded into nix. Currently, the pre-build hook can
only add chroot dirs/files through the interface, but it also has full
access to the chroot root.

The specific use case for this is systems where the operating system ABI
is more complex than just the kernel-support system calls. For example,
on OS X there is a set of system-provided frameworks that can reliably
be accessed by any program linked to them, no matter the version the
program is running on. Unfortunately, those frameworks do not
necessarily live in the same locations on each version of OS X, nor do
their dependencies, and thus nix needs to know the specific version of
OS X currently running in order to make those frameworks available. The
pre-build hook is a perfect mechanism for doing just that.
2015-04-18 16:56:02 -04:00
Shea Levy fd6774e285 Revert "Add the pre-build hook."
Going to reimplement differently.

This reverts commit 1e4a4a2e9f.
2015-04-18 14:59:58 -04:00
Shea Levy 9b1866b721 Merge branch 'add-manpath' of git://github.com/mmerickel/nix 2015-04-16 21:57:41 -04:00
Eelco Dolstra 035aeb9547 Fix using restricted mode with chroots 2015-04-16 18:46:17 +02:00
Michael Merickel 76f985b92d add the manpath to the installer 2015-04-12 20:30:47 -04:00
Shea Levy 1e4a4a2e9f Add the pre-build hook.
This hook can be used to set system specific per-derivation build
settings that don't fit into the derivation model and are too complex or
volatile to be hard-coded into nix. Currently, the pre-build hook can
only add chroot dirs/files.

The specific use case for this is systems where the operating system ABI
is more complex than just the kernel-supported system calls. For
example, on OS X there is a set of system-provided frameworks that can
reliably be accessed by any program linked to them, no matter the
version the program is running on. Unfortunately, those frameworks do
not necessarily live in the same locations on each version of OS X, nor
do their dependencies, and thus nix needs to know the specific version
of OS X currently running in order to make those frameworks available.
The pre-build hook is a perfect mechanism for doing just that.
2015-04-12 12:56:38 -04:00
Eelco Dolstra 4ed2187377 Use cached result if there is a network error 2015-04-09 12:49:13 +02:00
Eelco Dolstra 1fc905ad4c Move curl stuff into a separate file 2015-04-09 12:12:50 +02:00
Eelco Dolstra c1f04fae35 Implement a TTL on cached fetchurl/fetchTarball results
This is because we don't want to do HTTP requests on every evaluation,
even though we can prevent a full redownload via the cached ETag. The
default is one hour.
2015-04-09 11:55:36 +02:00
Eelco Dolstra 60340ce3e2 Implement caching of fetchurl/fetchTarball results
ETags are used to prevent redownloading unchanged files.
2015-04-09 11:42:04 +02:00
Eelco Dolstra 1711679ea5 Revert /nix/store permission back to 01775
This broke NixOS VM tests.

Mostly reverts 27b7b94923,
5ce50cd99e,
afa433e58c.
2015-04-07 13:21:26 +02:00
Eelco Dolstra afa433e58c Chroot builds: Provide world-readable /nix/store
This was causing NixOS VM tests to fail mysteriously since
5ce50cd99e. Nscd could (sometimes) no
longer read /etc/hosts:

open("/etc/hosts", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied)

Probably there was some wacky interaction between the guest kernel and
the 9pfs implementation in QEMU.
2015-04-02 16:59:40 +02:00
Eelco Dolstra be1ff23352 Add dependency on libcurl-dev
http://hydra.nixos.org/eval/1179370
2015-03-27 12:27:36 +01:00
Eelco Dolstra 000b5a000f Add fetchTarball builtin
This function downloads and unpacks the given URL at evaluation
time. This is primarily intended to make it easier to deal with Nix
expressions that have external dependencies. For instance, to fetch
Nixpkgs 14.12:

  with import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz) {};

Or to fetch a specific revision:

  with import (fetchTarball https://github.com/NixOS/nixpkgs/archive/2766a4b44ee6eafae03a042801270c7f6b8ed32a.tar.gz) {};

This patch also adds a ‘fetchurl’ builtin that downloads but doesn't
unpack its argument. Not sure if it's useful though.
2015-03-25 17:29:09 +01:00
Eelco Dolstra 7ea6ecf855 addToStore(): Take explicit name argument 2015-03-25 17:06:12 +01:00
Eelco Dolstra 5114a07d95 Improve setting the default chroot dirs 2015-03-24 11:57:46 +01:00
Eelco Dolstra fd89f97be9 Add the closure of store paths to the chroot
Thus, for example, to get /bin/sh in a chroot, you only need to
specify /bin/sh=${pkgs.bash}/bin/sh in build-chroot-dirs. The
dependencies of sh will be added automatically.
2015-03-24 11:52:34 +01:00
Eelco Dolstra 5ce50cd99e Tighten permissions on chroot directories 2015-03-24 11:35:53 +01:00
Eelco Dolstra 6f0c6e20e0 Don't rely on __noChroot for corepkgs
This doesn't work anymore if the "strict" chroot mode is
enabled. Instead, add Nix's store path as a dependency. This ensures
that its closure is present in the chroot.
2015-03-24 11:15:45 +01:00
Eelco Dolstra b005e63ccf Disable scanning for interior pointers
This may remove the "Repeated allocation of very large block"
warnings.
2015-03-19 20:10:08 +01:00
Eelco Dolstra 726f7f7fc9 Fix Boehm API violation
We were calling GC_INIT() after doing an allocation (in the baseEnv
construction), which is not allowed.
2015-03-19 20:02:37 +01:00
Eelco Dolstra da6b704b19 Check return values from malloc/strdup 2015-03-19 14:11:35 +01:00
Eelco Dolstra fa47279440 Print some Boehm GC stats 2015-03-18 16:24:54 +01:00
Eelco Dolstra 7a84143910 valueSize(): Take into account list/bindings/env size 2015-03-18 14:41:28 +01:00
Daniel HahlerandEelco Dolstra e659978ced Fix typos: s/the the/the/ 2015-03-06 16:43:22 +01:00
Eelco Dolstra 17c71334e1 forceValueDeep: Add to error prefix 2015-03-06 15:10:12 +01:00
Eelco Dolstra 5badc8f975 Improve error message 2015-03-06 14:24:08 +01:00
Eelco Dolstra 9f3eb56b46 Reduce verbosity in build-remote.pl 2015-03-04 16:27:42 +01:00
Eelco Dolstra 71b0a3a86b Add option to hide display of missing paths 2015-03-04 15:43:04 +01:00
Eelco Dolstra 75ede65e3d Don't use vfork() before clone()
I'm seeing hangs in Glibc's setxid_mark_thread() again. This is
probably because the use of an intermediate process to make clone()
safe from a multi-threaded program (see
524f89f139) is defeated by the use of
vfork(), since the intermediate process will have a copy of Glibc's
threading data structures due to the vfork(). So use a regular fork()
again.
2015-03-04 15:13:10 +01:00
Shea Levy c2699be93b Merge branch 'allow-system-library' of git://github.com/copumpkin/nix
Make the default impure prefix include all of /System/Library
2015-03-03 15:01:09 -05:00
Dan Peebles 336c4270c6 Make the default impure prefix (not actual allowed impurities!) include all of /System/Library, since we also want PrivateFrameworks from there and (briefly) TextEncodings, and who knows what else. Yay infectious impurities? 2015-03-02 23:01:24 -05:00
Dan Peebles 66d612f1da Allow local networking in the darwin sandbox to appease tests 2015-03-02 22:55:42 -05:00
Eelco Dolstra cffa7f80ab Typo 2015-02-23 16:02:50 +01:00
Eelco Dolstra 885bebf13b More graceful fallback for chroots on Linux < 2.13 2015-02-23 15:54:31 +01:00
Eelco Dolstra 99897f6979 Use chroots for all derivations
If ‘build-use-chroot’ is set to ‘true’, fixed-output derivations are
now also chrooted. However, unlike normal derivations, they don't get
a private network namespace, so they can still access the
network. Also, the use of the ‘__noChroot’ derivation attribute is
no longer allowed.

Setting ‘build-use-chroot’ to ‘relaxed’ gives the old behaviour.
2015-02-23 15:54:31 +01:00
Eelco Dolstra 15d2d3c34e Add restricted evaluation mode
If ‘--option restrict-eval true’ is given, the evaluator will throw an
exception if an attempt is made to access any file outside of the Nix
search path. This is primarily intended for Hydra, where we don't want
people doing ‘builtins.readFile ~/.ssh/id_dsa’ or stuff like that.
2015-02-23 15:54:31 +01:00
Shea Levy 47bdc52c1b Merge branch 'gh-476-fix-install-script' of git://github.com/jramnani/nix
sometimes cd prints to stdout
2015-02-22 12:00:51 -05:00
Shea Levy a8494de0be Merge branch 'docs/channels-path' of git://github.com/iElectric/nix 2015-02-22 11:59:38 -05:00
Domen Kožar 7bd8299c6c fixes https://github.com/NixOS/nixpkgs/issues/6485 2015-02-22 08:39:29 -08:00
Eelco Dolstra c33244d7c1 Merge branch 'tilde-paths' of https://github.com/shlevy/nix 2015-02-19 14:55:06 +01:00
Shea Levy c4653afbcd tilde paths: The rest of the string has to start with a slash anyway 2015-02-19 08:52:13 -05:00
Shea Levy e3e38a048e tilde paths: construct the entire path at parse time 2015-02-19 08:51:21 -05:00
Shea Levy 4b7c9f834c tilde paths: get HOME at parse time 2015-02-19 08:49:10 -05:00
Eelco Dolstra 9bedd9b09b Remove obsolete reference to ~ operator 2015-02-19 14:41:22 +01:00
Shea Levy 4646e94610 ExprConcatStrings: canonicalize concatenated paths 2015-02-19 08:39:25 -05:00
Eelco Dolstra 175935e053 FIXMEs 2015-02-19 14:10:33 +01:00
Shea Levy e0953d53de Allow the leading component of a path to be a ~ 2015-02-19 08:05:16 -05:00
Eelco Dolstra 1816ac0db1 Escape arguments to nix-shell #! scripts 2015-02-18 20:13:53 +01:00
Eelco Dolstra dc7e8fae48 Support passing command line arguments to nix-shell #! scripts 2015-02-18 15:55:18 +01:00
Eelco Dolstra bb10010582 Fix nix-shell shebang scripts if -p is used 2015-02-18 12:40:07 +01:00
Eelco Dolstra 147deb236e nix-store --generate-binary-cache-key: Write key to disk
This ensures proper permissions for the secret key.
2015-02-18 11:19:44 +01:00
Eelco Dolstra bd91064150 Use $<attr>Path instead of $<attr> for passAsFile 2015-02-17 16:42:54 +01:00
Eelco Dolstra a70d275f3d Allow passing attributes via files instead of environment variables
Closes #473.
2015-02-17 14:42:15 +01:00
Eelco Dolstra 29e1ff675b Keep sorted 2015-02-17 13:55:37 +01:00
Eelco Dolstra f19b4abfb2 Include NAR size in fingerprint computation
This is not strictly needed for integrity (since we already include
the NAR hash in the fingerprint) but it helps against endless data
attacks [1]. (However, this will also require
download-from-binary-cache.pl to bail out if it receives more than the
specified number of bytes.)

[1] https://isis.poly.edu/~jcappos/papers/cappos_mirror_ccs_08.pdf
2015-02-17 13:16:58 +01:00
Eelco Dolstra 8c8750ae66 Test chroot building 2015-02-16 12:20:03 +01:00
Harald van DijkandEelco Dolstra 5451b8db9d Use pivot_root in addition to chroot when possible
chroot only changes the process root directory, not the mount namespace root
directory, and it is well-known that any process with chroot capability can
break out of a chroot "jail". By using pivot_root as well, and unmounting the
original mount namespace root directory, breaking out becomes impossible.

Non-root processes typically have no ability to use chroot() anyway, but they
can gain that capability through the use of clone() or unshare(). For security
reasons, these syscalls are limited in functionality when used inside a normal
chroot environment. Using pivot_root() this way does allow those syscalls to be
put to their full use.
2015-02-16 12:18:19 +01:00
Eelco Dolstra b0bad3e615 Revert "Remove Fedora 18, 19 builds"
This reverts commit 9c58691ce3. Fedora
18/19 images should build again.
2015-02-12 17:44:29 +01:00
Jeff Ramnani d53735c823 Nix install script failed when "cd" printed to stdout.
In some cases the bash builtin command "cd" can print the variable $CWD
to stdout.  This caused the install script to fail while copying files
because the source path was wrong.

Fixes #476.
2015-02-11 12:39:14 -06:00
Eelco Dolstra b4e7eec16a Don't depend on libsodium on Darwin
It doesn't build at the moment.

http://hydra.nixos.org/build/19557641
2015-02-10 14:15:42 +01:00
Eelco Dolstra 1c972cba14 Make libsodium an optional dependency 2015-02-10 11:54:06 +01:00
Eelco Dolstra 5d9cd27dce Add Fedora 21 build
Fixes #467.
2015-02-10 11:33:33 +01:00
Eelco Dolstra a596c525ad Add base64 encoder/decoder 2015-02-10 11:33:33 +01:00
Shea Levy 70cae879e3 nix-build: Respect -Q during evaluation
Fixes #474
2015-02-08 20:44:05 -05:00
Eelco Dolstra 2be7f79fd4 Remove tab 2015-02-05 17:21:30 +01:00
Eelco Dolstra de8ed5c1d3 Typo 2015-02-04 18:17:06 +01:00
Shea LevyandEelco Dolstra d66d9e8425 Require linux 3.13 or later for chroot
Fixes #453
2015-02-04 18:15:56 +01:00
Eelco Dolstra f3a5930488 Sign a subset of the .narinfo
We only need to sign the store path, NAR hash and references (the
"fingerprint"). Everything else is irrelevant to security. For
instance, the compression algorithm or the hash of the compressed NAR
don't matter as long as the contents of the uncompressed NAR are
correct.

(Maybe we should include derivers in the fingerprint, but they're
broken and nobody cares about them. Also, it might be nice in the
future if .narinfos contained signatures from multiple independent
signers. But that's impossible if the deriver is included in the
fingerprint, since everybody will tend to have a different deriver for
the same store path.)

Also renamed the "Signature" field to "Sig" since the format changed
in an incompatible way.
2015-02-04 17:59:31 +01:00
Eelco Dolstra e0def5bc4b Use libsodium instead of OpenSSL for binary cache signing
Sodium's Ed25519 signatures are much shorter than OpenSSL's RSA
signatures. Public keys are also much shorter, so they're now
specified directly in the nix.conf option ‘binary-cache-public-keys’.

The new command ‘nix-store --generate-binary-cache-key’ generates and
prints a public and secret key.
2015-02-04 17:10:31 +01:00
Eelco Dolstra 0d1dafa0c4 Simplify parseHash32 2015-02-03 18:56:47 +01:00
Eelco Dolstra db2ec59903 Simplify printHash32 2015-02-03 18:35:24 +01:00
Jaka HudoklinandEelco Dolstra 3688db3d43 nix-install-package: follow symlinks 2015-01-30 11:30:21 +01:00
Shea Levy 73bf32ce94 Merge remote-tracking branch 'shlevy/baseNameOf-no-copy'
baseNameOf: Don't copy paths to the store first
2015-01-29 03:29:09 -05:00
Oliver DunklandEelco Dolstra de91a42c6e Moves runHook to a later execution position
It moves runHook to a later position in the rcfile. After that we are
able to set the PS1 environment-variable for a nix-shell environment
e.g.:

  # turn the color of the prompt to blue
  shellHook = ''
    export PS1="\n\[\033[1;34m\][\u@\h:\w]$\[\033[0m\] ";
  '';
2015-01-28 13:39:48 +01:00
Daniel PeeblesandShea Levy f46e329a13 Make inputs writeable in the sandbox (builds still can’t actually write due to user permissions) 2015-01-18 23:25:29 -05:00
Eelco Dolstra f6716e95bb Shut up "Wide character in print" warning in copy-from-other-stores.pl 2015-01-15 17:56:56 +01:00
Eelco Dolstra c2a8b5c42d Fix assertion failure in nix-env
$ nix-env -f ~/Dev/nixops/ -iA foo
  nix-env: src/libexpr/eval.hh:57: void nix::Bindings::push_back(const nix::Attr&): Assertion `size_ < capacity' failed.
  Aborted
2015-01-15 12:15:22 +01:00
Eelco Dolstra a5e2c8e560 Set correct user agent for NAR downloads from binary caches 2015-01-15 12:05:27 +01:00
Shea LevyandEelco Dolstra 79ca503332 Allow using /bin and /usr/bin as impure prefixes on non-darwin by default
These directories are generally world-readable anyway, and give us the two
most common linux impurities (env and sh)
2015-01-13 15:41:46 +01:00
Eelco Dolstra fcf57aad27 SysError -> Error 2015-01-13 11:17:56 +01:00
Eelco Dolstra 100961e370 Don't resolve symlinks while checking __impureHostDeps
Since these come from untrusted users, we shouldn't do any I/O on them
before we've checked that they're in an allowed prefix.
2015-01-13 11:16:32 +01:00
Daniel PeeblesandEelco Dolstra f1151a3373 Add basic Apple sandbox support 2015-01-12 12:00:01 +01:00
Tobias Geerinckx-RiceandEelco Dolstra c23d67920e doc: nix-channel --remove takes a name, not a url 2015-01-12 10:56:58 +01:00
Eelco Dolstra 2a3b1df423 Fix builtins.readDir on XFS
The DT_UNKNOWN fallback code was getting the type of the wrong path,
causing readDir to report "directory" as the type of every file.

Reported by deepfire on IRC.
2015-01-09 14:56:25 +01:00
Eelco Dolstra 57d64d24aa Doh^2 2015-01-08 16:59:22 +01:00
Eelco Dolstra 57b82256b0 Doh 2015-01-08 16:49:31 +01:00
Данило Глинський (Danylo Hlynskyi)andEelco Dolstra ed56ea980b Fix typo (assuming this is a typo)
Fix typo (assuming this is a typo)
`allowedRequisites` mentions `allowedReferences` in code example
2015-01-08 16:43:56 +01:00
Eelco Dolstra 27b7b94923 Set /nix/store permission to 1737
I.e., not readable to the nixbld group. This improves purity a bit for
non-chroot builds, because it prevents a builder from enumerating
store paths (i.e. it can only access paths it knows about).
2015-01-08 16:39:07 +01:00
Eelco Dolstra 128538ef06 nix-shell: Add --run flag
‘--run’ is like ‘--command’, except that it runs the command in a
non-interactive shell. This is important if you do things like:

  $ nix-shell --command make

Hitting Ctrl-C while make is running drops you into the interactive
Nix shell, which is probably not what you want. So you can now do

  $ nix-shell --run make

instead.
2015-01-08 15:14:38 +01:00
Eelco Dolstra b76589206a nix-shell: Interpret filenames relative to the #!-script
So you can have a script like:

  #! /usr/bin/env nix-shell
  #! nix-shell script.nix -i python

  import prettytable

  x = prettytable.PrettyTable(["Foo", "Bar"])
  for i in range(1, 10): x.add_row([i, i**2])
  print x

with a ‘script.nix’ in the same directory:

  with import <nixpkgs> {};

  runCommand "dummy" { buildInputs = [ python pythonPackages.prettytable ]; } ""

(Of course, in this particular case, using the ‘-p’ flag is more
convenient.)
2015-01-08 14:56:14 +01:00
Eelco Dolstra a957893b26 Allow nix-shell to be used as a #! interpreter
This allows scripts to fetch their own dependencies via nix-shell. For
instance, here is a Haskell script that, when executed, pulls in GHC
and the HTTP package:

  #! /usr/bin/env nix-shell
  #! nix-shell -i runghc -p haskellPackages.ghc haskellPackages.HTTP

  import Network.HTTP

  main = do
    resp <- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
    body <- getResponseBody resp
    print (take 100 body)

Or a Perl script that pulls in Perl and some CPAN packages:

  #! /usr/bin/env nix-shell
  #! nix-shell -i perl -p perl perlPackages.HTMLTokeParserSimple perlPackages.LWP

  use HTML::TokeParser::Simple;

  my $p = HTML::TokeParser::Simple->new(url => 'http://nixos.org/');

  while (my $token = $p->get_tag("a")) {
      my $href = $token->get_attr("href");
      print "$href\n" if $href;
  }

Note that the options to nix-shell must be given on a separate line
that starts with the magic string ‘#! nix-shell’. This is because
‘env’ does not allow passing arguments to an interpreter directly.
2015-01-08 14:32:45 +01:00
Eelco Dolstra 7ba0e9cb48 nix-shell --command: Remove bogus argument to "exit"
Fixes "exit: Inappropriate: numeric argument required" errors.
2015-01-07 16:10:20 +01:00
Eelco Dolstra 153a943de7 Show position info for failing <...> lookups 2015-01-07 13:43:55 +01:00
Eelco Dolstra 6fec43ccb3 Remove quotes around filenames in position info 2015-01-07 12:08:10 +01:00
Eelco Dolstra 4d5c9d85ea Document how to set up build users on Mac OS X 2015-01-06 11:17:11 +01:00
Eelco Dolstra df05f49dcd Fix building on Darwin
Fixes #433.
2015-01-06 10:49:44 +01:00
Rob Vermaas 1b167c964f Merge pull request #431 from j-keck/master
small documentation fixes
2015-01-05 15:13:51 +01:00
j-keck 14fb7378df doc: remove wrong phrase.
'... another level of indirection not shown in the figure above ...'
but in the 'user-environments.png' figure there is '~/.nix-profile'.
the figure was updated with the commit: f982df3 on Mar 16, 2005.
2015-01-05 15:08:53 +01:00
j-keck 2c052278d2 doc: remove double word
'... when when ...' -> '... when ...'
2015-01-05 13:40:19 +01:00
Eelco Dolstra 8027083c3a Allow $NIX_PAGER to override $PAGER 2015-01-02 15:26:56 +01:00
aszligandEelco Dolstra 8b88d25cda libutil: Limit readLink() error to only overflows.
Let's not just improve the error message itself, but also the behaviour
to actually work around the ntfs-3g symlink bug. If the readlink() call
returns a smaller size than the stat() call, this really isn't a problem
even if the symlink target really has changed between the calls.

So if stat() reports the size for the absolute path, it's most likely
that the relative path is smaller and thus it should also work for file
system bugs as mentioned in 93002d69fc58c2b71e2dfad202139230c630c53a.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Tested-by: John Ericson <Ericson2314@Yahoo.com>
2015-01-02 12:53:42 +01:00
aszligandEelco Dolstra bbd45ac80f libutil: Improve errmsg on readLink size mismatch.
A message like "error: reading symbolic link `...' : Success" really is
quite confusing, so let's not indicate "success" but rather point out
the real issue.

We could also limit the check of this to just check for non-negative
values, but this would introduce a race condition between stat() and
readlink() if the link target changes between those two calls, thus
leading to a buffer overflow vulnerability.

Reported by @Ericson2314 on IRC. Happened due to a possible ntfs-3g bug
where a relative symlink returned the absolute path (st_)size in stat()
while readlink() returned the relative size.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Tested-by: John Ericson <Ericson2314@Yahoo.com>
2015-01-02 12:53:42 +01:00
Eelco Dolstra 411b237ee5 edition -> subtitle
For some reason, docbook-xsl doesn't render edition.
2015-01-02 12:53:32 +01:00
Shea LevyandEelco Dolstra 3d97b8d1e7 LocalStore initialization: Don't die if build-users-group doesn't exist
See NixOS/nixpkgs@9245516
2014-12-29 14:40:13 +01:00
Eelco Dolstra bd0f362d2f Revive running builds in a PID namespace 2014-12-23 17:25:06 +01:00
Eelco Dolstra f16b8786a2 Belatedly add contributors 2014-12-16 18:58:04 +01:00
Eelco Dolstra 2162a9c1c7 Bump version number 2014-12-15 18:05:56 +01:00
Eelco Dolstra ccde347eb8 Merge pull request #420 from linquize/cygwin
Add exe, dll to .gitignore
2014-12-15 16:38:05 +01:00
Linquize 4579a44617 Add exe, dll to .gitignore 2014-12-15 23:34:13 +08:00
Shea Levy c9bd6a1de4 Fix context test 2014-10-18 20:34:48 -04:00
Shea Levy 0ee1ca628a baseNameOf: Don't copy paths to the store first 2014-10-18 20:28:28 -04:00
138 changed files with 5362 additions and 2492 deletions
+2
View File
@@ -112,6 +112,8 @@ Makefile.config
*.a
*.o
*.so
*.dll
*.exe
*.dep
*~
*.pc
+2 -1
View File
@@ -10,6 +10,7 @@ makefiles = \
src/nix-instantiate/local.mk \
src/nix-env/local.mk \
src/nix-daemon/local.mk \
src/nix-collect-garbage/local.mk \
src/download-via-ssh/local.mk \
src/nix-log2xml/local.mk \
src/bsdiff-4.3/local.mk \
@@ -25,7 +26,7 @@ makefiles = \
GLOBAL_CXXFLAGS += -std=c++0x -g -Wall
include Makefile.config
-include Makefile.config
OPTIMIZE = 1
+2
View File
@@ -4,9 +4,11 @@ CFLAGS = @CFLAGS@
CXX = @CXX@
CXXFLAGS = @CXXFLAGS@
HAVE_OPENSSL = @HAVE_OPENSSL@
HAVE_SODIUM = @HAVE_SODIUM@
OPENSSL_LIBS = @OPENSSL_LIBS@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
SODIUM_LIBS = @SODIUM_LIBS@
bash = @bash@
bindir = @bindir@
bsddiff_compat_include = @bsddiff_compat_include@
+13
View File
@@ -87,6 +87,7 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
# include <sys/param.h>
# endif
])
AC_CHECK_HEADERS([sys/syscall.h])
# Check for lutimes, optionally used for changing the mtime of
@@ -205,6 +206,18 @@ AC_CHECK_HEADERS([bzlib.h], [true],
PKG_CHECK_MODULES([SQLITE3], [sqlite3 >= 3.6.19], [CXXFLAGS="$SQLITE3_CFLAGS $CXXFLAGS"])
# Look for libcurl, a required dependency.
PKG_CHECK_MODULES([LIBCURL], [libcurl], [CXXFLAGS="$LIBCURL_CFLAGS $CXXFLAGS"])
# Look for libsodium, an optional dependency.
PKG_CHECK_MODULES([SODIUM], [libsodium],
[AC_DEFINE([HAVE_SODIUM], [1], [Whether to use libsodium for cryptography.])
CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"
have_sodium=1], [have_sodium=])
AC_SUBST(HAVE_SODIUM, [$have_sodium])
# Whether to use the Boehm garbage collector.
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=no]]),
+1 -2
View File
@@ -23,6 +23,5 @@ derivation {
# network traffic, so don't do that.
preferLocalBuild = true;
# Don't build in a chroot because Nix's dependencies may not be there.
__noChroot = true;
inherit chrootDeps;
}
+11 -2
View File
@@ -2,7 +2,7 @@ let
fromEnv = var: def:
let val = builtins.getEnv var; in
if val != "" then val else def;
in {
in rec {
perl = "@perl@";
shell = "@bash@";
coreutils = "@coreutils@";
@@ -12,6 +12,15 @@ in {
tar = "@tar@";
tarFlags = "@tarFlags@";
tr = "@tr@";
curl = "@curl@";
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
nixPrefix = "@prefix@";
# If Nix is installed in the Nix store, then automatically add it as
# a dependency to the core packages. This ensures that they work
# properly in a chroot.
chrootDeps =
if dirOf nixPrefix == builtins.storeDir then
[ (builtins.storePath nixPrefix) ]
else
[ ];
}
+3 -17
View File
@@ -5,20 +5,9 @@ with import <nix/config.nix>;
assert (outputHash != "" && outputHashAlgo != "")
|| md5 != "" || sha1 != "" || sha256 != "";
let
builder = builtins.toFile "fetchurl.sh"
(''
echo "downloading $url into $out"
${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out"
'' + (if executable then "${coreutils}/chmod +x $out" else ""));
in
derivation {
name = baseNameOf (toString url);
builder = shell;
args = [ "-e" builder ];
builder = "builtin:fetchurl";
# New-style output content requirements.
outputHashAlgo = if outputHashAlgo != "" then outputHashAlgo else
@@ -26,15 +15,12 @@ derivation {
outputHash = if outputHash != "" then outputHash else
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
outputHashMode = if executable then "recursive" else "flat";
inherit system url;
inherit system url executable;
# No need to double the amount of network traffic
preferLocalBuild = true;
# Don't build in a chroot because Nix's dependencies may not be there.
__noChroot = true;
impureEnvVars = [
# We borrow these environment variables from the caller to allow
# easy proxy configuration. This is impure, but a fixed-output
+2 -2
View File
@@ -1,10 +1,10 @@
attrs @ { drvPath, outputs, ... }:
attrs @ { drvPath, outputs, name, ... }:
let
commonAttrs = (builtins.listToAttrs outputsList) //
{ all = map (x: x.value) outputsList;
inherit drvPath;
inherit drvPath name;
type = "derivation";
};
+2 -3
View File
@@ -41,9 +41,8 @@ derivation {
args = [ "-e" builder ];
inherit storePath hashAlgo compressionType;
# Don't build in a chroot because Nix's dependencies may not be there.
__noChroot = true;
# Remote machines may not have ${nixBinDir} or ${coreutils} in the same prefixes
preferLocalBuild = true;
inherit chrootDeps;
}
+1 -2
View File
@@ -37,6 +37,5 @@ derivation {
# No point in doing this remotely.
preferLocalBuild = true;
# Don't build in a chroot because Nix's dependencies may not be there.
__noChroot = true;
inherit chrootDeps;
}
+90 -12
View File
@@ -227,23 +227,32 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
<varlistentry><term><literal>build-use-chroot</literal></term>
<listitem><para>If set to <literal>true</literal>, builds will be
performed in a <emphasis>chroot environment</emphasis>, i.e., the
build will be isolated from the normal file system hierarchy and
will only see its dependencies in the Nix store, the temporary
build directory, private versions of <filename>/proc</filename>,
performed in a <emphasis>chroot environment</emphasis>, i.e.,
theyre isolated from the normal file system hierarchy and will
only see their dependencies in the Nix store, the temporary build
directory, private versions of <filename>/proc</filename>,
<filename>/dev</filename>, <filename>/dev/shm</filename> and
<filename>/dev/pts</filename>, and the paths configured with the
<link linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
option</link>. This is useful to prevent undeclared dependencies
on files in directories such as
<filename>/usr/bin</filename>.</para>
on files in directories such as <filename>/usr/bin</filename>. In
addition, on Linux, builds run in private PID, mount, network, IPC
and UTS namespaces to isolate them from other processes in the
system (except that fixed-output derivations do not run in private
network namespace to ensure they can access the network).</para>
<para>The use of a chroot requires that Nix is run as root (so you
should use the <link linkend='conf-build-users-group'>“build
users” feature</link> to perform the actual builds under different
users than root). Currently, chroot builds only work on Linux
because Nix uses “bind mounts” to make the Nix store and other
directories available inside the chroot.</para>
<para>Currently, chroots only work on Linux and Mac OS X. The use
of a chroot requires that Nix is run as root (so you should use
the <link linkend='conf-build-users-group'>build users”
feature</link> to perform the actual builds under different users
than root).</para>
<para>If this option is set to <literal>relaxed</literal>, then
fixed-output derivations and derivations that have the
<varname>__noChroot</varname> attribute set to
<literal>true</literal> do not run in chroots.</para>
<para>The default is <literal>false</literal>.</para>
</listitem>
@@ -392,6 +401,26 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry>
<varlistentry><term><literal>signed-binary-caches</literal></term>
<listitem><para>If set to <literal>*</literal>, Nix will only
download binaries if they are signed using one of the keys listed
in <option>binary-cache-public-keys</option>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-cache-public-keys</literal></term>
<listitem><para>A whitespace-separated list of public keys
corresponding to the secret keys trusted to sign binary
caches. For example:
<literal>cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
<listitem><para>The maximum number of parallel HTTP connections
@@ -538,6 +567,55 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
</varlistentry>
<varlistentry xml:id="conf-restrict-eval"><term><literal>restrict-eval</literal></term>
<listitem>
<para>If set to <literal>true</literal>, the Nix evaluator will
not allow access to any files outside of the Nix search path (as
set via the <envar>NIX_PATH</envar> environment variable or the
<option>-I</option> option). The default is
<literal>false</literal>.</para>
</listitem>
</varlistentry>
<varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term>
<listitem>
<para>If set, the path to a program that can set extra
derivation-specific settings for this system. This is used for settings
that can't be captured by the derivation model itself and are too variable
between different versions of the same system to be hard-coded into nix.
</para>
<para>The hook is passed the derivation path and, if chroots are enabled,
the chroot directory. It can then modify the chroot and send a series of
commands to modify various settings to stdout. The currently recognized
commands are:</para>
<variablelist>
<varlistentry xml:id="extra-chroot-dirs"><term><literal>extra-chroot-dirs</literal></term>
<listitem>
<para>Pass a list of files and directories to be included in the
chroot for this build. One entry per line, terminated by an empty
line. Entries have the same format as build-chroot-dirs.</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>
</variablelist>
</para>
+14 -2
View File
@@ -36,8 +36,20 @@ nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
<literal>&lt;nixpkgs/<replaceable>path</replaceable>></literal> in
<filename>/home/eelco/Dev/nixpkgs-branch/<replaceable>path</replaceable></filename>
and
<filename>/etc/nixos/nixpkgs/<replaceable>path</replaceable></filename>.
</para>
<filename>/etc/nixos/nixpkgs/<replaceable>path</replaceable></filename>.</para>
<para>If a path in the Nix search path starts with
<literal>http://</literal> or <literal>https://</literal>, it is
interpreted as the URL of a tarball that will be downloaded and
unpacked to a temporary location. The tarball must consist of a
single top-level directory. For example, setting
<envar>NIX_PATH</envar> to
<screen>
nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz</screen>
tells Nix to download the latest revision in the Nixpkgs/NixOS
14.12 channel.</para>
<para>The search path can be extended using the <option
linkend="opt-I">-I</option> option, which takes precedence over
+16
View File
@@ -58,6 +58,13 @@ and so on).</para>
<command>nix-build</command> will use <filename>default.nix</filename>
in the current directory, if it exists.</para>
<para>If an element of <replaceable>paths</replaceable> starts with
<literal>http://</literal> or <literal>https://</literal>, it is
interpreted as the URL of a tarball that will be downloaded and
unpacked to a temporary location. The tarball must include a single
top-level directory containing at least a file named
<filename>default.nix</filename>.</para>
<para><command>nix-build</command> is essentially a wrapper around
<link
linkend="sec-nix-instantiate"><command>nix-instantiate</command></link>
@@ -175,6 +182,15 @@ bar
</para>
<para>Build the GNU Hello package from the latest revision of the
master branch of Nixpkgs:
<screen>
$ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz -A hello
</screen>
</para>
</refsection>
+3 -3
View File
@@ -21,7 +21,7 @@
<command>nix-channel</command>
<group choice='req'>
<arg choice='plain'><option>--add</option> <replaceable>url</replaceable> <arg choice='opt'><replaceable>name</replaceable></arg></arg>
<arg choice='plain'><option>--remove</option> <replaceable>url</replaceable></arg>
<arg choice='plain'><option>--remove</option> <replaceable>name</replaceable></arg>
<arg choice='plain'><option>--list</option></arg>
<arg choice='plain'><option>--update</option> <arg rep='repeat'><replaceable>names</replaceable></arg></arg>
<arg choice='plain'><option>--rollback</option> <arg choice='opt'><replaceable>generation</replaceable></arg></arg>
@@ -129,7 +129,7 @@ $ nix-instantiate --eval -E '(import &lt;nixpkgs> {}).lib.nixpkgsVersion'
<variablelist>
<varlistentry><term><filename>/nix/var/nix/profiles/<replaceable>username</replaceable>/channels</filename></term>
<varlistentry><term><filename>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels</filename></term>
<listitem><para><command>nix-channel</command> uses a
<command>nix-env</command> profile to keep track of previous
@@ -144,7 +144,7 @@ $ nix-instantiate --eval -E '(import &lt;nixpkgs> {}).lib.nixpkgsVersion'
<varlistentry><term><filename>~/.nix-defexpr/channels</filename></term>
<listitem><para>This is a symlink to
<filename>/nix/var/nix/profiles/<replaceable>username</replaceable>/channels</filename>. It
<filename>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels</filename>. It
ensures that <command>nix-env</command> can find your channels. In
a multi-user installation, you may also have
<filename>~/.nix-defexpr/channels_root</filename>, which links to
@@ -30,6 +30,7 @@
<arg><option>--include-outputs</option></arg>
<arg><option>--use-substitutes</option></arg>
<arg><option>-s</option></arg>
<arg><option>-v</option></arg>
<arg choice='plain'>
<replaceable>user@</replaceable><replaceable>machine</replaceable>
</arg>
@@ -138,6 +139,12 @@ those paths. If this bothers you, use
</varlistentry>
<varlistentry><term><option>-v</option></term>
<listitem><para>Show verbose output.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
+121 -14
View File
@@ -154,7 +154,15 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<option>--install</option>, <option>--upgrade</option>, and
<option>--query --available</option> operations to obtain
derivations. The default is
<filename>~/.nix-defexpr</filename>.</para></listitem>
<filename>~/.nix-defexpr</filename>.</para>
<para>If the argument starts with <literal>http://</literal> or
<literal>https://</literal>, it is interpreted as the URL of a
tarball that will be downloaded and unpacked to a temporary
location. The tarball must include a single top-level directory
containing at least a file named <filename>default.nix</filename>.</para>
</listitem>
</varlistentry>
@@ -416,7 +424,7 @@ $ nix-env --install gcc-3.3.2
installing `gcc-3.3.2'
uninstalling `gcc-3.1'</screen>
Note the the previously installed version is removed, since
Note the previously installed version is removed, since
<option>--preserve-installed</option> was not specified.</para>
<para>To install an arbitrary version:
@@ -496,6 +504,18 @@ the following paths will be substituted:
</para>
<para>To install Firefox from the latest revision in the Nixpkgs/NixOS
14.12 channel:
<screen>
$ nix-env -f https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz -iA firefox
</screen>
(The GitHub repository <literal>nixpkgs-channels</literal> is updated
automatically from the main <literal>nixpkgs</literal> repository
after certain tests have succeeded and binaries have been built and
uploaded to the binary cache at <uri>cache.nixos.org</uri>.)</para>
</refsection>
</refsection>
@@ -701,6 +721,44 @@ $ nix-env -e '.*' <lineannotation>(remove everything)</lineannotation></screen>
<!--######################################################################-->
<refsection xml:id="rsec-nix-env-set"><title>Operation <option>--set</option></title>
<refsection><title>Synopsis</title>
<cmdsynopsis>
<command>nix-env</command>
<arg choice='plain'><option>--set</option></arg>
<arg choice='plain'><replaceable>drvname</replaceable></arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>The <option>--set</option> operation modifies the current generation of a
profile so that it contains exactly the specified derivation, and nothing else.
</para>
</refsection>
<refsection><title>Examples</title>
<para>
The following updates a profile such that its current generation will contain
just Firefox:
<screen>
$ nix-env -p /nix/var/nix/profiles/browser --set firefox</screen>
</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection xml:id="rsec-nix-env-set-flag"><title>Operation <option>--set-flag</option></title>
@@ -1091,54 +1149,103 @@ user environment elements, etc. -->
<refsection><title>Examples</title>
<para>To show installed packages:
<screen>
$ nix-env -q <lineannotation>(show installed derivations)</lineannotation>
$ nix-env -q
bison-1.875c
docbook-xml-4.2
firefox-1.0.4
MPlayer-1.0pre7
ORBit2-2.8.3
...
<replaceable></replaceable>
</screen>
$ nix-env -qa <lineannotation>(show available derivations)</lineannotation>
</para>
<para>To show available packages:
<screen>
$ nix-env -qa
firefox-1.0.7
GConf-2.4.0.1
MPlayer-1.0pre7
ORBit2-2.8.3
...
<replaceable></replaceable>
</screen>
$ nix-env -qas <lineannotation>(show status of available derivations)</lineannotation>
</para>
<para>To show the status of available packages:
<screen>
$ nix-env -qas
-P- firefox-1.0.7 <lineannotation>(not installed but present)</lineannotation>
--S GConf-2.4.0.1 <lineannotation>(not present, but there is a substitute for fast installation)</lineannotation>
--S MPlayer-1.0pre3 <lineannotation>(i.e., this is not the installed MPlayer, even though the version is the same!)</lineannotation>
IP- ORBit2-2.8.3 <lineannotation>(installed and by definition present)</lineannotation>
...
<replaceable></replaceable>
</screen>
<lineannotation>(show available derivations in the Nix expression <!-- !!! <filename>-->foo.nix<!-- </filename> -->)</lineannotation>
</para>
<para>To show available packages in the Nix expression <filename>foo.nix</filename>:
<screen>
$ nix-env -f ./foo.nix -qa
foo-1.2.3
</screen>
$ nix-env -qc <lineannotation>(compare installed versions to whats available)</lineannotation>
</para>
<para>To compare installed versions to whats available:
<screen>
$ nix-env -qc
<replaceable>...</replaceable>
acrobat-reader-7.0 - ? <lineannotation>(package is not available at all)</lineannotation>
autoconf-2.59 = 2.59 <lineannotation>(same version)</lineannotation>
firefox-1.0.4 &lt; 1.0.7 <lineannotation>(a more recent version is available)</lineannotation>
<replaceable>...</replaceable>
</screen>
$ nix-env -qa '.*zip.*' <lineannotation>(show all packages with “zip” in the name)</lineannotation>
</para>
<para>To show all packages with “<literal>zip</literal>” in the name:
<screen>
$ nix-env -qa '.*zip.*'
bzip2-1.0.6
gzip-1.6
zip-3.0
<replaceable>...</replaceable>
<replaceable></replaceable>
</screen>
$ nix-env -qa '.*(firefox|chromium).*' <lineannotation>(show all packages with “firefox” or “chromium” in the name)</lineannotation>
</para>
<para>To show all packages with “<literal>firefox</literal>” or
<literal>chromium</literal>” in the name:
<screen>
$ nix-env -qa '.*(firefox|chromium).*'
chromium-37.0.2062.94
chromium-beta-38.0.2125.24
firefox-32.0.3
firefox-with-plugins-13.0.1
<replaceable>...</replaceable>
<replaceable></replaceable>
</screen>
</para>
<para>To show all packages in the latest revision of the Nixpkgs
repository:
<screen>
$ nix-env -f https://github.com/NixOS/nixpkgs/archive/master.tar.gz -qa
</screen>
</para>
</refsection>
</refsection>
+71 -6
View File
@@ -27,6 +27,7 @@
<arg><option>--manifest</option></arg>
<arg><option>--manifest-path</option> <replaceable>filename</replaceable></arg>
<arg><option>--url-prefix</option> <replaceable>url</replaceable></arg>
<arg><option>--key-file</option> <replaceable>path</replaceable></arg>
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -43,7 +44,7 @@ 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
@@ -101,9 +102,9 @@ automatically.</para>
<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
instead of <command>xz</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>
@@ -155,6 +156,19 @@ automatically.</para>
</varlistentry>
<varlistentry><term><option>--key-file</option> <replaceable>path</replaceable></term>
<listitem><para>Sign the binary cache using the secret key stored
in <replaceable>path</replaceable>. This secret key must have been
created using <command
linkend="rsec-nix-store-generate-binary-cache-key">nix-store
--generate-binary-cache-key</command>. Users of this binary cache
should add the corresponding public key to the option
<option>binary-cache-public-keys</option> in
<filename>nix.conf</filename>.</para></listitem>
</varlistentry>
</variablelist>
</refsection>
@@ -203,6 +217,40 @@ $ nix-pull http://example.org/cache
to cause the binaries to be used by subsequent Nix operations.</para>
<para>To generate a signed binary cache, you must first generate a key
pair, in this example called <literal>cache.example.org-1</literal>,
storing the secret key in <filename>./sk</filename> and the public key
in <filename>./pk</filename>:
<screen>
$ nix-store --generate-binary-cache-key cache.example.org-1 sk pk
$ cat sk
cache.example.org-1:jcMRQYFo8pQKzTtimpQLIPeHkMYZjfhB24hGfwF+u9PuX8H8FO7q564+X3G/JDlqqIqGar3OXRRwS9N3Wh3vbw==
$ cat pk
cache.example.org-1:7l/B/BTu6ueuPl9xvyQ5aqiKhmq9zl0UcEvTd1od728=
</screen>
You can then generate a binary cache signed with the secret key:
<screen>
$ nix-push --dest /tmp/cache --key-file ./sk $(type -p firefox)
</screen>
Users who wish to verify the integrity of binaries downloaded from
your cache would add the following to their
<filename>nix.conf</filename>:
<programlisting>
binary-caches = http://cache.example.org
signed-binary-caches = *
binary-cache-public-keys = cache.example.org-1:7l/B/BTu6ueuPl9xvyQ5aqiKhmq9zl0UcEvTd1od728=
</programlisting>
Nix will then ignore any binary that has a missing, incorrect or
unrecognised signature.</para>
</refsection>
@@ -224,7 +272,7 @@ Priority: 10
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
@@ -303,12 +351,13 @@ NarHash: sha256:0s491y1h9hxj5ghiizlxk7ax6jwbha00zwn7lpyd5xg5bhf60vzg
NarSize: 109521136
References: 2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16 ...
Deriver: 7akyyc87ka32xwmqza9dvyg5pwx3j212-glibc-2.7.drv
Sig: cache.example.org-1:WepnSp2UT0odDpR3NRjPVhJBHmdBgSBSTbHpdh4SCz92nGXwFY82bkPEmISoC0hGqBXDXEmB6y3Ohgna3mMgDg==
</screen>
The fields are as follows:
<variablelist>
<varlistentry><term><literal>StorePath</literal></term>
<listitem><para>The full store path, including the name part
@@ -381,6 +430,22 @@ The fields are as follows:
</varlistentry>
<varlistentry><term><literal>Sig</literal></term>
<listitem><para>A signature of the the form
<literal><replaceable>key-name</replaceable>:<replaceable>sig</replaceable></literal>,
where <replaceable>key-name</replaceable> is the symbolic name of
the key pair used to sign and verify the cache
(e.g. <literal>cache.example.org-1</literal>), and
<replaceable>sig</replaceable> is the actual signature, computed
over the <varname>StorePath</varname>, <varname>NarHash</varname>,
<varname>NarSize</varname> and <varname>References</varname>
fields using the <link
xlink:href="http://ed25519.cr.yp.to/">Ed25519 public-key signature
system</link>.</para></listitem>
</varlistentry>
</variablelist>
</para>
+162 -5
View File
@@ -29,6 +29,7 @@
<replaceable>attrPath</replaceable>
</arg>
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
<arg><option>--run</option> <replaceable>cmd</replaceable></arg>
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
<arg><option>--pure</option></arg>
<group choice='req'>
@@ -60,6 +61,13 @@ derivation for development.</para>
<filename>shell.nix</filename> if it exists, and
<filename>default.nix</filename> otherwise.</para>
<para>If <replaceable>path</replaceable> starts with
<literal>http://</literal> or <literal>https://</literal>, it is
interpreted as the URL of a tarball that will be downloaded and
unpacked to a temporary location. The tarball must include a single
top-level directory containing at least a file named
<filename>default.nix</filename>.</para>
<para>If the derivation defines the variable
<varname>shellHook</varname>, it will be evaluated after
<literal>$stdenv/setup</literal> has been sourced. Since this hook is
@@ -92,11 +100,24 @@ also <xref linkend="sec-common-options" />.</phrase></para>
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
<listitem><para>In the environment of the derivation, run the
shell command <replaceable>cmd</replaceable> instead of starting
an interactive shell. However, if you end the shell command with
<literal>return</literal>, you still get an interactive shell.
This can be useful for doing any additional
initialisation.</para></listitem>
shell command <replaceable>cmd</replaceable>. This command is
executed in an interactive shell. (Use <option>--run</option> to
use a non-interactive shell instead.) However, a call to
<literal>exit</literal> is implicitly added to the command, so the
shell will exit after running the command. To prevent this, add
<literal>return</literal> at the end; e.g. <literal>--command
"echo Hello; return"</literal> will print <literal>Hello</literal>
and then drop you into the interactive shell. This can be useful
for doing any additional initialisation.</para></listitem>
</varlistentry>
<varlistentry><term><option>--run</option> <replaceable>cmd</replaceable></term>
<listitem><para>Like <option>--command</option>, but executes the
command in a non-interactive shell. This means (among other
things) that if you hit Ctrl-C while the command is running, the
shell exits.</para></listitem>
</varlistentry>
@@ -135,6 +156,15 @@ also <xref linkend="sec-common-options" />.</phrase></para>
</varlistentry>
<varlistentry><term><option>-i</option> <replaceable>interpreter</replaceable></term>
<listitem><para>The chained script interpreter to be invoked by
<command>nix-shell</command>. Only applicable in
<literal>#!</literal>-scripts (described <link
linkend="ssec-nix-shell-shebang">below</link>).</para>
</listitem></varlistentry>
</variablelist>
<para>The following common options are supported:</para>
@@ -184,6 +214,133 @@ $ nix-shell -p sqlite xorg.libX11
… -L/nix/store/j1zg5v…-sqlite-3.8.0.2/lib -L/nix/store/0gmcz9…-libX11-1.6.1/lib …
</screen>
The <command>-p</command> flag looks up Nixpkgs in the Nix search
path. You can override it by passing <option>-I</option> or setting
<envar>NIX_PATH</envar>. For example, the following gives you a shell
containing the Pan package from a specific revision of Nixpkgs:
<screen>
$ nix-shell -p pan -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/8a3eea054838b55aca962c3fbde9c83c102b8bf2.tar.gz
[nix-shell:~]$ pan --version
Pan 0.139
</screen>
</para>
</refsection>
<refsection xml:id="ssec-nix-shell-shebang"><title>Use as a <literal>#!</literal>-interpreter</title>
<para>You can use <command>nix-shell</command> as a script interpreter
to allow scripts written in arbitrary languages to obtain their own
dependencies via Nix. This is done by starting the script with the
following lines:
<programlisting>
#! /usr/bin/env nix-shell
#! nix-shell -i <replaceable>real-interpreter</replaceable> -p <replaceable>packages</replaceable>
</programlisting>
where <replaceable>real-interpreter</replaceable> is the “real” script
interpreter that will be invoked by <command>nix-shell</command> after
it has obtained the dependencies and initialised the environment, and
<replaceable>packages</replaceable> are the attribute names of the
dependencies in Nixpkgs.</para>
<para>The lines starting with <literal>#! nix-shell</literal> specify
<command>nix-shell</command> options (see above). Note that you cannot
write <literal>#1 /usr/bin/env nix-shell -i ...</literal> because
<command>/usr/bin/env</command> does not support passing options to
the interpreter.</para>
<para>For example, here is a Python script that depends on Python and
the <literal>prettytable</literal> package:
<programlisting>
#! /usr/bin/env nix-shell
#! nix-shell -i python -p python pythonPackages.prettytable
import prettytable
# Print a simple table.
t = prettytable.PrettyTable(["N", "N^2"])
for n in range(1, 10): t.add_row([n, n * n])
print t
</programlisting>
</para>
<para>Similarly, the following is a Perl script that specifies that it
requires Perl and the <literal>HTML::TokeParser::Simple</literal> and
<literal>LWP</literal> packages:
<programlisting>
#! /usr/bin/env nix-shell
#! nix-shell -i perl -p perl perlPackages.HTMLTokeParserSimple perlPackages.LWP
use HTML::TokeParser::Simple;
# Fetch nixos.org and print all hrefs.
my $p = HTML::TokeParser::Simple->new(url => 'http://nixos.org/');
while (my $token = $p->get_tag("a")) {
my $href = $token->get_attr("href");
print "$href\n" if $href;
}
</programlisting>
</para>
<para>Finally, the following Haskell script uses a specific branch of
Nixpkgs/NixOS (the 14.12 stable branch):
<programlisting><![CDATA[
#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p haskellPackages.ghc haskellPackages.HTTP haskellPackages.tagsoup
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz
import Network.HTTP
import Text.HTML.TagSoup
-- Fetch nixos.org and print all hrefs.
main = do
resp <- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
body <- getResponseBody resp
let tags = filter (isTagOpenName "a") $ parseTags body
let tags' = map (fromAttrib "href") tags
mapM_ putStrLn $ filter (/= "") tags'
]]></programlisting>
If you want to be even more precise, you can specify a specific
revision of Nixpkgs:
<programlisting>
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/0672315759b3e15e2121365f067c1c8c56bb4722.tar.gz
</programlisting>
</para>
<para>The examples above all used <option>-p</option> to get
dependencies from Nixpkgs. You can also use a Nix expression to build
your own dependencies. For example, the Python example could have been
written as:
<programlisting>
#! /usr/bin/env nix-shell
#! nix-shell deps.nix -i python
</programlisting>
where the file <filename>deps.nix</filename> in the same directory
as the <literal>#!</literal>-script contains:
<programlisting>
with import &lt;nixpkgs> {};
runCommand "dummy" { buildInputs = [ python pythonPackages.prettytable ]; } ""
</programlisting>
</para>
</refsection>
+50
View File
@@ -1338,6 +1338,56 @@ $ nix-store --clear-failed-paths *
</refsection>
<!--######################################################################-->
<refsection xml:id='rsec-nix-store-generate-binary-cache-key'><title>Operation <option>--generate-binary-cache-key</option></title>
<refsection>
<title>Synopsis</title>
<cmdsynopsis>
<command>nix-store</command>
<arg choice='plain'>
<option>--generate-binary-cache-key</option>
<option>key-name</option>
<option>secret-key-file</option>
<option>public-key-file</option>
</arg>
</cmdsynopsis>
</refsection>
<refsection><title>Description</title>
<para>This command generates an <link
xlink:href="http://ed25519.cr.yp.to/">Ed25519 key pair</link> that can
be used to create a signed binary cache. It takes three mandatory
parameters:
<orderedlist>
<listitem><para>A key name, such as
<literal>cache.example.org-1</literal>, that is used to look up keys
on the client when it verifies signatures. It can be anything, but
its suggested to use the host name of your cache
(e.g. <literal>cache.example.org</literal>) with a suffix denoting
the number of the key (to be incremented every time you need to
revoke a key).</para></listitem>
<listitem><para>The file name where the secret key is to be
stored.</para></listitem>
<listitem><para>The file name where the public key is to be
stored.</para></listitem>
</orderedlist>
For an example, see the manual page for <command
linkend="sec-nix-push">nix-push</command>.</para>
</refsection>
</refsection>
<!--######################################################################-->
<refsection condition="manpage"><title>Environment variables</title>
+48 -21
View File
@@ -40,7 +40,7 @@ allowedReferences = [];
recursively. For example,
<programlisting>
allowedReferences = [ foobar ];
allowedRequisites = [ foobar ];
</programlisting>
enforces that the output of a derivation cannot have any other
@@ -90,6 +90,33 @@ derivation {
</varlistentry>
<varlistentry><term><varname>impureEnvVars</varname></term>
<listitem><para>This attribute allows you to specify a list of
environment variables that should be passed from the environment
of the calling user to the builder. Usually, the environment is
cleared completely when the builder is executed, but with this
attribute you can allow specific environment variables to be
passed unmodified. For example, <function>fetchurl</function> in
Nixpkgs has the line
<programlisting>
impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
</programlisting>
to make it use the proxy server configuration specified by the
user in the environment variables <envar>http_proxy</envar> and
friends.</para>
<para>This attribute is only allowed in <link
linkend="fixed-output-drvs">fixed-output derivations</link>, where
impurities such as these are okay since (the hash of) the output
is known in advance. It is ignored for all other
derivations.</para></listitem>
</varlistentry>
<varlistentry xml:id="fixed-output-drvs">
<term><varname>outputHash</varname></term>
<term><varname>outputHashAlgo</varname></term>
@@ -215,29 +242,29 @@ stdenv.mkDerivation {
</varlistentry>
<varlistentry><term><varname>impureEnvVars</varname></term>
<varlistentry><term><varname>passAsFile</varname></term>
<listitem><para>This attribute allows you to specify a list of
environment variables that should be passed from the environment
of the calling user to the builder. Usually, the environment is
cleared completely when the builder is executed, but with this
attribute you can allow specific environment variables to be
passed unmodified. For example, <function>fetchurl</function> in
Nixpkgs has the line
<listitem><para>A list of names of attributes that should be
passed via files rather than environment variables. For example,
if you have
<programlisting>
impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
</programlisting>
<programlisting>
passAsFile = ["big"];
big = "a very long string";
</programlisting>
to make it use the proxy server configuration specified by the
user in the environment variables <envar>http_proxy</envar> and
friends.</para>
<para>This attribute is only allowed in <link
linkend="fixed-output-drvs">fixed-output derivations</link>, where
impurities such as these are okay since (the hash of) the output
is known in advance. It is ignored for all other
derivations.</para></listitem>
then when the builder runs, the environment variable
<envar>bigPath</envar> will contain the absolute path to a
temporary file containing <literal>a very long
string</literal>. That is, for any attribute
<replaceable>x</replaceable> listed in
<varname>passAsFile</varname>, Nix will pass an environment
variable <envar><replaceable>x</replaceable>Path</envar> holding
the path of the file containing the value of attribute
<replaceable>x</replaceable>. This is useful when you need to pass
large strings to a builder, since most operating systems impose a
limit on the size of the environment (typically, a few hundred
kilobyte).</para></listitem>
</varlistentry>
+133 -1
View File
@@ -39,6 +39,28 @@ available as <function>builtins.derivation</function>.</para>
</varlistentry>
<varlistentry><term><function>builtins.all</function>
<replaceable>pred</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Return <literal>true</literal> if the function
<replaceable>pred</replaceable> returns <literal>true</literal>
for all elements of <replaceable>list</replaceable>,
and <literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.any</function>
<replaceable>pred</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Return <literal>true</literal> if the function
<replaceable>pred</replaceable> returns <literal>true</literal>
for at least one element of <replaceable>list</replaceable>,
and <literal>false</literal> otherwise.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.attrNames</function>
<replaceable>set</replaceable></term>
@@ -211,6 +233,45 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.fetchurl</function>
<replaceable>url</replaceable></term>
<listitem><para>Download the specified URL and return the path of
the downloaded file. This function is not available if <link
linkend="conf-restrict-eval">restricted evaluation mode</link> is
enabled.</para></listitem>
</varlistentry>
<varlistentry><term><function>fetchTarball</function>
<replaceable>url</replaceable></term>
<listitem><para>Download the specified URL, unpack it and return
the path of the unpacked tree. The file must be a tape archive
(<filename>.tar</filename>) compressed with
<literal>gzip</literal>, <literal>bzip2</literal> or
<literal>xz</literal>. The top-level path component of the files
in the tarball is removed, so it is best if the tarball contains a
single directory at top level. The typical use of the function is
to obtain external Nix expression dependencies, such as a
particular version of Nixpkgs, e.g.
<programlisting>
with import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz) {};
stdenv.mkDerivation { … }
</programlisting>
</para>
<para>This function is not available if <link
linkend="conf-restrict-eval">restricted evaluation mode</link> is
enabled.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.filter</function>
<replaceable>f</replaceable> <replaceable>xs</replaceable></term>
@@ -274,6 +335,19 @@ stdenv.mkDerivation {
</varlistentry>
<varlistentry><term><function>builtins.foldl</function>
<replaceable>op</replaceable> <replaceable>nul</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Reduce a list by applying a binary operator, from
left to right, e.g. <literal>foldl op nul [x0 x1 x2 ...] = op (op
(op nul x0) x1) x2) ...</literal>. The operator is applied
strictly, i.e., its arguments are evaluated first. For example,
<literal>foldl (x: y: x + y) 0 [1 2 3]</literal> evaluates to
6.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term>
<listitem><para>Convert a JSON string to a Nix
@@ -290,6 +364,24 @@ builtins.fromJSON ''{"x": [1, 2, 3], "y": null}''
</varlistentry>
<varlistentry><term><function>builtins.genList</function>
<replaceable>generator</replaceable> <replaceable>length</replaceable></term>
<listitem><para>Generate list of size
<replaceable>length</replaceable>, with each element
<replaceable>i></replaceable> equal to the value returned by
<replaceable>generator</replaceable> <literal>i</literal>. For
example,
<programlisting>
builtins.genList (x: x * x) 5
</programlisting>
returns the list <literal>[ 0 1 4 9 16 ]</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.getAttr</function>
<replaceable>s</replaceable> <replaceable>set</replaceable></term>
@@ -624,7 +716,7 @@ in config.someSetting</programlisting>
./A</literal> will return the set
<programlisting>
{ A = "regular"; B = "directory"; }</programlisting>
{ B = "regular"; C = "directory"; }</programlisting>
The possible values for the file type are
<literal>"regular"</literal>, <literal>"directory"</literal>,
@@ -659,6 +751,23 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.replaceStrings</function>
<replaceable>from</replaceable> <replaceable>to</replaceable> <replaceable>s</replaceable></term>
<listitem><para>Given string <replaceable>s</replaceable>, replace
every occurrence of the strings in <replaceable>from</replaceable>
with the corresponding string in
<replaceable>to</replaceable>. For example,
<programlisting>
builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar"
</programlisting>
evaluates to <literal>"fabir"</literal>.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.seq</function>
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
@@ -670,6 +779,29 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
</varlistentry>
<varlistentry><term><function>builtins.sort</function>
<replaceable>comparator</replaceable> <replaceable>list</replaceable></term>
<listitem><para>Return <replaceable>list</replaceable> in sorted
order. It repeatedly calls the function
<replaceable>comparator</replaceable> with two elements. The
comparator should return <literal>true</literal> if the first
element is less than the second, and <literal>false</literal>
otherwise. For example,
<programlisting>
builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
</programlisting>
produces the list <literal>[ 42 77 147 249 483 526
]</literal>.</para>
<para>This is a stable sort: it preserves the relative order of
elements deemed equal by the comparator.</para></listitem>
</varlistentry>
<varlistentry><term><function>builtins.stringLength</function>
<replaceable>e</replaceable></term>
+8 -1
View File
@@ -155,7 +155,14 @@ stdenv.mkDerivation {
expression that contained it. For instance, if a Nix expression in
<filename>/foo/bar/bla.nix</filename> refers to
<filename>../xyzzy/fnord.nix</filename>, the absolute path is
<filename>/foo/xyzzy/fnord.nix</filename>.</para></listitem>
<filename>/foo/xyzzy/fnord.nix</filename>.</para>
<para>If the first component of a path is a <literal>~</literal>,
it is interpreted as if the rest of the path were relative to the
user's home directory. e.g. <filename>~/foo</filename> would be
equivalent to <filename>/home/edolstra/foo</filename> for a user
whose home directory is <filename>/home/edolstra</filename>.
</para></listitem>
<listitem><para><emphasis>Booleans</emphasis> with values
<literal>true</literal> and
+28
View File
@@ -52,6 +52,34 @@ This creates 10 build users. There can never be more concurrent builds
than the number of build users, so you may want to increase this if
you expect to do many builds at the same time.</para>
<para>On Mac OS X, you can create the required group and users by
running the following script:
<programlisting>
#! /bin/bash -e
dseditgroup -o create nixbld -q
gid=$(dscl . -read /Groups/nixbld | awk '($1 == "PrimaryGroupID:") {print $2 }')
echo "created nixbld group with gid $gid"
for i in $(seq 1 10); do
user=/Users/nixbld$i
uid="$((30000 + $i))"
dscl . create $user
dscl . create $user RealName "Nix build user $i"
dscl . create $user PrimaryGroupID "$gid"
dscl . create $user UserShell /usr/bin/false
dscl . create $user NFSHomeDirectory /var/empty
dscl . create $user UniqueID "$uid"
dseditgroup -o edit -a nixbld$i -t user nixbld
echo "created nixbld$i user with uid $uid"
done
</programlisting>
</para>
</simplesect>
+1 -1
View File
@@ -197,7 +197,7 @@ collection</emphasis> (Nixpkgs).</para>
<simplesect><title>Managing build environments</title>
<para>Nix is extremely useful for developers as it makes it easy to
automatically set up the the build environment for a package. Given a
automatically set up the build environment for a package. Given a
Nix expression that describes the dependencies of your package, the
command <command>nix-shell</command> will build or download those
dependencies if theyre not already in your Nix store, and then start
+1 -3
View File
@@ -4,10 +4,8 @@
version="5.0">
<info>
<title>Nix Package Manager Guide</title>
<edition>Version <xi:include href="version.txt" parse="text" /></edition>
<subtitle>Version <xi:include href="version.txt" parse="text" /></subtitle>
<author>
<personname>
+1 -1
View File
@@ -36,7 +36,7 @@ its much more convenient to use the Nixpkgs
<emphasis>channel</emphasis>, since it makes it easy to stay up to
date with new versions of Nixpkgs. (Channels are described in more
detail in <xref linkend="sec-channels"/>.) Nixpkgs is automatically
added to your list of “subscribed” channels when when you install
added to your list of “subscribed” channels when you install
Nix. If this is not the case for some reason, you can add it as
follows:
+1 -2
View File
@@ -120,8 +120,7 @@ can also see all available generations:
<screen>
$ nix-env --list-generations</screen></para>
<para>Actually, there is another level of indirection not shown in the
figure above. You generally wouldnt have
<para>You generally wouldnt have
<filename>/nix/var/nix/profiles/<replaceable>some-profile</replaceable>/bin</filename>
in your <envar>PATH</envar>. Rather, there is a symlink
<filename>~/.nix-profile</filename> that points to your current
@@ -12,6 +12,8 @@
</partintro>
-->
<xi:include href="rl-1.10.xml" />
<xi:include href="rl-1.9.xml" />
<xi:include href="rl-1.8.xml" />
<xi:include href="rl-1.7.xml" />
<xi:include href="rl-1.6.1.xml" />
+64
View File
@@ -0,0 +1,64 @@
<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="ssec-relnotes-1.10">
<title>Release 1.10 (September 3, 2015)</title>
<para>This is primarily a bug fix release. It also has a number of new
features:</para>
<itemizedlist>
<listitem>
<para>A number of builtin functions have been added to reduce
Nixpkgs/NixOS evaluation time and memory consumption:
<function>all</function>,
<function>any</function>,
<function>concatStringsSep</function>,
<function>foldl</function>,
<function>genList</function>,
<function>replaceStrings</function>,
<function>sort</function>.
</para>
</listitem>
<listitem>
<para>The garbage collector is more robust when the disk is full.</para>
</listitem>
<listitem>
<para>Nix supports a new API for building derivations that doesnt
require a <literal>.drv</literal> file to be present on disk; it
only requires an in-memory representation of the derivation. This
is used by the Hydra continuous build system to make remote builds
more efficient.</para>
</listitem>
<listitem>
<para>The function <literal>&lt;nix/fetchurl.nix></literal> now
uses a <emphasis>builtin</emphasis> builder (i.e. it doesnt
require starting an external process; the download is performed by
Nix itself). This ensures that derivation paths dont change when
Nix is upgraded, and obviates the need for ugly hacks to support
chroot execution.</para>
</listitem>
<listitem>
<para><option>--version -v</option> now prints some configuration
information, in particular what compile-time optional features are
enabled, and the paths of various directories.</para>
</listitem>
<listitem>
<para>Build users have their supplementary groups set correctly.</para>
</listitem>
</itemizedlist>
<para>This release has contributions from Eelco Dolstra, Guillaume
Maudoux, Iwan Aucamp, Jaka Hudoklin, Kirill Elagin, Ludovic Courtès,
Manolis Ragkousis, Nicolas B. Pierron and Shea Levy.</para>
</section>
+7
View File
@@ -113,4 +113,11 @@ $ nix-store -l $(which xterm)
</itemizedlist>
<para>This release has contributions from Adam Szkoda, Aristid
Breitkreuz, Bob van der Linden, Charles Strahan, darealshinji, Eelco
Dolstra, Gergely Risko, Joel Taylor, Ludovic Courtès, Marko Durkovic,
Mikey Ariel, Paul Colomiets, Ricardo M. Correia, Ricky Elrod, Robert
Helgesson, Rob Vermaas, Russell O'Connor, Shea Levy, Shell Turner,
Sönke Hahn, Steve Purcell, Vladimír Čunát and Wout Mertens.</para>
</section>
+216
View File
@@ -0,0 +1,216 @@
<section xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="ssec-relnotes-1.9">
<title>Release 1.9 (June 12, 2015)</title>
<para>In addition to the usual bug fixes, this release has the
following new features:</para>
<itemizedlist>
<listitem>
<para>Signed binary cache support. You can enable signature
checking by adding the following to <filename>nix.conf</filename>:
<programlisting>
signed-binary-caches = *
binary-cache-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
</programlisting>
This will prevent Nix from downloading any binary from the cache
that is not signed by one of the keys listed in
<option>binary-cache-public-keys</option>.</para>
<para>Signature checking is only supported if you built Nix with
the <literal>libsodium</literal> package.</para>
<para>Note that while Nix has had experimental support for signed
binary caches since version 1.7, this release changes the
signature format in a backwards-incompatible way.</para>
</listitem>
<listitem>
<para>Automatic downloading of Nix expression tarballs. In various
places, you can now specify the URL of a tarball containing Nix
expressions (such as Nixpkgs), which will be downloaded and
unpacked automatically. For example:</para>
<itemizedlist>
<listitem><para>In <command>nix-env</command>:
<screen>
$ nix-env -f https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz -iA firefox
</screen>
This installs Firefox from the latest tested and built revision
of the NixOS 14.12 channel.</para></listitem>
<listitem><para>In <command>nix-build</command> and
<command>nix-shell</command>:
<screen>
$ nix-build https://github.com/NixOS/nixpkgs/archive/master.tar.gz -A hello
</screen>
This builds GNU Hello from the latest revision of the Nixpkgs
master branch.</para></listitem>
<listitem><para>In the Nix search path (as specified via
<envar>NIX_PATH</envar> or <option>-I</option>). For example, to
start a shell containing the Pan package from a specific version
of Nixpkgs:
<screen>
$ nix-shell -p pan -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/8a3eea054838b55aca962c3fbde9c83c102b8bf2.tar.gz
</screen>
</para></listitem>
<listitem><para>In <command>nixos-rebuild</command> (on NixOS):
<screen>
$ nixos-rebuild test -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-unstable.tar.gz
</screen>
</para></listitem>
<listitem><para>In Nix expressions, via the new builtin function <function>fetchTarball</function>:
<programlisting>
with import (fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz) {}; …
</programlisting>
(This is not allowed in restricted mode.)</para></listitem>
</itemizedlist>
</listitem>
<listitem>
<para><command>nix-shell</command> improvements:</para>
<itemizedlist>
<listitem><para><command>nix-shell</command> now has a flag
<option>--run</option> to execute a command in the
<command>nix-shell</command> environment,
e.g. <literal>nix-shell --run make</literal>. This is like
the existing <option>--command</option> flag, except that it
uses a non-interactive shell (ensuring that hitting Ctrl-C wont
drop you into the child shell).</para></listitem>
<listitem><para><command>nix-shell</command> can now be used as
a <literal>#!</literal>-interpreter. This allows you to write
scripts that dynamically fetch their own dependencies. For
example, here is a Haskell script that, when invoked, first
downloads GHC and the Haskell packages on which it depends:
<programlisting>
#! /usr/bin/env nix-shell
#! nix-shell -i runghc -p haskellPackages.ghc haskellPackages.HTTP
import Network.HTTP
main = do
resp &lt;- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
body &lt;- getResponseBody resp
print (take 100 body)
</programlisting>
Of course, the dependencies are cached in the Nix store, so the
second invocation of this script will be much
faster.</para></listitem>
</itemizedlist>
</listitem>
<listitem>
<para>Chroot improvements:</para>
<itemizedlist>
<listitem><para>Chroot builds are now supported on Mac OS X
(using its sandbox mechanism).</para></listitem>
<listitem><para>If chroots are enabled, they are now used for
all derivations, including fixed-output derivations (such as
<function>fetchurl</function>). The latter do have network
access, but can no longer access the host filesystem. If you
need the old behaviour, you can set the option
<option>build-use-chroot</option> to
<literal>relaxed</literal>.</para></listitem>
<listitem><para>On Linux, if chroots are enabled, builds are
performed in a private PID namespace once again. (This
functionality was lost in Nix 1.8.)</para></listitem>
<listitem><para>Store paths listed in
<option>build-chroot-dirs</option> are now automatically
expanded to their closure. For instance, if you want
<filename>/nix/store/…-bash/bin/sh</filename> mounted in your
chroot as <filename>/bin/sh</filename>, you only need to say
<literal>build-chroot-dirs =
/bin/sh=/nix/store/…-bash/bin/sh</literal>; it is no longer
necessary to specify the dependencies of Bash.</para></listitem>
</itemizedlist>
</listitem>
<listitem><para>The new derivation attribute
<varname>passAsFile</varname> allows you to specify that the
contents of derivation attributes should be passed via files rather
than environment variables. This is useful if you need to pass very
long strings that exceed the size limit of the environment. The
Nixpkgs function <function>writeTextFile</function> uses
this.</para></listitem>
<listitem><para>You can now use <literal>~</literal> in Nix file
names to refer to your home directory, e.g. <literal>import
~/.nixpkgs/config.nix</literal>.</para></listitem>
<listitem><para>Nix has a new option <option>restrict-eval</option>
that allows limiting what paths the Nix evaluator has access to. By
passing <literal>--option restrict-eval true</literal> to Nix, the
evaluator will throw an exception if an attempt is made to access
any file outside of the Nix search path. This is primarily intended
for Hydra to ensure that a Hydra jobset only refers to its declared
inputs (and is therefore reproducible).</para></listitem>
<listitem><para><command>nix-env</command> now only creates a new
“generation” symlink in <filename>/nix/var/nix/profiles</filename>
if something actually changed.</para></listitem>
<listitem><para>The environment variable <envar>NIX_PAGER</envar>
can now be set to override <envar>PAGER</envar>. You can set it to
<literal>cat</literal> to disable paging for Nix commands
only.</para></listitem>
<listitem><para>Failing <literal>&lt;...></literal>
lookups now show position information.</para></listitem>
<listitem><para>Improved Boehm GC use: we disabled scanning for
interior pointers, which should reduce the “<literal>Repeated
allocation of very large block</literal>” warnings and associated
retention of memory.</para></listitem>
</itemizedlist>
<para>This release has contributions from aszlig, Benjamin Staffin,
Charles Strahan, Christian Theune, Daniel Hahler, Danylo Hlynskyi
Daniel Peebles, Dan Peebles, Domen Kožar, Eelco Dolstra, Harald van
Dijk, Hoang Xuan Phu, Jaka Hudoklin, Jeff Ramnani, j-keck, Linquize,
Luca Bruno, Michael Merickel, Oliver Dunkl, Rob Vermaas, Rok Garbas,
Shea Levy, Tobias Geerinckx-Rice and William A. Kennington III.</para>
</section>
+2 -1
View File
@@ -8,6 +8,7 @@ clean-files += Makefile.config
GLOBAL_CXXFLAGS += -I . -I src -I src/libutil -I src/libstore -I src/libmain -I src/libexpr
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh), $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh) src/nix-store/serve-protocol.hh, \
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
$(foreach i, $(call rwildcard, src/boost, *.hpp), $(eval $(call install-file-in, $(i), $(includedir)/nix/$(patsubst src/%/,%,$(dir $(i))), 0644)))
+23
View File
@@ -0,0 +1,23 @@
FROM busybox
RUN set -x \
&& wget -O- http://nixos.org/releases/nix/nix-1.9/nix-1.9-x86_64-linux.tar.bz2 | \
bzcat - | tar xf - \
&& echo "nixbld:x:30000:nixbld1,nixbld10,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9" >> /etc/group \
&& for i in $(seq 1 9); do echo "nixbld$i:x:3000$i:30000:::" >> /etc/passwd; done \
&& sed -i 's/\$HOME\/\.nix-profile\/etc\/ssl\/certs\/ca-bundle\.crt/\$HOME\/\.nix-profile\/etc\/ca-bundle\.crt/g' nix-1.9-x86_64-linux/install \
&& mkdir -m 0755 /nix && USER=root sh nix-1.9-x86_64-linux/install \
&& echo ". /root/.nix-profile/etc/profile.d/nix.sh" >> /etc/profile \
&& rm -r /nix-1.9-x86_64-linux
ONBUILD ENV \
ENV=/etc/profile \
PATH=/root/.nix-profile/bin:/root/.nix-profile/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
GIT_SSL_CAINFO=/root/.nix-profile/etc/ca-bundle.crt \
SSL_CERT_FILE=/root/.nix-profile/etc/ca-bundle.crt
ENV \
ENV=/etc/profile \
PATH=/root/.nix-profile/bin:/root/.nix-profile/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
GIT_SSL_CAINFO=/root/.nix-profile/etc/ca-bundle.crt \
SSL_CERT_FILE=/root/.nix-profile/etc/ca-bundle.crt
-37
View File
@@ -1,37 +0,0 @@
" Vim syntax file
" Language: nix
" Maintainer: Marc Weber <marco-oweber@gmx.de>
" Modify and commit if you feel that way
" Last Change: 2007 Dec
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
finish
endif
syn keyword nixKeyword let throw inherit import true false null with
syn keyword nixConditional if else then
syn keyword nixBrace ( ) { } =
syn keyword nixBuiltin __currentSystem __currentTime __isFunction __getEnv __trace __toPath __pathExists
\ __readFile __toXML __toFile __filterSource __attrNames __getAttr __hasAttr __isAttrs __listToAttrs __isList
\ __head __tail __add __sub __lessThan __substring __stringLength
syn match nixAttr "\w\+\ze\s*="
syn match nixFuncArg "\zs\w\+\ze\s*:"
syn region nixStringParam start=+\${+ end=+}+
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
syn match nixEndOfLineComment "#.*$"
syn region nixStringIndented start=+''+ skip=+'''\|''${\|"+ end=+''+ contains=nixStringParam
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
hi def link nixKeyword Keyword
hi def link nixConditional Conditional
hi def link nixBrace Special
hi def link nixString String
hi def link nixStringIndented String
hi def link nixBuiltin Special
hi def link nixStringParam Macro
hi def link nixMultiLineComment Comment
hi def link nixEndOfLineComment Comment
hi def link nixAttr Identifier
hi def link nixFuncArg Identifier
+20 -9
View File
@@ -1,5 +1,7 @@
package Nix::Config;
use MIME::Base64;
$version = "@PACKAGE_VERSION@";
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
@@ -19,24 +21,33 @@ $useBindings = "@perlbindings@" eq "yes";
%config = ();
%binaryCachePublicKeys = ();
$defaultPublicKeys = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=";
sub readConfig {
if (defined $ENV{'_NIX_OPTIONS'}) {
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
my ($n, $v) = split '=', $s, 2;
$config{$n} = $v;
}
return;
} else {
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;
}
close CONFIG;
}
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;
foreach my $s (split(/ /, $config{"binary-cache-public-keys"} // $defaultPublicKeys)) {
my ($keyName, $publicKey) = split ":", $s;
next unless defined $keyName && defined $publicKey;
$binaryCachePublicKeys{$keyName} = decode_base64($publicKey);
}
close CONFIG;
}
return 1;
-42
View File
@@ -1,42 +0,0 @@
package Nix::Crypto;
use strict;
use MIME::Base64;
use Nix::Store;
use Nix::Config;
use IPC::Open2;
our @ISA = qw(Exporter);
our @EXPORT = qw(signString isValidSignature);
sub signString {
my ($privateKeyFile, $s) = @_;
my $hash = hashString("sha256", 0, $s);
my ($from, $to);
my $pid = open2($from, $to, $Nix::Config::openssl, "rsautl", "-sign", "-inkey", $privateKeyFile);
print $to $hash;
close $to;
local $/ = undef;
my $sig = <$from>;
close $from;
waitpid($pid, 0);
die "$0: OpenSSL returned exit code $? while signing hash\n" if $? != 0;
my $sig64 = encode_base64($sig, "");
return $sig64;
}
sub isValidSignature {
my ($publicKeyFile, $sig64, $s) = @_;
my ($from, $to);
my $pid = open2($from, $to, $Nix::Config::openssl, "rsautl", "-verify", "-inkey", $publicKeyFile, "-pubin");
print $to decode_base64($sig64);
close $to;
my $decoded = <$from>;
close $from;
waitpid($pid, 0);
return 0 if $? != 0;
my $hash = hashString("sha256", 0, $s);
return $decoded eq $hash;
}
1;
+41 -18
View File
@@ -8,11 +8,12 @@ use Cwd;
use File::stat;
use File::Path;
use Fcntl ':flock';
use MIME::Base64;
use Nix::Config;
use Nix::Crypto;
use Nix::Store;
our @ISA = qw(Exporter);
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo fingerprintPath);
sub addNAR {
@@ -376,7 +377,6 @@ EOF
}
# Delete all old manifests downloaded from a given URL.
sub deleteOldManifests {
my ($url, $curUrlFile) = @_;
@@ -394,12 +394,29 @@ sub deleteOldManifests {
}
# Return a fingerprint of a store path to be used in binary cache
# signatures. It contains the store path, the base-32 SHA-256 hash of
# the contents of the path, and the references.
sub fingerprintPath {
my ($storePath, $narHash, $narSize, $references) = @_;
die if substr($storePath, 0, length($Nix::Config::storeDir)) ne $Nix::Config::storeDir;
die if substr($narHash, 0, 7) ne "sha256:";
# Convert hash from base-16 to base-32, if necessary.
$narHash = "sha256:" . convertHash("sha256", substr($narHash, 7), 1)
if length($narHash) == 71;
die if length($narHash) != 59;
foreach my $ref (@{$references}) {
die if substr($ref, 0, length($Nix::Config::storeDir)) ne $Nix::Config::storeDir;
}
return "1;" . $storePath . ";" . $narHash . ";" . $narSize . ";" . join(",", @{$references});
}
# Parse a NAR info file.
sub parseNARInfo {
my ($storePath, $content, $requireValidSig, $location) = @_;
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system, $sig);
my $signedData = "";
my $compression = "bzip2";
my @refs;
@@ -415,8 +432,7 @@ sub parseNARInfo {
elsif ($1 eq "References") { @refs = split / /, $2; }
elsif ($1 eq "Deriver") { $deriver = $2; }
elsif ($1 eq "System") { $system = $2; }
elsif ($1 eq "Signature") { $sig = $2; last; }
$signedData .= "$line\n";
elsif ($1 eq "Sig") { $sig = $2; }
}
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
@@ -434,30 +450,37 @@ sub parseNARInfo {
};
if ($requireValidSig) {
# FIXME: might be useful to support multiple signatures per .narinfo.
if (!defined $sig) {
warn "NAR info file $location lacks a signature; ignoring\n";
return undef;
}
my ($sigVersion, $keyName, $sig64) = split ";", $sig;
$sigVersion //= 0;
if ($sigVersion != 1) {
warn "NAR info file $location has unsupported version $sigVersion; ignoring\n";
return undef;
}
my ($keyName, $sig64) = split ":", $sig;
return undef unless defined $keyName && defined $sig64;
my $publicKeyFile = $Nix::Config::config{"binary-cache-public-key-$keyName"};
if (!defined $publicKeyFile) {
my $publicKey = $Nix::Config::binaryCachePublicKeys{$keyName};
if (!defined $publicKey) {
warn "NAR info file $location is signed by unknown key $keyName; ignoring\n";
return undef;
}
if (! -f $publicKeyFile) {
die "binary cache public key file $publicKeyFile does not exist\n";
my $fingerprint;
eval {
$fingerprint = fingerprintPath(
$storePath, $narHash, $narSize,
[ map { "$Nix::Config::storeDir/$_" } @refs ]);
};
if ($@) {
warn "cannot compute fingerprint of $location; ignoring\n";
return undef;
}
if (!isValidSignature($publicKeyFile, $sig64, $signedData)) {
warn "NAR info file $location has an invalid signature; ignoring\n";
if (!checkSignature($publicKey, decode_base64($sig64), $fingerprint)) {
warn "NAR info file $location has an incorrect signature; ignoring\n";
return undef;
}
$res->{signedBy} = $keyName;
}
+3 -1
View File
@@ -13,10 +13,12 @@ our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(
setVerbosity
isValidPath queryReferences queryPathInfo queryDeriver queryPathHash
queryPathFromHashPart
topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
hashPath hashFile hashString
hashPath hashFile hashString convertHash
signString checkSignature
addToStore makeFixedOutputPath
derivationFromPath
);
+67 -1
View File
@@ -11,6 +11,10 @@
#include <misc.hh>
#include <util.hh>
#if HAVE_SODIUM
#include <sodium.h>
#endif
using namespace nix;
@@ -44,6 +48,11 @@ void init()
doInit();
void setVerbosity(int level)
CODE:
verbosity = (Verbosity) level;
int isValidPath(char * path)
CODE:
try {
@@ -223,11 +232,68 @@ SV * hashString(char * algo, int base32, char * s)
}
SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
Hash h = parseHash16or32(parseHashType(algo), s);
string s = toBase32 ? printHash32(h) : printHash(h);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak(e.what());
}
SV * signString(SV * secretKey_, char * msg)
PPCODE:
try {
#if HAVE_SODIUM
STRLEN secretKeyLen;
unsigned char * secretKey = (unsigned char *) SvPV(secretKey_, secretKeyLen);
if (secretKeyLen != crypto_sign_SECRETKEYBYTES)
throw Error("secret key is not valid");
unsigned char sig[crypto_sign_BYTES];
unsigned long long sigLen;
crypto_sign_detached(sig, &sigLen, (unsigned char *) msg, strlen(msg), secretKey);
XPUSHs(sv_2mortal(newSVpv((char *) sig, sigLen)));
#else
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
#endif
} catch (Error & e) {
croak(e.what());
}
int checkSignature(SV * publicKey_, SV * sig_, char * msg)
CODE:
try {
#if HAVE_SODIUM
STRLEN publicKeyLen;
unsigned char * publicKey = (unsigned char *) SvPV(publicKey_, publicKeyLen);
if (publicKeyLen != crypto_sign_PUBLICKEYBYTES)
throw Error("public key is not valid");
STRLEN sigLen;
unsigned char * sig = (unsigned char *) SvPV(sig_, sigLen);
if (sigLen != crypto_sign_BYTES)
throw Error("signature is not valid");
RETVAL = crypto_sign_verify_detached(sig, (unsigned char *) msg, strlen(msg), publicKey) == 0;
#else
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
#endif
} catch (Error & e) {
croak(e.what());
}
OUTPUT:
RETVAL
SV * addToStore(char * srcPath, int recursive, char * algo)
PPCODE:
try {
doInit();
Path path = store->addToStore(srcPath, recursive, parseHashType(algo));
Path path = store->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo));
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
} catch (Error & e) {
croak(e.what());
+6 -5
View File
@@ -5,8 +5,7 @@ nix_perl_sources := \
$(d)/lib/Nix/SSH.pm \
$(d)/lib/Nix/CopyClosure.pm \
$(d)/lib/Nix/Config.pm.in \
$(d)/lib/Nix/Utils.pm \
$(d)/lib/Nix/Crypto.pm
$(d)/lib/Nix/Utils.pm
nix_perl_modules := $(nix_perl_sources:.in=)
@@ -23,16 +22,18 @@ ifeq ($(perlbindings), yes)
Store_SOURCES := $(Store_DIR)/Store.cc
Store_LIBS = libstore libutil
Store_CXXFLAGS = \
-I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
-D_FILE_OFFSET_BITS=64 -Wno-unused-variable -Wno-literal-suffix -Wno-reserved-user-defined-literal
Store_LIBS = libstore libutil
Store_LDFLAGS := $(SODIUM_LIBS)
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
Store_LDFLAGS = $(shell find ${archlib} -name ${libperl})
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
endif
Store_ALLOW_UNDEFINED = 1
+45 -33
View File
@@ -24,7 +24,7 @@ let
buildInputs =
[ curl bison flex perl libxml2 libxslt bzip2
tetex dblatex nukeReferences pkgconfig sqlite
tetex dblatex nukeReferences pkgconfig sqlite libsodium
docbook5 docbook5_xsl
] ++ lib.optional (!lib.inNixShell) git;
@@ -80,7 +80,9 @@ let
name = "nix";
src = tarball;
buildInputs = [ curl perl bzip2 openssl pkgconfig sqlite boehmgc ];
buildInputs =
[ curl perl bzip2 openssl pkgconfig sqlite boehmgc ]
++ lib.optional stdenv.isLinux libsodium;
configureFlags = ''
--disable-init-state
@@ -91,14 +93,6 @@ let
--sysconfdir=/etc
'';
# Provide a default value for the build-chroot-dirs setting
# that includes /bin/sh pointing to bash.
preHook = lib.optionalString stdenv.isLinux (
let sh = stdenv.shell; in
''
export DEFAULT_CHROOT_DIRS="/bin/sh=${sh} $(tr '\n' ' ' < ${writeReferencesToFile sh})"
'');
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d";
@@ -181,23 +175,33 @@ let
};
rpm_fedora20i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora20i386);
rpm_fedora20x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora20x86_64);
rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) [];
rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) [];
rpm_fedora19i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora19i386) [];
rpm_fedora19x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora19x86_64) [];
rpm_fedora20i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora20i386) [];
rpm_fedora20x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora20x86_64) [];
rpm_fedora21i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora21i386) [ "libsodium-devel" ];
rpm_fedora21x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora21x86_64) [ "libsodium-devel" ];
deb_debian7i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian7i386);
deb_debian7x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian7x86_64);
deb_debian7i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian7i386) [];
deb_debian7x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian7x86_64) [];
deb_debian8i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian8i386) [ "libsodium-dev" ];
deb_debian8x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian8x86_64) [ "libsodium-dev" ];
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386);
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64);
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386);
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64);
deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386);
deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64);
deb_ubuntu1404i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1404i386);
deb_ubuntu1404x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1404x86_64);
deb_ubuntu1410i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1410i386);
deb_ubuntu1410x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1410x86_64);
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) [];
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) [];
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) [];
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) [];
deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386) [];
deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64) [];
deb_ubuntu1404i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1404i386) [];
deb_ubuntu1404x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1404x86_64) [];
deb_ubuntu1410i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1410i386) [];
deb_ubuntu1410x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1410x86_64) [];
deb_ubuntu1504i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1504i386) [ "libsodium-dev" ];
deb_ubuntu1504x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1504x86_64) [ "libsodium-dev" ];
# System tests.
@@ -245,12 +249,14 @@ let
binaryTarball.x86_64-linux
deb_debian7i386
deb_debian7x86_64
deb_ubuntu1404i386
deb_ubuntu1404x86_64
deb_ubuntu1410i386
deb_ubuntu1410x86_64
deb_ubuntu1404i386 # LTS
deb_ubuntu1404x86_64 # LTS
deb_ubuntu1504i386
deb_ubuntu1504x86_64
rpm_fedora20i386
rpm_fedora20x86_64
rpm_fedora21i386
rpm_fedora21x86_64
tests.remoteBuilds
tests.nix-copy-closure
tests.binaryTarball
@@ -264,7 +270,7 @@ let
makeRPM_x86_64 = makeRPM "x86_64-linux";
makeRPM =
system: diskImageFun:
system: diskImageFun: extraPackages:
with import <nixpkgs> { inherit system; };
@@ -272,7 +278,9 @@ let
name = "nix-rpm";
src = jobs.tarball;
diskImage = (diskImageFun vmTools.diskImageFuns)
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" ]; };
{ extraPackages =
[ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" "libcurl-devel" ]
++ extraPackages; };
memSize = 1024;
meta.schedulingPriority = 50;
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
@@ -283,7 +291,7 @@ let
makeDeb_x86_64 = makeDeb "x86_64-linux";
makeDeb =
system: diskImageFun:
system: diskImageFun: extraPackages:
with import <nixpkgs> { inherit system; };
@@ -291,11 +299,15 @@ let
name = "nix-deb";
src = jobs.tarball;
diskImage = (diskImageFun vmTools.diskImageFuns)
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" ]; };
{ extraPackages =
[ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" "libcurl-dev" ]
++ extraPackages; };
memSize = 1024;
meta.schedulingPriority = 50;
configureFlags = "--sysconfdir=/etc";
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ];
debRequires =
[ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ]
++ lib.optionals (lib.elem "libsodium-dev" extraPackages) [ "libsodium13" ] ;
debMaintainer = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
doInstallCheck = true;
};
+2 -2
View File
@@ -82,7 +82,7 @@ if (defined $conf && -e $conf) {
push @machines,
{ hostName => $tokens[0]
, systemTypes => [ split(/,/, $tokens[1]) ]
, sshKeys => $tokens[2]
, sshKey => $tokens[2]
, maxJobs => int($tokens[3])
, speedFactor => 1.0 * (defined $tokens[4] ? int($tokens[4]) : 1)
, supportedFeatures => [ @supportedFeatures, @mandatoryFeatures ]
@@ -201,7 +201,7 @@ REQ: while (1) {
# Connect to the selected machine.
my @sshOpts = ("-i", $machine->{sshKeys});
my @sshOpts = ("-i", $machine->{sshKey});
$hostName = $machine->{hostName};
eval {
($from, $to) = connectToRemoteNix($hostName, \@sshOpts, "2>&4");
+2 -1
View File
@@ -9,6 +9,7 @@ my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
STDOUT->autoflush(1);
binmode STDERR, ":encoding(utf8)";
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
@@ -93,7 +94,7 @@ elsif ($ARGV[0] eq "--substitute") {
my ($store, $sourcePath) = findStorePath $storePath;
die unless $store;
print STDERR "\n*** Copying $storePath from $sourcePath\n\n";
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $destPath") == 0
system("@coreutils@/cp", "-rpd", $sourcePath, $destPath) == 0
or die "cannot copy $sourcePath to $storePath";
print "\n"; # no hash to verify
}
+8 -2
View File
@@ -47,11 +47,17 @@ $caBundle = "/etc/ssl/certs/ca-certificates.crt" if !$caBundle && -f "/etc/ssl/c
my $userName = getpwuid($<) || $ENV{"USER"} or die "cannot figure out user name";
my $userAgent = "Nix/$Nix::Config::version";
sub isTrue {
my ($x) = @_;
return $x eq "true" || $x eq "1";
}
# FIXME: this should be cache URLs required to have valid signatures,
# or "*" to require signatures on all binary caches.
# FIXME: should binary caches using a key in
# binary-cache-public-keys be trusted by default?
my $requireSignedBinaryCaches = ($Nix::Config::config{"signed-binary-caches"} // "0") ne "0";
my $curlConnectTimeout = int(
@@ -75,7 +81,7 @@ sub addRequest {
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
$curl->setopt(CURLOPT_SSL_VERIFYPEER, 0) unless isTrue($Nix::Config::config{"verify-https-binary-caches"} // "1");
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
$curl->setopt(CURLOPT_USERAGENT, $userAgent);
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
$curl->setopt(CURLOPT_FAILONERROR, 1);
$curl->setopt(CURLOPT_CONNECTTIMEOUT, $curlConnectTimeout);
@@ -555,7 +561,7 @@ sub downloadBinary {
die if $requireSignedBinaryCaches && !defined $info->{signedBy};
print STDERR "\n*** Downloading $url ", ($requireSignedBinaryCaches ? "(signed by $info->{signedBy}) " : ""), "to $storePath...\n";
checkURL $url;
if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
warn "download of $url failed" . ($! ? ": $!" : "") . "\n";
next;
}
+2 -2
View File
@@ -41,7 +41,7 @@ mkdir -p $dest/store
echo -n "copying Nix to $dest/store..." >&2
for i in $(cd $self/store && echo *); do
for i in $(cd $self/store >/dev/null && echo *); do
echo -n "." >&2
i_tmp="$dest/store/$i.$$"
if [ -e "$i_tmp" ]; then
@@ -75,7 +75,7 @@ fi
# Install an SSL certificate bundle.
if [ -z "$SSL_CERT_FILE" -o ! -f "$SSL_CERT_FILE" ]; then
$nix/bin/nix-env -i "$cacert"
export SSL_CERT_FILE="$HOME/.nix-profile/etc/ca-bundle.crt"
export SSL_CERT_FILE="$HOME/.nix-profile/etc/ssl/certs/ca-bundle.crt"
fi
# Subscribe the user to the Nixpkgs channel and fetch it.
-1
View File
@@ -1,7 +1,6 @@
nix_bin_scripts := \
$(d)/nix-build \
$(d)/nix-channel \
$(d)/nix-collect-garbage \
$(d)/nix-copy-closure \
$(d)/nix-generate-patches \
$(d)/nix-install-package \
+67 -4
View File
@@ -5,6 +5,8 @@ use strict;
use Nix::Config;
use Nix::Store;
use Nix::Utils;
use File::Basename;
use Cwd;
binmode STDERR, ":encoding(utf8)";
@@ -14,6 +16,7 @@ my $runEnv = $0 =~ /nix-shell$/;
my $pure = 0;
my $fromArgs = 0;
my $packages = 0;
my $interactive = 1;
my @instArgs = ();
my @buildArgs = ();
@@ -25,6 +28,9 @@ my @envExclude = ();
my $myName = $runEnv ? "nix-shell" : "nix-build";
my $inShebang = 0;
my $script;
my @savedArgs;
my $tmpDir = mkTempDir($myName);
@@ -35,6 +41,30 @@ my $drvLink = "$tmpDir/derivation";
$SIG{'INT'} = sub { exit 1 };
# Heuristic to see if we're invoked as a shebang script, namely, if we
# have a single argument, it's the name of an executable file, and it
# starts with "#!".
if ($runEnv && defined $ARGV[0] && $ARGV[0] !~ /nix-shell/) {
$script = $ARGV[0];
if (-f $script && -x $script) {
open SCRIPT, "<$script" or die "$0: cannot open $script: $!\n";
my $first = <SCRIPT>;
if ($first =~ /^\#\!/) {
$inShebang = 1;
@savedArgs = @ARGV; shift @savedArgs;
@ARGV = ();
while (<SCRIPT>) {
chomp;
if (/^\#\!\s*nix-shell (.*)$/) {
push @ARGV, split(/ /, $1);
}
}
}
close SCRIPT;
}
}
for (my $n = 0; $n < scalar @ARGV; $n++) {
my $arg = $ARGV[$n];
@@ -131,10 +161,11 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
$runEnv = 1;
}
elsif ($arg eq "--command") {
elsif ($arg eq "--command" || $arg eq "--run") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
$envCommand = "$ARGV[$n]\nexit $!";
$envCommand = "$ARGV[$n]\nexit";
$interactive = 0 if $arg eq "--run";
}
elsif ($arg eq "--exclude") {
@@ -155,15 +186,39 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
$packages = 1;
}
elsif ($inShebang && $arg eq "-i") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
my $interpreter = $ARGV[$n];
# Überhack to support Perl. Perl examines the shebang and
# executes it unless it contains the string "perl" or "indir",
# or (undocumented) argv[0] does not contain "perl". Exploit
# the latter by doing "exec -a".
my $execArgs = $interpreter =~ /perl/ ? "-a PERL" : "";
sub shellEscape {
my $s = $_;
$s =~ s/'/'\\''/g;
return "'" . $s . "'";
}
$envCommand = "exec $execArgs $interpreter $script ${\(join ' ', (map shellEscape, @savedArgs))}";
}
elsif (substr($arg, 0, 1) eq "-") {
push @buildArgs, $arg;
}
elsif ($arg eq "-Q" || $arg eq "--no-build-output") {
push @buildArgs, $arg;
push @instArgs, $arg;
}
else {
push @exprs, $arg;
}
}
die "$0: -p and -E are mutually exclusive\n" if $packages && $fromArgs;
if ($packages) {
push @instArgs, "--expr";
@exprs = (
@@ -182,6 +237,11 @@ foreach my $expr (@exprs) {
# Instantiate.
my @drvPaths;
if ($expr !~ /^\/.*\.drv$/) {
# If we're in a #! script, interpret filenames relative to the
# script.
$expr = dirname(Cwd::abs_path($script)) . "/" . $expr
if $inShebang && !$packages && $expr !~ /^\//;
# !!! would prefer the perl 5.8.0 pipe open feature here.
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
@@ -232,17 +292,20 @@ foreach my $expr (@exprs) {
($pure ? '' : 'p=$PATH; ' ) .
'dontAddDisableDepTrack=1; ' .
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
'if [ "$(type -t runHook)" = function ]; then runHook shellHook; fi; ' .
($pure ? '' : 'PATH=$PATH:$p; unset p; ') .
'set +e; ' .
'[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
'if [ "$(type -t runHook)" = function ]; then runHook shellHook; fi; ' .
'unset NIX_ENFORCE_PURITY; ' .
'unset NIX_INDENT_MAKE; ' .
'shopt -u nullglob; ' .
'unset TZ; ' . (defined $ENV{'TZ'} ? "export TZ='${ENV{'TZ'}}'; " : '') .
$envCommand);
$ENV{BASH_ENV} = $rcfile;
exec($ENV{NIX_BUILD_SHELL} // "bash", "--rcfile", $rcfile);
my @args = ($ENV{NIX_BUILD_SHELL} // "bash");
push @args, "--rcfile" if $interactive;
push @args, $rcfile;
exec @args;
die;
}
+54 -35
View File
@@ -6,6 +6,7 @@ use File::Basename;
use File::Path qw(mkpath);
use Nix::Config;
use Nix::Manifest;
use File::Temp qw(tempdir);
binmode STDERR, ":encoding(utf8)";
@@ -98,42 +99,14 @@ sub update {
my $url = $channels{$name};
my $origUrl = "$url/MANIFEST";
# Check if $url is a redirect. If so, follow it now to ensure
# consistency if the redirection is changed between
# downloading the manifest and the tarball.
my $headers = `$Nix::Config::curl --silent --head '$url'`;
# We want to download the url to a file to see if it's a tarball while also checking if we
# got redirected in the process, so that we can grab the various parts of a nix channel
# definition from a consistent location if the redirect changes mid-download.
my $tmpdir = tempdir( CLEANUP => 1 );
my $filename;
($url, $filename) = `cd $tmpdir && $Nix::Config::curl --silent --write-out '%{url_effective}\n%{filename_effective}' -L '$url' -O`;
chomp $url;
die "$0: unable to check $url\n" if $? != 0;
$headers =~ s/\r//g;
$url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m;
# 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.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\n" if $? != 0;
chomp $path;
# If the URL contains a version number, append it to the name
# attribute (so that "nix-env -q" on the channels profile
@@ -141,6 +114,52 @@ sub update {
my $cname = $name;
$cname .= $1 if basename($url) =~ /(-\d.*)$/;
my $path;
my $ret = -1;
if (-e "$tmpdir/$filename" && $filename =~ /\.tar\.(gz|bz2|xz)$/) {
# Get our temporary download into the store.
(my $hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url 'file://$tmpdir/$filename'`;
chomp $path;
# Try unpacking the expressions to see if they'll be valid for us to process later.
# Like anything in nix, this will cache the result so we don't do it again outside of the loop below.
$ret = system("$Nix::Config::binDir/nix-build --no-out-link -E 'import <nix/unpack-channel.nix> " .
"{ name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }'");
}
# The URL doesn't unpack directly, so let's try treating it like a full channel folder with files in it
my $extraAttrs = "";
if ($ret != 0) {
# Check if the channel advertises a binary cache.
my $binaryCacheURL = `$Nix::Config::curl --silent '$url'/binary-cache-url`;
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.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\n" if $? != 0;
chomp $path;
}
# Regardless of where it came from, add the expression representing this channel to accumulated expression
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; $extraAttrs }' ";
}
-65
View File
@@ -1,65 +0,0 @@
#! @perl@ -w @perlFlags@
use strict;
use Nix::Config;
my $profilesDir = "@localstatedir@/nix/profiles";
# Process the command line arguments.
my @args = ();
my $arg;
my $removeOld = 0;
my $gen;
my $dryRun = 0;
while ($arg = shift) {
if ($arg eq "--delete-old" || $arg eq "-d") {
$removeOld = 1;
$gen = "old";
} elsif ($arg eq "--delete-older-than") {
$removeOld = 1;
$gen = shift;
} elsif ($arg eq "--dry-run") {
$dryRun = 1;
} elsif ($arg eq "--help") {
exec "man nix-collect-garbage" or die;
} else {
push @args, $arg;
}
}
# If `-d' was specified, remove all old generations of all profiles.
# Of course, this makes rollbacks to before this point in time
# impossible.
sub removeOldGenerations;
sub removeOldGenerations {
my $dir = shift;
my $dh;
opendir $dh, $dir or die;
foreach my $name (sort (readdir $dh)) {
next if $name eq "." || $name eq "..";
$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", $gen, $dryRun ? "--dry-run" : ());
}
elsif (! -l $name && -d $name) {
removeOldGenerations $name;
}
}
closedir $dh or die;
}
removeOldGenerations $profilesDir if $removeOld;
# Run the actual garbage collector.
exec "$Nix::Config::binDir/nix-store", "--gc", @args unless $dryRun;
+5
View File
@@ -25,6 +25,7 @@ my $toMode = 1;
my $includeOutputs = 0;
my $dryRun = 0;
my $useSubstitutes = 0;
my $verbosity = 1;
# !!! Copied from nix-pack-closure, should put this in a module.
@@ -61,6 +62,10 @@ while (@ARGV) {
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
$useSubstitutes = 1;
}
elsif ($arg eq "-v") {
$verbosity++;
setVerbosity($verbosity);
}
elsif (!defined $sshHost) {
$sshHost = $arg;
}
+1 -1
View File
@@ -71,7 +71,7 @@ sub barf {
my $pkgFile = $source;
if ($fromURL) {
$pkgFile = "$tmpDir/tmp.nixpkg";
system("@curl@", "--silent", $source, "-o", $pkgFile) == 0
system("@curl@", "-L", "--silent", $source, "-o", $pkgFile) == 0
or barf "curl failed: $?";
}
+8 -4
View File
@@ -21,11 +21,15 @@ if [ -n "$HOME" ]; then
export NIX_PATH=${NIX_PATH:+$NIX_PATH:}nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs
# Set $SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Fedora, NixOS
export SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # Ubuntu, Debian
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
elif [ -e "$NIX_LINK/etc/ca-bundle.crt" ]; then # fall back to Nix profile
elif [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Old NixOS
export SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
export SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
elif [ -e "$NIX_LINK/etc/ssl/certs/ca-bundle.crt" ]; then # fall back to cacert in Nix profile
export SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
elif [ -e "$NIX_LINK/etc/ca-bundle.crt" ]; then # old cacert in Nix profile
export SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
fi
fi
+17 -12
View File
@@ -6,11 +6,11 @@ use File::Basename;
use File::Path qw(mkpath);
use File::stat;
use File::Copy;
use MIME::Base64;
use Nix::Config;
use Nix::Store;
use Nix::Manifest;
use Nix::Utils;
use Nix::Crypto;
binmode STDERR, ":encoding(utf8)";
@@ -27,9 +27,9 @@ my $writeManifest = 0;
my $manifestPath;
my $archivesURL;
my $link = 0;
my $privateKeyFile;
my $keyName;
my $secretKeyFile;
my @roots;
my @buildArgs;
for (my $n = 0; $n < scalar @ARGV; $n++) {
my $arg = $ARGV[$n];
@@ -61,14 +61,14 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
$archivesURL = $ARGV[$n];
} elsif ($arg eq "--link") {
$link = 1;
} elsif ($arg eq "--key") {
} elsif ($arg eq "--key-file") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
$privateKeyFile = $ARGV[$n];
} elsif ($arg eq "--key-name") {
$secretKeyFile = $ARGV[$n];
} elsif ($arg eq "--max-jobs" || $arg eq "-j") {
$n++;
die "$0: $arg requires an argument\n" unless $n < scalar @ARGV;
$keyName = $ARGV[$n];
push @buildArgs, ($arg, $ARGV[$n]);
} elsif (substr($arg, 0, 1) eq "-") {
die "$0: unknown flag $arg\n";
} else {
@@ -110,7 +110,7 @@ my %narFiles;
foreach my $storePath (@storePaths) {
my $pathHash = substr(basename($storePath), 0, 32);
my $narInfoFile = "$destDir/$pathHash.narinfo";
if (-e $narInfoFile) {
if (!$force && -e $narInfoFile) {
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile), 0, $narInfoFile) or die "cannot read $narInfoFile\n";
my $narFile = "$destDir/$narInfo->{url}";
if (-e $narFile) {
@@ -158,7 +158,7 @@ close NIX;
# 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 |")
my $pid = open(READ, "-|", "$Nix::Config::binDir/nix-build", $nixExpr, "-o", "$tmpDir/result", @buildArgs)
or die "cannot run nix-build";
while (<READ>) {
chomp;
@@ -257,9 +257,14 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
}
}
if (defined $privateKeyFile && defined $keyName) {
my $sig = signString($privateKeyFile, $info);
$info .= "Signature: 1;$keyName;$sig\n";
if (defined $secretKeyFile) {
my $s = readFile $secretKeyFile;
chomp $s;
my ($keyName, $secretKey) = split ":", $s;
die "invalid secret key file $secretKeyFile\n" unless defined $keyName && defined $secretKey;
my $fingerprint = fingerprintPath($storePath, $narHash, $narSize, $refs);
my $sig = encode_base64(signString(decode_base64($secretKey), $fingerprint), "");
$info .= "Sig: $keyName:$sig\n";
}
my $pathHash = substr(basename($storePath), 0, 32);
+13 -14
View File
@@ -43,8 +43,7 @@ static std::pair<FdSink, FdSource> connect(const string & conn)
static void substitute(std::pair<FdSink, FdSource> & pipes, Path storePath, Path destPath)
{
writeInt(cmdDumpStorePath, pipes.first);
writeString(storePath, pipes.first);
pipes.first << cmdDumpStorePath << storePath;
pipes.first.flush();
restorePath(destPath, pipes.second);
std::cout << std::endl;
@@ -58,17 +57,17 @@ static void query(std::pair<FdSink, FdSource> & pipes)
string cmd = tokenized.front();
tokenized.pop_front();
if (cmd == "have") {
writeInt(cmdQueryValidPaths, pipes.first);
writeInt(0, pipes.first); // don't lock
writeInt(0, pipes.first); // don't substitute
writeStrings(tokenized, pipes.first);
pipes.first
<< cmdQueryValidPaths
<< 0 // don't lock
<< 0 // don't substitute
<< tokenized;
pipes.first.flush();
PathSet paths = readStrings<PathSet>(pipes.second);
foreach (PathSet::iterator, i, paths)
std::cout << *i << std::endl;
for (auto & i : paths)
std::cout << i << std::endl;
} else if (cmd == "info") {
writeInt(cmdQueryPathInfos, pipes.first);
writeStrings(tokenized, pipes.first);
pipes.first << cmdQueryPathInfos << tokenized;
pipes.first.flush();
while (1) {
Path path = readString(pipes.second);
@@ -80,8 +79,8 @@ static void query(std::pair<FdSink, FdSource> & pipes)
std::cout << deriver << std::endl;
PathSet references = readStorePaths<PathSet>(pipes.second);
std::cout << references.size() << std::endl;
foreach (PathSet::iterator, i, references)
std::cout << *i << std::endl;
for (auto & i : references)
std::cout << i << std::endl;
std::cout << readLongLong(pipes.second) << std::endl;
std::cout << readLongLong(pipes.second) << std::endl;
}
@@ -116,13 +115,13 @@ int main(int argc, char * * argv)
std::pair<FdSink, FdSource> pipes = connect(host);
/* Exchange the greeting */
writeInt(SERVE_MAGIC_1, pipes.first);
pipes.first << SERVE_MAGIC_1;
pipes.first.flush();
unsigned int magic = readInt(pipes.second);
if (magic != SERVE_MAGIC_2)
throw Error("protocol mismatch");
readInt(pipes.second); // Server version, unused for now
writeInt(SERVE_PROTOCOL_VERSION, pipes.first);
pipes.first << SERVE_PROTOCOL_VERSION;
pipes.first.flush();
string arg = argv[1];
+5 -6
View File
@@ -42,11 +42,10 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
Value * v = &vIn;
foreach (Strings::iterator, i, tokens) {
for (auto & attr : tokens) {
/* Is *i an index (integer) or a normal attribute name? */
/* Is i an index (integer) or a normal attribute name? */
enum { apAttr, apIndex } apType = apAttr;
string attr = *i;
unsigned int attrIndex;
if (string2Int(attr, attrIndex)) apType = apIndex;
@@ -77,15 +76,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
else if (apType == apIndex) {
if (v->type != tList)
if (!v->isList())
throw TypeError(
format("the expression selected by the selection path %1% should be a list but is %2%")
% attrPath % showType(*v));
if (attrIndex >= v->list.length)
if (attrIndex >= v->listSize())
throw Error(format("list index %1% in selection path %2% is out of range") % attrIndex % attrPath);
v = v->list.elems[attrIndex];
v = v->listElems()[attrIndex];
}
}
+63
View File
@@ -0,0 +1,63 @@
#include "attr-set.hh"
#include "eval.hh"
#include <algorithm>
namespace nix {
static void * allocBytes(size_t n)
{
void * p;
#if HAVE_BOEHMGC
p = GC_malloc(n);
#else
p = malloc(n);
#endif
if (!p) throw std::bad_alloc();
return p;
}
/* Allocate a new array of attributes for an attribute set with a specific
capacity. The space is implicitly reserved after the Bindings
structure. */
Bindings * EvalState::allocBindings(Bindings::size_t capacity)
{
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings(capacity);
}
void EvalState::mkAttrs(Value & v, unsigned int capacity)
{
if (capacity == 0) {
v = vEmptySet;
return;
}
clearValue(v);
v.type = tAttrs;
v.attrs = allocBindings(capacity);
nrAttrsets++;
nrAttrsInAttrsets += capacity;
}
/* Create a new attribute named 'name' on an existing attribute set stored
in 'vAttrs' and return the newly allocated Value which is associated with
this attribute. */
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
{
Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v));
return v;
}
void Bindings::sort()
{
std::sort(begin(), end());
}
}
+82
View File
@@ -0,0 +1,82 @@
#pragma once
#include "nixexpr.hh"
#include "symbol-table.hh"
#include <algorithm>
namespace nix {
class EvalState;
struct Value;
/* Map one attribute name to its value. */
struct Attr
{
Symbol name;
Value * value;
Pos * pos;
Attr(Symbol name, Value * value, Pos * pos = &noPos)
: name(name), value(value), pos(pos) { };
Attr() : pos(&noPos) { };
bool operator < (const Attr & a) const
{
return name < a.name;
}
};
/* Bindings contains all the attributes of an attribute set. It is defined
by its size and its capacity, the capacity being the number of Attr
elements allocated after this structure, while the size corresponds to
the number of elements already inserted in this structure. */
class Bindings
{
public:
typedef uint32_t size_t;
private:
size_t size_, capacity_;
Attr attrs[0];
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
Bindings(const Bindings & bindings) = delete;
public:
size_t size() const { return size_; }
bool empty() const { return !size_; }
typedef Attr * iterator;
void push_back(const Attr & attr)
{
assert(size_ < capacity_);
attrs[size_++] = attr;
}
iterator find(const Symbol & name)
{
Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return i;
return end();
}
iterator begin() { return &attrs[0]; }
iterator end() { return &attrs[size_]; }
Attr & operator[](size_t pos)
{
return attrs[pos];
}
void sort();
size_t capacity() { return capacity_; }
friend class EvalState;
};
}
+5 -2
View File
@@ -1,5 +1,6 @@
#include "common-opts.hh"
#include "../libmain/shared.hh"
#include "shared.hh"
#include "download.hh"
#include "util.hh"
@@ -53,7 +54,9 @@ bool parseSearchPathArg(Strings::iterator & i,
Path lookupFileArg(EvalState & state, string s)
{
if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
if (isUri(s))
return downloadFileCached(s, true);
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
Path p = s.substr(1, s.size() - 2);
return state.findFile(p);
} else
+6 -6
View File
@@ -7,9 +7,9 @@
namespace nix {
LocalNoInlineNoReturn(void throwEvalError(const char * s))
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
{
throw EvalError(s);
throw EvalError(format(s) % pos);
}
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
@@ -24,7 +24,7 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const
}
void EvalState::forceValue(Value & v)
void EvalState::forceValue(Value & v, const Pos & pos)
{
if (v.type == tThunk) {
Env * env = v.thunk.env;
@@ -43,7 +43,7 @@ void EvalState::forceValue(Value & v)
else if (v.type == tApp)
callFunction(*v.app.left, *v.app.right, v, noPos);
else if (v.type == tBlackhole)
throwEvalError("infinite recursion encountered");
throwEvalError("infinite recursion encountered, at %1%", pos);
}
@@ -66,7 +66,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
inline void EvalState::forceList(Value & v)
{
forceValue(v);
if (v.type != tList)
if (!v.isList())
throwTypeError("value is %1% while a list was expected", v);
}
@@ -74,7 +74,7 @@ inline void EvalState::forceList(Value & v)
inline void EvalState::forceList(Value & v, const Pos & pos)
{
forceValue(v);
if (v.type != tList)
if (!v.isList())
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
}
+266 -186
View File
@@ -21,9 +21,6 @@
#else
#define GC_STRDUP strdup
#define GC_MALLOC malloc
#define NEW new
#endif
@@ -32,14 +29,36 @@
namespace nix {
void Bindings::sort()
static char * dupString(const char * s)
{
std::sort(begin(), end());
char * t;
#if HAVE_BOEHMGC
t = GC_strdup(s);
#else
t = strdup(s);
#endif
if (!t) throw std::bad_alloc();
return t;
}
static void * allocBytes(size_t n)
{
void * p;
#if HAVE_BOEHMGC
p = GC_malloc(n);
#else
p = malloc(n);
#endif
if (!p) throw std::bad_alloc();
return p;
}
static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
{
checkInterrupt();
if (active.find(&v) != active.end()) {
str << "<CYCLE>";
return;
@@ -73,8 +92,8 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
str << "{ ";
typedef std::map<string, Value *> Sorted;
Sorted sorted;
foreach (Bindings::iterator, i, *v.attrs)
sorted[i->name] = i->value;
for (auto & i : *v.attrs)
sorted[i.name] = i.value;
for (auto & i : sorted) {
str << i.first << " = ";
printValue(str, active, *i.second);
@@ -83,10 +102,12 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
str << "}";
break;
}
case tList:
case tList1:
case tList2:
case tListN:
str << "[ ";
for (unsigned int n = 0; n < v.list.length; ++n) {
printValue(str, active, *v.list.elems[n]);
for (unsigned int n = 0; n < v.listSize(); ++n) {
printValue(str, active, *v.listElems()[n]);
str << " ";
}
str << "]";
@@ -132,7 +153,7 @@ string showType(const Value & v)
case tPath: return "a path";
case tNull: return "null";
case tAttrs: return "a set";
case tList: return "a list";
case tList1: case tList2: case tListN: return "a list";
case tThunk: return "a thunk";
case tApp: return "a function application";
case tLambda: return "a function";
@@ -168,6 +189,61 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
}
static bool gcInitialised = false;
void initGC()
{
if (gcInitialised) return;
#if HAVE_BOEHMGC
/* Initialise the Boehm garbage collector. */
GC_set_all_interior_pointers(0);
GC_INIT();
GC_oom_fn = oomHandler;
/* Set the initial heap size to something fairly big (25% of
physical RAM, up to a maximum of 384 MiB) so that in most cases
we don't need to garbage collect at all. (Collection has a
fairly significant overhead.) The heap size can be overridden
through libgc's GC_INITIAL_HEAP_SIZE environment variable. We
should probably also provide a nix.conf setting for this. Note
that GC_expand_hp() causes a lot of virtual, but not physical
(resident) memory to be allocated. This might be a problem on
systems that don't overcommit. */
if (!getenv("GC_INITIAL_HEAP_SIZE")) {
size_t size = 32 * 1024 * 1024;
#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
size_t maxSize = 384 * 1024 * 1024;
long pageSize = sysconf(_SC_PAGESIZE);
long pages = sysconf(_SC_PHYS_PAGES);
if (pageSize != -1)
size = (pageSize * pages) / 4; // 25% of RAM
if (size > maxSize) size = maxSize;
#endif
debug(format("setting initial heap size to %1% bytes") % size);
GC_expand_hp(size);
}
#endif
gcInitialised = true;
}
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
static Strings parseNixPath(const string & in)
{
string marker = "\001//";
auto res = tokenizeString<Strings>(replaceStrings(in, "://", marker), ":");
for (auto & s : res)
s = replaceStrings(s, marker, "://");
return res;
}
EvalState::EvalState(const Strings & _searchPath)
: sWith(symbols.create("<with>"))
, sOutPath(symbols.create("outPath"))
@@ -185,61 +261,25 @@ EvalState::EvalState(const Strings & _searchPath)
, sLine(symbols.create("line"))
, sColumn(symbols.create("column"))
, sFunctor(symbols.create("__functor"))
, repair(false)
, baseEnv(allocEnv(128))
, staticBaseEnv(false, 0)
, baseEnvDispl(0)
{
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
nrAttrsets = nrAttrsInAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
nrListConcats = nrPrimOpCalls = nrFunctionCalls = 0;
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
#if HAVE_BOEHMGC
static bool gcInitialised = false;
if (!gcInitialised) {
restricted = settings.get("restrict-eval", false);
/* Initialise the Boehm garbage collector. This isn't
necessary on most platforms, but for portability we do it
anyway. */
GC_INIT();
GC_oom_fn = oomHandler;
/* Set the initial heap size to something fairly big (25% of
physical RAM, up to a maximum of 384 MiB) so that in most
cases we don't need to garbage collect at all. (Collection
has a fairly significant overhead.) The heap size can be
overridden through libgc's GC_INITIAL_HEAP_SIZE environment
variable. We should probably also provide a nix.conf
setting for this. Note that GC_expand_hp() causes a lot of
virtual, but not physical (resident) memory to be
allocated. This might be a problem on systems that don't
overcommit. */
if (!getenv("GC_INITIAL_HEAP_SIZE")) {
size_t size = 32 * 1024 * 1024;
#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
size_t maxSize = 384 * 1024 * 1024;
long pageSize = sysconf(_SC_PAGESIZE);
long pages = sysconf(_SC_PHYS_PAGES);
if (pageSize != -1)
size = (pageSize * pages) / 4; // 25% of RAM
if (size > maxSize) size = maxSize;
#endif
debug(format("setting initial heap size to %1% bytes") % size);
GC_expand_hp(size);
}
gcInitialised = true;
}
#endif
assert(gcInitialised);
/* Initialise the Nix expression search path. */
Strings paths = tokenizeString<Strings>(getEnv("NIX_PATH", ""), ":");
Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
for (auto & i : _searchPath) addToSearchPath(i, true);
for (auto & i : paths) addToSearchPath(i);
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
clearValue(vEmptySet);
vEmptySet.type = tAttrs;
vEmptySet.attrs = allocBindings(0);
createBaseEnv();
}
@@ -250,6 +290,33 @@ EvalState::~EvalState()
}
Path EvalState::checkSourcePath(const Path & path_)
{
if (!restricted) return path_;
/* Resolve symlinks. */
Path path = canonPath(path_, true);
for (auto & i : searchPath)
if (path == i.second || isInDir(path, i.second))
return path;
/* To support import-from-derivation, allow access to anything in
the store. FIXME: only allow access to paths that have been
constructed by this evaluation. */
if (isInStore(path)) return path;
#if 0
/* Hack to support the chroot dependencies of corepkgs (see
corepkgs/config.nix.in). */
if (path == settings.nixPrefix && isStorePath(settings.nixPrefix))
return path;
#endif
throw RestrictedPathError(format("access to path %1% is forbidden in restricted mode") % path_);
}
void EvalState::addConstant(const string & name, Value & v)
{
Value * v2 = allocValue();
@@ -291,11 +358,6 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
throw EvalError(format(s) % s2);
}
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
{
throw EvalError(format(s) % pos);
}
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const Pos & pos))
{
throw EvalError(format(s) % s2 % pos);
@@ -359,7 +421,7 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
void mkString(Value & v, const char * s)
{
mkStringNoCopy(v, GC_STRDUP(s));
mkStringNoCopy(v, dupString(s));
}
@@ -369,9 +431,9 @@ void mkString(Value & v, const string & s, const PathSet & context)
if (!context.empty()) {
unsigned int n = 0;
v.string.context = (const char * *)
GC_MALLOC((context.size() + 1) * sizeof(char *));
foreach (PathSet::const_iterator, i, context)
v.string.context[n++] = GC_STRDUP(i->c_str());
allocBytes((context.size() + 1) * sizeof(char *));
for (auto & i : context)
v.string.context[n++] = dupString(i.c_str());
v.string.context[n] = 0;
}
}
@@ -379,7 +441,7 @@ void mkString(Value & v, const string & s, const PathSet & context)
void mkPath(Value & v, const char * s)
{
mkPathNoCopy(v, GC_STRDUP(s));
mkPathNoCopy(v, dupString(s));
}
@@ -412,7 +474,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
Value * EvalState::allocValue()
{
nrValues++;
return (Value *) GC_MALLOC(sizeof(Value));
return (Value *) allocBytes(sizeof(Value));
}
@@ -422,10 +484,10 @@ Env & EvalState::allocEnv(unsigned int size)
nrEnvs++;
nrValuesInEnvs += size;
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
env->size = size;
/* Clear the values because maybeThunk() and lookupVar fromWith expects this. */
/* Clear the values because maybeThunk() and lookupVar fromWith expect this. */
for (unsigned i = 0; i < size; ++i)
env->values[i] = 0;
@@ -433,37 +495,19 @@ Env & EvalState::allocEnv(unsigned int size)
}
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
{
Value * v = allocValue();
vAttrs.attrs->push_back(Attr(name, v));
return v;
}
Bindings * EvalState::allocBindings(Bindings::size_t capacity)
{
return new (GC_MALLOC(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings(capacity);
}
void EvalState::mkList(Value & v, unsigned int length)
void EvalState::mkList(Value & v, unsigned int size)
{
clearValue(v);
v.type = tList;
v.list.length = length;
v.list.elems = length ? (Value * *) GC_MALLOC(length * sizeof(Value *)) : 0;
nrListElems += length;
}
void EvalState::mkAttrs(Value & v, unsigned int expected)
{
clearValue(v);
v.type = tAttrs;
v.attrs = allocBindings(expected);
nrAttrsets++;
nrAttrsInAttrsets += expected;
if (size == 1)
v.type = tList1;
else if (size == 2)
v.type = tList2;
else {
v.type = tListN;
v.bigList.size = size;
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
}
nrListElems += size;
}
@@ -555,7 +599,7 @@ void EvalState::evalFile(const Path & path, Value & v)
}
startNest(nest, lvlTalkative, format("evaluating file %1%") % path2);
Expr * e = parseExprFromFile(path2);
Expr * e = parseExprFromFile(checkSourcePath(path2));
try {
eval(e, v);
} catch (Error & e) {
@@ -651,15 +695,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
environment, while the inherited attributes are evaluated
in the original environment. */
unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs) {
for (auto & i : attrs) {
Value * vAttr;
if (hasOverrides && !i->second.inherited) {
if (hasOverrides && !i.second.inherited) {
vAttr = state.allocValue();
mkThunk(*vAttr, env2, i->second.e);
mkThunk(*vAttr, env2, i.second.e);
} else
vAttr = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
env2.values[displ++] = vAttr;
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
v.attrs->push_back(Attr(i.first, vAttr, &i.second.pos));
}
/* If the rec contains an attribute called `__overrides', then
@@ -690,25 +734,25 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
}
else
foreach (AttrDefs::iterator, i, attrs)
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
for (auto & i : attrs)
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), &i.second.pos));
/* Dynamic attrs apply *after* rec and __overrides. */
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
for (auto & i : dynamicAttrs) {
Value nameVal;
i->nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceValue(nameVal);
i.nameExpr->eval(state, *dynamicEnv, nameVal);
state.forceValue(nameVal, i.pos);
if (nameVal.type == tNull)
continue;
state.forceStringNoCtx(nameVal);
Symbol nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
throwEvalError("dynamic attribute %1% at %2% already defined at %3%", nameSym, i->pos, *j->pos);
throwEvalError("dynamic attribute %1% at %2% already defined at %3%", nameSym, i.pos, *j->pos);
i->valueExpr->setName(nameSym);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
v.attrs->push_back(Attr(nameSym, i->valueExpr->maybeThunk(state, *dynamicEnv), &i->pos));
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos));
v.attrs->sort(); // FIXME: inefficient
}
}
@@ -725,8 +769,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
while the inherited attributes are evaluated in the original
environment. */
unsigned int displ = 0;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
for (auto & i : attrs->attrs)
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
body->eval(state, env2, v);
}
@@ -735,19 +779,36 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
void ExprList::eval(EvalState & state, Env & env, Value & v)
{
state.mkList(v, elems.size());
for (unsigned int n = 0; n < v.list.length; ++n)
v.list.elems[n] = elems[n]->maybeThunk(state, env);
for (unsigned int n = 0; n < elems.size(); ++n)
v.listElems()[n] = elems[n]->maybeThunk(state, env);
}
void ExprVar::eval(EvalState & state, Env & env, Value & v)
{
Value * v2 = state.lookupVar(&env, *this, false);
state.forceValue(*v2);
state.forceValue(*v2, pos);
v = *v2;
}
static string showAttrPath(EvalState & state, Env & env, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
try {
out << getName(i, state, env);
} catch (Error & e) {
assert(!i.symbol.set());
out << "\"${" << *i.expr << "}\"";
}
}
return out.str();
}
unsigned long nrLookups = 0;
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
@@ -760,12 +821,12 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
try {
foreach (AttrPath::const_iterator, i, attrPath) {
for (auto & i : attrPath) {
nrLookups++;
Bindings::iterator j;
Symbol name = getName(*i, state, env);
Symbol name = getName(i, state, env);
if (def) {
state.forceValue(*vAttrs);
state.forceValue(*vAttrs, pos);
if (vAttrs->type != tAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
@@ -774,28 +835,20 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
}
} else {
state.forceAttrs(*vAttrs, pos);
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
AttrPath staticPath;
AttrPath::const_iterator j;
for (j = attrPath.begin(); j != i; ++j)
staticPath.push_back(AttrName(getName(*j, state, env)));
staticPath.push_back(AttrName(getName(*j, state, env)));
for (j = j + 1; j != attrPath.end(); ++j)
staticPath.push_back(*j);
throwEvalError("attribute %1% missing, at %2%", showAttrPath(staticPath), pos);
}
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError("attribute %1% missing, at %2%", name, pos);
}
vAttrs = j->value;
pos2 = j->pos;
if (state.countCalls && pos2) state.attrSelects[*pos2]++;
}
state.forceValue(*vAttrs);
state.forceValue(*vAttrs, ( pos2 != NULL ? *pos2 : this->pos ) );
} catch (Error & e) {
if (pos2 && pos2->file != state.sDerivationNix)
addErrorPrefix(e, "while evaluating the attribute %1% at %2%:\n",
showAttrPath(attrPath), *pos2);
showAttrPath(state, env, attrPath), *pos2);
throw;
}
@@ -810,10 +863,10 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
e->eval(state, env, vTmp);
foreach (AttrPath::const_iterator, i, attrPath) {
for (auto & i : attrPath) {
state.forceValue(*vAttrs);
Bindings::iterator j;
Symbol name = getName(*i, state, env);
Symbol name = getName(i, state, env);
if (vAttrs->type != tAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
@@ -892,10 +945,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
if (fun.type == tAttrs) {
auto found = fun.attrs->find(sFunctor);
if (found != fun.attrs->end()) {
forceValue(*found->value);
forceValue(*found->value, pos);
Value * v2 = allocValue();
callFunction(*found->value, fun, *v2, pos);
forceValue(*v2);
forceValue(*v2, pos);
return callFunction(*v2, arg, v, pos);
}
}
@@ -926,12 +979,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
there is no matching actual argument but the formal
argument has a default, use the default. */
unsigned int attrsUsed = 0;
foreach (Formals::Formals_::iterator, i, lambda.formals->formals) {
Bindings::iterator j = arg.attrs->find(i->name);
for (auto & i : lambda.formals->formals) {
Bindings::iterator j = arg.attrs->find(i.name);
if (j == arg.attrs->end()) {
if (!i->def) throwTypeError("%1% called without required argument %2%, at %3%",
lambda, i->name, pos);
env2.values[displ++] = i->def->maybeThunk(*this, env2);
if (!i.def) throwTypeError("%1% called without required argument %2%, at %3%",
lambda, i.name, pos);
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
attrsUsed++;
env2.values[displ++] = j->value;
@@ -943,9 +996,9 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) {
/* Nope, so show the first unexpected argument to the
user. */
foreach (Bindings::iterator, i, *arg.attrs)
if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end())
throwTypeError("%1% called with unexpected argument %2%, at %3%", lambda, i->name, pos);
for (auto & i : *arg.attrs)
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
throwTypeError("%1% called with unexpected argument %2%, at %3%", lambda, i.name, pos);
abort(); // can't happen
}
}
@@ -987,12 +1040,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
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);
for (auto & i : fun.lambda.fun->formals->formals) {
Bindings::iterator j = args.find(i.name);
if (j != args.end())
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);
else if (!i.def)
throwTypeError("cannot auto-call a function that has an argument without a default value (%1%)", i.name);
}
actualArgs->attrs->sort();
@@ -1120,20 +1173,21 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, co
unsigned int len = 0;
for (unsigned int n = 0; n < nrLists; ++n) {
forceList(*lists[n], pos);
unsigned int l = lists[n]->list.length;
unsigned int l = lists[n]->listSize();
len += l;
if (l) nonEmpty = lists[n];
}
if (nonEmpty && len == nonEmpty->list.length) {
if (nonEmpty && len == nonEmpty->listSize()) {
v = *nonEmpty;
return;
}
mkList(v, len);
auto out = v.listElems();
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 *));
unsigned int l = lists[n]->listSize();
memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
pos += l;
}
}
@@ -1148,9 +1202,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
bool first = !forceString;
ValueType firstType = tString;
foreach (vector<Expr *>::iterator, i, *es) {
for (auto & i : *es) {
Value vTmp;
(*i)->eval(state, env, vTmp);
i->eval(state, env, vTmp);
/* If the first element is a path, then the result will also
be a path, we don't copy anything (yet - that's done later,
@@ -1174,7 +1228,8 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == tPath) {
if (!context.empty())
throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
mkPath(v, s.str().c_str());
auto path = canonPath(s.str());
mkPath(v, path.c_str());
} else
mkString(v, s.str(), context);
}
@@ -1199,13 +1254,18 @@ void EvalState::forceValueDeep(Value & v)
forceValue(v);
if (v.type == tAttrs) {
foreach (Bindings::iterator, i, *v.attrs)
recurse(*i->value);
for (auto & i : *v.attrs)
try {
recurse(*i.value);
} catch (Error & e) {
addErrorPrefix(e, "while evaluating the attribute %1% at %2%:\n", i.name, *i.pos);
throw;
}
}
else if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n)
recurse(*v.list.elems[n]);
else if (v.isList()) {
for (unsigned int n = 0; n < v.listSize(); ++n)
recurse(*v.listElems()[n]);
}
};
@@ -1215,7 +1275,7 @@ void EvalState::forceValueDeep(Value & v)
NixInt EvalState::forceInt(Value & v, const Pos & pos)
{
forceValue(v);
forceValue(v, pos);
if (v.type != tInt)
throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
return v.integer;
@@ -1241,7 +1301,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
string EvalState::forceString(Value & v, const Pos & pos)
{
forceValue(v);
forceValue(v, pos);
if (v.type != tString) {
if (pos)
throwTypeError("value is %1% while a string was expected, at %2%", v, pos);
@@ -1329,14 +1389,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
if (v.type == tInt) return int2String(v.integer);
if (v.type == tNull) return "";
if (v.type == tList) {
if (v.isList()) {
string result;
for (unsigned int n = 0; n < v.list.length; ++n) {
result += coerceToString(pos, *v.list.elems[n],
for (unsigned int n = 0; n < v.listSize(); ++n) {
result += coerceToString(pos, *v.listElems()[n],
context, coerceMore, copyToStore);
if (n < v.list.length - 1
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0))
&& (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
result += " ";
}
return result;
@@ -1357,8 +1417,8 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
dstPath = srcToStore[path];
else {
dstPath = settings.readOnlyMode
? computeStorePathForPath(path).first
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
? computeStorePathForPath(checkSourcePath(path)).first
: store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
srcToStore[path] = dstPath;
printMsg(lvlChatty, format("copied source %1% -> %2%")
% path % dstPath);
@@ -1407,10 +1467,12 @@ bool EvalState::eqValues(Value & v1, Value & v2)
case tNull:
return true;
case tList:
if (v1.list.length != v2.list.length) return false;
for (unsigned int n = 0; n < v1.list.length; ++n)
if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
case tList1:
case tList2:
case tListN:
if (v1.listSize() != v2.listSize()) return false;
for (unsigned int n = 0; n < v1.listSize(); ++n)
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
return true;
case tAttrs: {
@@ -1466,6 +1528,7 @@ void EvalState::printStats()
printMsg(v, format(" time elapsed: %1%") % cpuTime);
printMsg(v, format(" size of a value: %1%") % sizeof(Value));
printMsg(v, format(" size of an attr: %1%") % sizeof(Attr));
printMsg(v, format(" environments allocated: %1% (%2% bytes)") % nrEnvs % bEnvs);
printMsg(v, format(" list elements: %1% (%2% bytes)") % nrListElems % bLists);
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
@@ -1482,31 +1545,38 @@ void EvalState::printStats()
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
printMsg(v, format(" total allocations: %1% bytes") % (bEnvs + bLists + bValues + bAttrsets));
#if HAVE_BOEHMGC
GC_word heapSize, totalBytes;
GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes);
printMsg(v, format(" current Boehm heap size: %1% bytes") % heapSize);
printMsg(v, format(" total Boehm heap allocations: %1% bytes") % totalBytes);
#endif
if (countCalls) {
v = lvlInfo;
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
PrimOpCalls_ primOpCalls_;
foreach (PrimOpCalls::iterator, i, primOpCalls)
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
for (auto & i : primOpCalls)
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i.second, i.first));
for (auto i = primOpCalls_.rbegin(); i != primOpCalls_.rend(); ++i)
printMsg(v, format("%1$10d %2%") % i->first % i->second);
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
FunctionCalls_ functionCalls_;
foreach (FunctionCalls::iterator, i, functionCalls)
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
for (auto & i : functionCalls)
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i.second, i.first));
for (auto i = functionCalls_.rbegin(); i != functionCalls_.rend(); ++i)
printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
typedef std::multimap<unsigned int, Pos> AttrSelects_;
AttrSelects_ attrSelects_;
foreach (AttrSelects::iterator, i, attrSelects)
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
for (auto & i : attrSelects)
attrSelects_.insert(std::pair<unsigned int, Pos>(i.second, i.first));
for (auto i = attrSelects_.rbegin(); i != attrSelects_.rend(); ++i)
printMsg(v, format("%1$10d %2%") % i->first % i->second);
}
@@ -1543,12 +1613,22 @@ size_t valueSize(Value & v)
sz += doString(v.path);
break;
case tAttrs:
for (auto & i : *v.attrs)
sz += doValue(*i.value);
if (seen.find(v.attrs) == seen.end()) {
seen.insert(v.attrs);
sz += sizeof(Bindings) + sizeof(Attr) * v.attrs->capacity();
for (auto & i : *v.attrs)
sz += doValue(*i.value);
}
break;
case tList:
for (unsigned int n = 0; n < v.list.length; ++n)
sz += doValue(*v.list.elems[n]);
case tList1:
case tList2:
case tListN:
if (seen.find(v.listElems()) == seen.end()) {
seen.insert(v.listElems());
sz += v.listSize() * sizeof(Value *);
for (unsigned int n = 0; n < v.listSize(); ++n)
sz += doValue(*v.listElems()[n]);
}
break;
case tThunk:
sz += doEnv(*v.thunk.env);
@@ -1580,7 +1660,7 @@ size_t valueSize(Value & v)
if (seen.find(&env) != seen.end()) return 0;
seen.insert(&env);
size_t sz = sizeof(Env);
size_t sz = sizeof(Env) + sizeof(Value *) * env.size;
for (unsigned int i = 0; i < env.size; ++i)
if (env.values[i])
+29 -78
View File
@@ -1,5 +1,6 @@
#pragma once
#include "attr-set.hh"
#include "value.hh"
#include "nixexpr.hh"
#include "symbol-table.hh"
@@ -18,68 +19,6 @@ namespace nix {
class EvalState;
struct Attr
{
Symbol name;
Value * value;
Pos * pos;
Attr(Symbol name, Value * value, Pos * pos = &noPos)
: name(name), value(value), pos(pos) { };
Attr() : pos(&noPos) { };
bool operator < (const Attr & a) const
{
return name < a.name;
}
};
class Bindings
{
public:
typedef uint32_t size_t;
private:
size_t size_, capacity;
Attr attrs[0];
Bindings(uint32_t capacity) : size_(0), capacity(capacity) { }
Bindings(const Bindings & bindings) = delete;
public:
size_t size() const { return size_; }
bool empty() const { return !size_; }
typedef Attr * iterator;
void push_back(const Attr & attr)
{
assert(size_ < capacity);
attrs[size_++] = attr;
}
iterator find(const Symbol & name)
{
Attr key(name, 0);
iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return i;
return end();
}
iterator begin() { return &attrs[0]; }
iterator end() { return &attrs[size_]; }
Attr & operator[](size_t pos)
{
return attrs[pos];
}
void sort();
friend class EvalState;
};
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
@@ -119,6 +58,10 @@ std::ostream & operator << (std::ostream & str, const Value & v);
typedef list<std::pair<string, Path> > SearchPath;
/* Initialise the Boehm GC, if applicable. */
void initGC();
class EvalState
{
public:
@@ -131,7 +74,13 @@ public:
/* If set, force copying files to the Nix store even if they
already exist there. */
bool repair;
bool repair = false;
/* If set, don't allow access to files outside of the Nix search
path or to environment variables. */
bool restricted;
Value vEmptySet;
private:
SrcToStore srcToStore;
@@ -153,6 +102,8 @@ public:
void addToSearchPath(const string & s, bool warn = false);
Path checkSourcePath(const Path & path);
/* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path);
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
@@ -169,7 +120,7 @@ public:
/* Look up a file in the search path. */
Path findFile(const string & path);
Path findFile(SearchPath & searchPath, const string & path);
Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos);
/* Evaluate an expression to normal form, storing the result in
value `v'. */
@@ -185,7 +136,7 @@ public:
of the evaluation of the thunk. If `v' is a delayed function
application, call the function and overwrite `v' with the
result. Otherwise, this is a no-op. */
inline void forceValue(Value & v);
inline void forceValue(Value & v, const Pos & pos = noPos);
/* Force a value, then recursively force list elements and
attributes. */
@@ -232,7 +183,7 @@ public:
private:
unsigned int baseEnvDispl;
unsigned int baseEnvDispl = 0;
void createBaseEnv();
@@ -278,7 +229,7 @@ public:
Bindings * allocBindings(Bindings::size_t capacity);
void mkList(Value & v, unsigned int length);
void mkAttrs(Value & v, unsigned int expected);
void mkAttrs(Value & v, unsigned int capacity);
void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, Pos * pos);
@@ -289,17 +240,17 @@ public:
private:
unsigned long nrEnvs;
unsigned long nrValuesInEnvs;
unsigned long nrValues;
unsigned long nrListElems;
unsigned long nrAttrsets;
unsigned long nrAttrsInAttrsets;
unsigned long nrOpUpdates;
unsigned long nrOpUpdateValuesCopied;
unsigned long nrListConcats;
unsigned long nrPrimOpCalls;
unsigned long nrFunctionCalls;
unsigned long nrEnvs = 0;
unsigned long nrValuesInEnvs = 0;
unsigned long nrValues = 0;
unsigned long nrListElems = 0;
unsigned long nrAttrsets = 0;
unsigned long nrAttrsInAttrsets = 0;
unsigned long nrOpUpdates = 0;
unsigned long nrOpUpdateValuesCopied = 0;
unsigned long nrListConcats = 0;
unsigned long nrPrimOpCalls = 0;
unsigned long nrFunctionCalls = 0;
bool countCalls;
+19 -19
View File
@@ -39,9 +39,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs()
state->forceList(*i->value, *i->pos);
/* For each output... */
for (unsigned int j = 0; j < i->value->list.length; ++j) {
for (unsigned int j = 0; j < i->value->listSize(); ++j) {
/* Evaluate the corresponding set. */
string name = state->forceStringNoCtx(*i->value->list.elems[j], *i->pos);
string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value);
@@ -85,8 +85,8 @@ StringSet DrvInfo::queryMetaNames()
{
StringSet res;
if (!getMeta()) return res;
foreach (Bindings::iterator, i, *meta)
res.insert(i->name);
for (auto & i : *meta)
res.insert(i.name);
return res;
}
@@ -94,16 +94,16 @@ StringSet DrvInfo::queryMetaNames()
bool DrvInfo::checkMeta(Value & v)
{
state->forceValue(v);
if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n)
if (!checkMeta(*v.list.elems[n])) return false;
if (v.isList()) {
for (unsigned int n = 0; n < v.listSize(); ++n)
if (!checkMeta(*v.listElems()[n])) return false;
return true;
}
else if (v.type == tAttrs) {
Bindings::iterator i = v.attrs->find(state->sOutPath);
if (i != v.attrs->end()) return false;
foreach (Bindings::iterator, i, *v.attrs)
if (!checkMeta(*i->value)) return false;
for (auto & i : *v.attrs)
if (!checkMeta(*i.value)) return false;
return true;
}
else return v.type == tInt || v.type == tBool || v.type == tString;
@@ -255,13 +255,13 @@ static void getDerivations(EvalState & state, Value & vIn,
precedence). */
typedef std::map<string, Symbol> SortedSymbols;
SortedSymbols attrs;
foreach (Bindings::iterator, i, *v.attrs)
attrs.insert(std::pair<string, Symbol>(i->name, i->name));
for (auto & i : *v.attrs)
attrs.insert(std::pair<string, Symbol>(i.name, i.name));
foreach (SortedSymbols::iterator, i, attrs) {
startNest(nest, lvlDebug, format("evaluating attribute %1%") % i->first);
string pathPrefix2 = addToPath(pathPrefix, i->first);
Value & v2(*v.attrs->find(i->second)->value);
for (auto & i : attrs) {
startNest(nest, lvlDebug, format("evaluating attribute %1%") % i.first);
string pathPrefix2 = addToPath(pathPrefix, i.first);
Value & v2(*v.attrs->find(i.second)->value);
if (combineChannels)
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
@@ -277,13 +277,13 @@ static void getDerivations(EvalState & state, Value & vIn,
}
}
else if (v.type == tList) {
for (unsigned int n = 0; n < v.list.length; ++n) {
else if (v.isList()) {
for (unsigned int n = 0; n < v.listSize(); ++n) {
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, ignoreAssertionFailures))
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
s++;
state.mkList(v, values.size());
for (size_t n = 0; n < values.size(); ++n)
v.list.elems[n] = values[n];
v.listElems()[n] = values[n];
}
else if (*s == '{') {
+22 -33
View File
@@ -1,6 +1,9 @@
%option reentrant bison-bridge bison-locations
%option noyywrap
%option never-interactive
%option stack
%option nodefault
%option nounput noyy_top_state
%x STRING
@@ -74,12 +77,16 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
#define YY_USER_INIT initLoc(yylloc)
#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng);
#define PUSH_STATE(state) yy_push_state(state, yyscanner)
#define POP_STATE() yy_pop_state(yyscanner)
%}
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-9]+
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
@@ -117,23 +124,24 @@ or { return OR_KW; }
return INT;
}
\$\{ { return DOLLAR_CURLY; }
\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
\{ { PUSH_STATE(INITIAL); return '{'; }
\} { POP_STATE(); return '}'; }
\" { BEGIN(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
/* !!! Not quite right: we want a follow restriction on
"$", it shouldn't be followed by a "{". Right now
"$\"" will be consumed as part of a string, rather
than a "$" followed by the string terminator.
Disallow "$\"" for now. */
\" { PUSH_STATE(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" |
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ {
/* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */
yylval->e = unescapeStr(data->symbols, yytext);
return STR;
}
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<STRING>\" { BEGIN(INITIAL); return '"'; }
<STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
<STRING>\" { POP_STATE(); return '"'; }
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->e = new ExprIndStr(yytext);
return IND_STR;
@@ -150,8 +158,8 @@ or { return OR_KW; }
yylval->e = unescapeStr(data->symbols, yytext + 2);
return IND_STR;
}
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
<IND_STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->e = new ExprIndStr("'");
return IND_STR;
@@ -159,6 +167,7 @@ or { return OR_KW; }
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
{PATH} { yylval->path = strdup(yytext); return PATH; }
{HPATH} { yylval->path = strdup(yytext); return HPATH; }
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
{URI} { yylval->uri = strdup(yytext); return URI; }
@@ -171,23 +180,3 @@ or { return OR_KW; }
%%
namespace nix {
/* Horrible, disgusting hack: allow the parser to set the scanner
start condition back to STRING. Necessary in interpolations like
"foo${expr}bar"; after the close brace we have to go back to the
STRING state. */
void backToString(yyscan_t scanner)
{
struct yyguts_t * yyg = (struct yyguts_t *) scanner;
BEGIN(STRING);
}
void backToIndString(yyscan_t scanner)
{
struct yyguts_t * yyg = (struct yyguts_t *) scanner;
BEGIN(IND_STRING);
}
}
+2 -2
View File
@@ -98,8 +98,8 @@ int compareVersions(const string & v1, const string & v2)
DrvNames drvNamesFromArgs(const Strings & opArgs)
{
DrvNames result;
foreach (Strings::const_iterator, i, opArgs)
result.push_back(DrvName(*i));
for (auto & i : opArgs)
result.push_back(DrvName(i));
return result;
}
+52 -55
View File
@@ -97,21 +97,21 @@ void ExprAttrs::show(std::ostream & str)
{
if (recursive) str << "rec ";
str << "{ ";
foreach (AttrDefs::iterator, i, attrs)
if (i->second.inherited)
str << "inherit " << i->first << " " << "; ";
for (auto & i : attrs)
if (i.second.inherited)
str << "inherit " << i.first << " " << "; ";
else
str << i->first << " = " << *i->second.e << "; ";
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs)
str << "\"${" << *i->nameExpr << "}\" = " << *i->valueExpr << "; ";
str << i.first << " = " << *i.second.e << "; ";
for (auto & i : dynamicAttrs)
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
str << "}";
}
void ExprList::show(std::ostream & str)
{
str << "[ ";
foreach (vector<Expr *>::iterator, i, elems)
str << "(" << **i << ") ";
for (auto & i : elems)
str << "(" << *i << ") ";
str << "]";
}
@@ -121,10 +121,10 @@ void ExprLambda::show(std::ostream & str)
if (matchAttrs) {
str << "{ ";
bool first = true;
foreach (Formals::Formals_::iterator, i, formals->formals) {
for (auto & i : formals->formals) {
if (first) first = false; else str << ", ";
str << i->name;
if (i->def) str << " ? " << *i->def;
str << i.name;
if (i.def) str << " ? " << *i.def;
}
if (formals->ellipsis) {
if (!first) str << ", ";
@@ -140,12 +140,12 @@ void ExprLambda::show(std::ostream & str)
void ExprLet::show(std::ostream & str)
{
str << "(let ";
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
if (i->second.inherited) {
str << "inherit " << i->first << "; ";
for (auto & i : attrs->attrs)
if (i.second.inherited) {
str << "inherit " << i.first << "; ";
}
else
str << i->first << " = " << *i->second.e << "; ";
str << i.first << " = " << *i.second.e << "; ";
str << "in " << *body << ")";
}
@@ -173,9 +173,9 @@ void ExprConcatStrings::show(std::ostream & str)
{
bool first = true;
str << "(";
foreach (vector<Expr *>::iterator, i, *es) {
for (auto & i : *es) {
if (first) first = false; else str << " + ";
str << **i;
str << *i;
}
str << ")";
}
@@ -191,7 +191,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
if (!pos)
str << "undefined position";
else
str << (format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%") % pos.file % pos.line % pos.column).str();
str << (format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%") % (string) pos.file % pos.line % pos.column).str();
return str;
}
@@ -200,15 +200,12 @@ string showAttrPath(const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
foreach (AttrPath::const_iterator, i, attrPath) {
if (!first)
out << '.';
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
if (i.symbol.set())
out << i.symbol;
else
first = false;
if (i->symbol.set())
out << i->symbol;
else
out << "\"${" << *i->expr << "}\"";
out << "\"${" << *i.expr << "}\"";
}
return out.str();
}
@@ -270,17 +267,17 @@ void ExprSelect::bindVars(const StaticEnv & env)
{
e->bindVars(env);
if (def) def->bindVars(env);
foreach (AttrPath::iterator, i, attrPath)
if (!i->symbol.set())
i->expr->bindVars(env);
for (auto & i : attrPath)
if (!i.symbol.set())
i.expr->bindVars(env);
}
void ExprOpHasAttr::bindVars(const StaticEnv & env)
{
e->bindVars(env);
foreach (AttrPath::iterator, i, attrPath)
if (!i->symbol.set())
i->expr->bindVars(env);
for (auto & i : attrPath)
if (!i.symbol.set())
i.expr->bindVars(env);
}
void ExprAttrs::bindVars(const StaticEnv & env)
@@ -292,27 +289,27 @@ void ExprAttrs::bindVars(const StaticEnv & env)
dynamicEnv = &newEnv;
unsigned int displ = 0;
foreach (AttrDefs::iterator, i, attrs)
newEnv.vars[i->first] = i->second.displ = displ++;
for (auto & i : attrs)
newEnv.vars[i.first] = i.second.displ = displ++;
foreach (AttrDefs::iterator, i, attrs)
i->second.e->bindVars(i->second.inherited ? env : newEnv);
for (auto & i : attrs)
i.second.e->bindVars(i.second.inherited ? env : newEnv);
}
else
foreach (AttrDefs::iterator, i, attrs)
i->second.e->bindVars(env);
for (auto & i : attrs)
i.second.e->bindVars(env);
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
i->nameExpr->bindVars(*dynamicEnv);
i->valueExpr->bindVars(*dynamicEnv);
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(*dynamicEnv);
i.valueExpr->bindVars(*dynamicEnv);
}
}
void ExprList::bindVars(const StaticEnv & env)
{
foreach (vector<Expr *>::iterator, i, elems)
(*i)->bindVars(env);
for (auto & i : elems)
i->bindVars(env);
}
void ExprLambda::bindVars(const StaticEnv & env)
@@ -324,11 +321,11 @@ void ExprLambda::bindVars(const StaticEnv & env)
if (!arg.empty()) newEnv.vars[arg] = displ++;
if (matchAttrs) {
foreach (Formals::Formals_::iterator, i, formals->formals)
newEnv.vars[i->name] = displ++;
for (auto & i : formals->formals)
newEnv.vars[i.name] = displ++;
foreach (Formals::Formals_::iterator, i, formals->formals)
if (i->def) i->def->bindVars(newEnv);
for (auto & i : formals->formals)
if (i.def) i.def->bindVars(newEnv);
}
body->bindVars(newEnv);
@@ -339,11 +336,11 @@ void ExprLet::bindVars(const StaticEnv & env)
StaticEnv newEnv(false, &env);
unsigned int displ = 0;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
newEnv.vars[i->first] = i->second.displ = displ++;
for (auto & i : attrs->attrs)
newEnv.vars[i.first] = i.second.displ = displ++;
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
i->second.e->bindVars(i->second.inherited ? env : newEnv);
for (auto & i : attrs->attrs)
i.second.e->bindVars(i.second.inherited ? env : newEnv);
body->bindVars(newEnv);
}
@@ -387,8 +384,8 @@ void ExprOpNot::bindVars(const StaticEnv & env)
void ExprConcatStrings::bindVars(const StaticEnv & env)
{
foreach (vector<Expr *>::iterator, i, *es)
(*i)->bindVars(env);
for (auto & i : *es)
i->bindVars(env);
}
void ExprPos::bindVars(const StaticEnv & env)
@@ -422,8 +419,8 @@ string ExprLambda::showNamePos() const
size_t SymbolTable::totalSize() const
{
size_t n = 0;
foreach (Symbols::const_iterator, i, symbols)
n += i->size();
for (auto & i : symbols)
n += i.size();
return n;
}
+1
View File
@@ -16,6 +16,7 @@ MakeError(ThrownError, AssertionError)
MakeError(Abort, EvalError)
MakeError(TypeError, EvalError)
MakeError(UndefinedVarError, Error)
MakeError(RestrictedPathError, Error)
/* Position objects. */
+37 -37
View File
@@ -136,8 +136,8 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
bool atStartOfLine = true; /* = seen only whitespace in the current line */
unsigned int minIndent = 1000000;
unsigned int curIndent = 0;
foreach (vector<Expr *>::iterator, i, es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
for (auto & i : es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
if (!e) {
/* Anti-quotations end the current start-of-line whitespace. */
if (atStartOfLine) {
@@ -216,10 +216,6 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
}
void backToString(yyscan_t scanner);
void backToIndString(yyscan_t scanner);
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
{
return Pos(data->path, loc.first_line, loc.first_column);
@@ -268,7 +264,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%token <id> ID ATTRPATH
%token <e> STR IND_STR
%token <n> INT
%token <path> PATH SPATH
%token <path> PATH HPATH SPATH
%token <uri> URI
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
%token DOLLAR_CURLY /* == ${ */
@@ -286,7 +282,6 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
%left '*' '/'
%right CONCAT
%nonassoc '?'
%nonassoc '~'
%nonassoc NEGATE
%%
@@ -376,6 +371,7 @@ expr_simple
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
}
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
| HPATH { $$ = new ExprPath(getEnv("HOME", "") + string{$1 + 1}); }
| SPATH {
string path($1 + 1, strlen($1) - 2);
$$ = new ExprApp(CUR_POS,
@@ -404,25 +400,18 @@ string_parts
string_parts_interpolated
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
| STR DOLLAR_CURLY expr '}'
{
backToString(scanner);
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
| DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
| STR DOLLAR_CURLY expr '}' {
$$ = new vector<Expr *>;
$$->push_back($1);
$$->push_back($3);
}
| DOLLAR_CURLY expr '}'
{
backToString(scanner);
$$ = new vector<Expr *>;
$$->push_back($2);
}
;
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
| { $$ = new vector<Expr *>; }
;
@@ -430,20 +419,20 @@ binds
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
| binds INHERIT attrs ';'
{ $$ = $1;
foreach (AttrPath::iterator, i, *$3) {
if ($$->attrs.find(i->symbol) != $$->attrs.end())
dupAttr(i->symbol, makeCurPos(@3, data), $$->attrs[i->symbol].pos);
for (auto & i : *$3) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
Pos pos = makeCurPos(@3, data);
$$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i->symbol), pos, true);
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true);
}
}
| binds INHERIT '(' expr ')' attrs ';'
{ $$ = $1;
/* !!! Should ensure sharing of the expression in $4. */
foreach (AttrPath::iterator, i, *$6) {
if ($$->attrs.find(i->symbol) != $$->attrs.end())
dupAttr(i->symbol, makeCurPos(@6, data), $$->attrs[i->symbol].pos);
$$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i->symbol), makeCurPos(@6, data));
for (auto & i : *$6) {
if ($$->attrs.find(i.symbol) != $$->attrs.end())
dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
}
}
| { $$ = new ExprAttrs; }
@@ -527,6 +516,8 @@ formal
#include <unistd.h>
#include <eval.hh>
#include <download.hh>
#include <store-api.hh>
namespace nix {
@@ -611,10 +602,14 @@ void EvalState::addToSearchPath(const string & s, bool warn)
path = string(s, pos + 1);
}
if (isUri(path))
path = downloadFileCached(path, true);
path = absPath(path);
if (pathExists(path)) {
debug(format("adding path %1% to the search path") % path);
searchPath.push_back(std::pair<string, Path>(prefix, path));
/* Resolve symlinks in the path to support restricted mode. */
searchPath.push_back(std::pair<string, Path>(prefix, canonPath(path, true)));
} else if (warn)
printMsg(lvlError, format("warning: Nix search path entry %1% does not exist, ignoring") % path);
}
@@ -626,22 +621,27 @@ Path EvalState::findFile(const string & path)
}
Path EvalState::findFile(SearchPath & searchPath, const string & path)
Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos)
{
foreach (SearchPath::iterator, i, searchPath) {
for (auto & i : searchPath) {
assert(!isUri(i.second));
Path res;
if (i->first.empty())
res = i->second + "/" + path;
if (i.first.empty())
res = i.second + "/" + path;
else {
if (path.compare(0, i->first.size(), i->first) != 0 ||
(path.size() > i->first.size() && path[i->first.size()] != '/'))
if (path.compare(0, i.first.size(), i.first) != 0 ||
(path.size() > i.first.size() && path[i.first.size()] != '/'))
continue;
res = i->second +
(path.size() == i->first.size() ? "" : "/" + string(path, i->first.size()));
res = i.second +
(path.size() == i.first.size() ? "" : "/" + string(path, i.first.size()));
}
if (pathExists(res)) return canonPath(res);
}
throw ThrownError(format("file %1% was not found in the Nix search path (add it using $NIX_PATH or -I)") % path);
format f = format(
"file %1% was not found in the Nix search path (add it using $NIX_PATH or -I)"
+ string(pos ? ", at %2%" : ""));
f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
throw ThrownError(f % path % pos);
}
+363 -120
View File
@@ -9,6 +9,7 @@
#include "json-to-value.hh"
#include "names.hh"
#include "eval-inline.hh"
#include "download.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -80,12 +81,16 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
% path % e.path % pos);
}
path = state.checkSourcePath(path);
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
Derivation drv = readDerivation(path);
Value & w = *state.allocValue();
state.mkAttrs(w, 2 + drv.outputs.size());
state.mkAttrs(w, 3 + drv.outputs.size());
Value * v2 = state.allocAttr(w, state.sDrvPath);
mkString(*v2, path, singleton<PathSet>("=" + path));
v2 = state.allocAttr(w, state.sName);
mkString(*v2, drv.env["name"]);
Value * outputsVal =
state.allocAttr(w, state.symbols.create("outputs"));
state.mkList(*outputsVal, drv.outputs.size());
@@ -95,12 +100,12 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
v2 = state.allocAttr(w, state.symbols.create(o.first));
mkString(*v2, o.second.path,
singleton<PathSet>("!" + o.first + "!" + path));
outputsVal->list.elems[outputs_index] = state.allocValue();
mkString(*(outputsVal->list.elems[outputs_index++]), o.first);
outputsVal->listElems()[outputs_index] = state.allocValue();
mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
}
w.attrs->sort();
Value fun;
state.evalFile(state.findFile("nix/imported-drv-to-derivation.nix"), fun);
state.evalFile(settings.nixDataDir + "/nix/corepkgs/imported-drv-to-derivation.nix", fun);
state.forceFunction(fun, pos);
mkApp(v, fun, w);
state.forceAttrs(v, pos);
@@ -133,7 +138,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
/* !!! Should we pass the Pos or the file name too? */
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
/* Load a ValueInitializer from a dso and return whatever it initializes */
/* Load a ValueInitializer from a DSO and return whatever it initializes */
static void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
@@ -146,6 +151,8 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
% path % e.path % pos);
}
path = state.checkSourcePath(path);
string sym = state.forceStringNoCtx(*args[1], pos);
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
@@ -181,13 +188,13 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
case tPath: t = "path"; break;
case tNull: t = "null"; break;
case tAttrs: t = "set"; break;
case tList: t = "list"; break;
case tList1: case tList2: case tListN: t = "list"; break;
case tLambda:
case tPrimOp:
case tPrimOpApp:
t = "lambda";
break;
case tExternal:
case tExternal:
t = args[0]->external->typeOf();
break;
default: abort();
@@ -277,8 +284,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceList(*startSet->value, pos);
ValueList workSet;
for (unsigned int n = 0; n < startSet->value->list.length; ++n)
workSet.push_back(startSet->value->list.elems[n]);
for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
workSet.push_back(startSet->value->listElems()[n]);
/* Get the operator. */
Bindings::iterator op =
@@ -316,17 +323,17 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
state.forceList(call, pos);
/* Add the values returned by the operator to the work set. */
for (unsigned int n = 0; n < call.list.length; ++n) {
state.forceValue(*call.list.elems[n]);
workSet.push_back(call.list.elems[n]);
for (unsigned int n = 0; n < call.listSize(); ++n) {
state.forceValue(*call.listElems()[n]);
workSet.push_back(call.listElems()[n]);
}
}
/* Create the result list. */
state.mkList(v, res.size());
unsigned int n = 0;
foreach (ValueList::iterator, i, res)
v.list.elems[n++] = *i;
for (auto & i : res)
v.listElems()[n++] = i;
}
@@ -380,7 +387,7 @@ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Val
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
string name = state.forceStringNoCtx(*args[0], pos);
mkString(v, getEnv(name));
mkString(v, state.restricted ? "" : getEnv(name));
}
@@ -472,24 +479,24 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
StringSet outputs;
outputs.insert("out");
foreach (Bindings::iterator, i, *args[0]->attrs) {
if (i->name == state.sIgnoreNulls) continue;
string key = i->name;
for (auto & 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;
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") {
state.forceList(*i->value, pos);
for (unsigned int n = 0; n < i->value->list.length; ++n) {
string s = state.coerceToString(posDrvName, *i->value->list.elems[n], context, true);
state.forceList(*i.value, pos);
for (unsigned int n = 0; n < i.value->listSize(); ++n) {
string s = state.coerceToString(posDrvName, *i.value->listElems()[n], context, true);
drv.args.push_back(s);
}
}
@@ -497,11 +504,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* All other attributes are passed to the builder through
the environment. */
else {
string s = state.coerceToString(posDrvName, *i->value, context, true);
string s = state.coerceToString(posDrvName, *i.value, context, true);
drv.env[key] = s;
if (key == "builder") drv.builder = s;
else if (i->name == state.sSystem) drv.platform = s;
else if (i->name == state.sName) {
else if (i.name == state.sSystem) drv.platform = s;
else if (i.name == state.sName) {
drvName = s;
printMsg(lvlVomit, format("derivation name is %1%") % drvName);
}
@@ -515,17 +522,17 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
else if (key == "outputs") {
Strings tmp = tokenizeString<Strings>(s);
outputs.clear();
foreach (Strings::iterator, j, tmp) {
if (outputs.find(*j) != outputs.end())
throw EvalError(format("duplicate derivation output %1%, at %2%") % *j % posDrvName);
/* !!! Check whether *j is a valid attribute
for (auto & j : tmp) {
if (outputs.find(j) != outputs.end())
throw EvalError(format("duplicate derivation output %1%, at %2%") % j % posDrvName);
/* !!! Check whether j is a valid attribute
name. */
/* Derivations cannot be named drv, because
then we'd have an attribute drvPath in
the resulting set. */
if (*j == "drv")
if (j == "drv")
throw EvalError(format("invalid derivation output name drv, at %1%") % posDrvName);
outputs.insert(*j);
outputs.insert(j);
}
if (outputs.empty())
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
@@ -542,8 +549,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* 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;
for (auto & path : context) {
/* Paths marked with `=' denote that the path of a derivation
is explicitly passed to the builder. Since that allows the
@@ -555,10 +561,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (path.at(0) == '=') {
/* !!! This doesn't work if readOnlyMode is set. */
PathSet refs; computeFSClosure(*store, string(path, 1), refs);
foreach (PathSet::iterator, j, refs) {
drv.inputSrcs.insert(*j);
if (isDerivation(*j))
drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j);
for (auto & j : refs) {
drv.inputSrcs.insert(j);
if (isDerivation(j))
drv.inputDrvs[j] = store->queryDerivationOutputNames(j);
}
}
@@ -617,20 +623,20 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
are empty, and the corresponding environment variables have
an empty value. This ensures that changes in the set of
output names do get reflected in the hash. */
foreach (StringSet::iterator, i, outputs) {
drv.env[*i] = "";
drv.outputs[*i] = DerivationOutput("", "", "");
for (auto & i : outputs) {
drv.env[i] = "";
drv.outputs[i] = DerivationOutput("", "", "");
}
/* 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);
drv.env[i->first] = outPath;
i->second.path = outPath;
for (auto & i : drv.outputs)
if (i.second.path == "") {
Path outPath = makeOutputPath(i.first, h, drvName);
drv.env[i.first] = outPath;
i.second.path = outPath;
}
}
@@ -647,9 +653,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
state.mkAttrs(v, 1 + drv.outputs.size());
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
foreach (DerivationOutputs::iterator, i, drv.outputs) {
mkString(*state.allocAttr(v, state.symbols.create(i->first)),
i->second.path, singleton<PathSet>("!" + i->first + "!" + drvPath));
for (auto & i : drv.outputs) {
mkString(*state.allocAttr(v, state.symbols.create(i.first)),
i.second.path, singleton<PathSet>("!" + i.first + "!" + drvPath));
}
v.attrs->sort();
}
@@ -680,7 +686,7 @@ static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Valu
static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
Path path = state.coerceToPath(pos, *args[0], context);
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context));
/* Resolve symlinks in path, unless path itself is a symlink
directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */
@@ -701,7 +707,15 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
Path path = state.coerceToPath(pos, *args[0], context);
if (!context.empty())
throw EvalError(format("string %1% cannot refer to other paths, at %2%") % path % pos);
mkBool(v, pathExists(path));
try {
mkBool(v, pathExists(state.checkSourcePath(path)));
} catch (SysError & e) {
/* Don't give away info from errors while canonicalising
path in restricted mode. */
mkBool(v, false);
} catch (RestrictedPathError & e) {
mkBool(v, false);
}
}
@@ -710,7 +724,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
mkString(v, baseNameOf(state.coerceToString(pos, *args[0], context)), context);
mkString(v, baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context);
}
@@ -736,7 +750,10 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
throw EvalError(format("cannot read %1%, since path %2% is not valid, at %3%")
% path % e.path % pos);
}
mkString(v, readFile(path).c_str());
string s = readFile(state.checkSourcePath(path));
if (s.find((char) 0) != string::npos)
throw Error(format("the contents of the file %1% cannot be represented as a Nix string") % path);
mkString(v, s.c_str());
}
@@ -749,8 +766,8 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
SearchPath searchPath;
PathSet context;
for (unsigned int n = 0; n < args[0]->list.length; ++n) {
Value & v2(*args[0]->list.elems[n]);
for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
Value & v2(*args[0]->listElems()[n]);
state.forceAttrs(v2, pos);
string prefix;
@@ -763,7 +780,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
throw EvalError(format("attribute path missing, at %1%") % pos);
string path = state.coerceToPath(pos, *i->value, context);
searchPath.push_back(std::pair<string, Path>(prefix, path));
searchPath.push_back(std::pair<string, Path>(prefix, state.checkSourcePath(path)));
}
string path = state.forceStringNoCtx(*args[1], pos);
@@ -775,7 +792,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
% path % e.path % pos);
}
mkPath(v, state.findFile(searchPath, path).c_str());
mkPath(v, state.findFile(searchPath, path, pos).c_str());
}
/* Read a directory (without . or ..) */
@@ -790,13 +807,13 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
% path % e.path % pos);
}
DirEntries entries = readDirectory(path);
DirEntries entries = readDirectory(state.checkSourcePath(path));
state.mkAttrs(v, entries.size());
for (auto & ent : entries) {
Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name));
if (ent.type == DT_UNKNOWN)
ent.type = getFileType(path);
ent.type = getFileType(path + "/" + ent.name);
mkStringNoCopy(*ent_val,
ent.type == DT_REG ? "regular" :
ent.type == DT_DIR ? "directory" :
@@ -855,11 +872,14 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
PathSet refs;
foreach (PathSet::iterator, i, context) {
Path path = *i;
for (auto path : context) {
if (path.at(0) == '=') path = string(path, 1);
if (isDerivation(path))
throw EvalError(format("in toFile: the file %1% cannot refer to derivation outputs, at %2%") % name % pos);
if (isDerivation(path)) {
/* See prim_unsafeDiscardOutputDependency. */
if (path.at(0) != '~')
throw EvalError(format("in toFile: the file %1% cannot refer to derivation outputs, at %2%") % name % pos);
path = string(path, 1);
}
refs.insert(path);
}
@@ -927,9 +947,11 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
FilterFromExpr filter(state, *args[0]);
path = state.checkSourcePath(path);
Path dstPath = settings.readOnlyMode
? computeStorePathForPath(path, true, htSHA256, filter).first
: store->addToStore(path, true, htSHA256, filter, state.repair);
: store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
mkString(v, dstPath, singleton<PathSet>(dstPath));
}
@@ -950,9 +972,9 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
unsigned int n = 0;
for (auto & i : *args[0]->attrs)
mkString(*(v.list.elems[n++] = state.allocValue()), i.name);
mkString(*(v.listElems()[n++] = state.allocValue()), i.name);
std::sort(v.list.elems, v.list.elems + n,
std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
}
@@ -967,13 +989,13 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
unsigned int n = 0;
for (auto & i : *args[0]->attrs)
v.list.elems[n++] = (Value *) &i;
v.listElems()[n++] = (Value *) &i;
std::sort(v.list.elems, v.list.elems + n,
std::sort(v.listElems(), v.listElems() + n,
[](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
for (unsigned int i = 0; i < n; ++i)
v.list.elems[i] = ((Attr *) v.list.elems[i])->value;
v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
}
@@ -1030,18 +1052,18 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
/* Get the attribute names to be removed. */
std::set<Symbol> names;
for (unsigned int i = 0; i < args[1]->list.length; ++i) {
state.forceStringNoCtx(*args[1]->list.elems[i], pos);
names.insert(state.symbols.create(args[1]->list.elems[i]->string.s));
for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
state.forceStringNoCtx(*args[1]->listElems()[i], pos);
names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
}
/* Copy all attributes not in that set. Note that we don't need
to sort v.attrs because it's a subset of an already sorted
vector. */
state.mkAttrs(v, args[0]->attrs->size());
foreach (Bindings::iterator, i, *args[0]->attrs) {
if (names.find(i->name) == names.end())
v.attrs->push_back(*i);
for (auto & i : *args[0]->attrs) {
if (names.find(i.name) == names.end())
v.attrs->push_back(i);
}
}
@@ -1055,12 +1077,12 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
{
state.forceList(*args[0], pos);
state.mkAttrs(v, args[0]->list.length);
state.mkAttrs(v, args[0]->listSize());
std::set<Symbol> seen;
for (unsigned int i = 0; i < args[0]->list.length; ++i) {
Value & v2(*args[0]->list.elems[i]);
for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
Value & v2(*args[0]->listElems()[i]);
state.forceAttrs(v2, pos);
Bindings::iterator j = v2.attrs->find(state.sName);
@@ -1093,8 +1115,8 @@ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * ar
state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
foreach (Bindings::iterator, i, *args[0]->attrs) {
Bindings::iterator j = args[1]->attrs->find(i->name);
for (auto & i : *args[0]->attrs) {
Bindings::iterator j = args[1]->attrs->find(i.name);
if (j != args[1]->attrs->end())
v.attrs->push_back(*j);
}
@@ -1113,11 +1135,11 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
state.forceList(*args[1], pos);
Value * res[args[1]->list.length];
Value * res[args[1]->listSize()];
unsigned int found = 0;
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
Value & v2(*args[1]->list.elems[n]);
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value & v2(*args[1]->listElems()[n]);
state.forceAttrs(v2, pos);
Bindings::iterator i = v2.attrs->find(attrName);
if (i != v2.attrs->end())
@@ -1126,7 +1148,7 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
state.mkList(v, found);
for (unsigned int n = 0; n < found; ++n)
v.list.elems[n] = res[n];
v.listElems()[n] = res[n];
}
@@ -1155,9 +1177,9 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
}
state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
for (auto & i : args[0]->lambda.fun->formals->formals)
// !!! should optimise booleans (allocate only once)
mkBool(*state.allocAttr(v, i->name), i->def);
mkBool(*state.allocAttr(v, i.name), i.def);
v.attrs->sort();
}
@@ -1171,17 +1193,17 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceValue(*args[0]);
mkBool(v, args[0]->type == tList);
mkBool(v, args[0]->isList());
}
static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
{
state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.list.length)
if (n < 0 || (unsigned int) n >= list.listSize())
throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
state.forceValue(*list.list.elems[n]);
v = *list.list.elems[n];
state.forceValue(*list.listElems()[n]);
v = *list.listElems()[n];
}
@@ -1199,17 +1221,17 @@ static void prim_head(EvalState & state, const Pos & pos, Value * * args, Value
}
/* Return a list consisting of everything but the the first element of
/* Return a list consisting of everything but the first element of
a list. Warning: this function takes O(n) time, so you probably
don't want to use it! */
static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
if (args[0]->list.length == 0)
if (args[0]->listSize() == 0)
throw Error(format("tail called on an empty list, at %1%") % pos);
state.mkList(v, args[0]->list.length - 1);
for (unsigned int n = 0; n < v.list.length; ++n)
v.list.elems[n] = args[0]->list.elems[n + 1];
state.mkList(v, args[0]->listSize() - 1);
for (unsigned int n = 0; n < v.listSize(); ++n)
v.listElems()[n] = args[0]->listElems()[n + 1];
}
@@ -1219,11 +1241,11 @@ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
state.mkList(v, args[1]->list.length);
state.mkList(v, args[1]->listSize());
for (unsigned int n = 0; n < v.list.length; ++n)
mkApp(*(v.list.elems[n] = state.allocValue()),
*args[0], *args[1]->list.elems[n]);
for (unsigned int n = 0; n < v.listSize(); ++n)
mkApp(*(v.listElems()[n] = state.allocValue()),
*args[0], *args[1]->listElems()[n]);
}
@@ -1236,15 +1258,15 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
state.forceList(*args[1], pos);
// FIXME: putting this on the stack is risky.
Value * vs[args[1]->list.length];
Value * vs[args[1]->listSize()];
unsigned int k = 0;
bool same = true;
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value res;
state.callFunction(*args[0], *args[1]->list.elems[n], res, noPos);
state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
if (state.forceBool(res))
vs[k++] = args[1]->list.elems[n];
vs[k++] = args[1]->listElems()[n];
else
same = false;
}
@@ -1253,7 +1275,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
v = *args[1];
else {
state.mkList(v, k);
for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n];
for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n];
}
}
@@ -1263,8 +1285,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
{
bool res = false;
state.forceList(*args[1], pos);
for (unsigned int n = 0; n < args[1]->list.length; ++n)
if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
res = true;
break;
}
@@ -1276,7 +1298,7 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
state.concatLists(v, args[0]->list.length, args[0]->list.elems, pos);
state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos);
}
@@ -1284,7 +1306,114 @@ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args,
static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
mkInt(v, args[0]->list.length);
mkInt(v, args[0]->listSize());
}
/* Reduce a list by applying a binary operator, from left to
right. The operator is applied strictly. */
static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[2], pos);
Value * vCur = args[1];
if (args[2]->listSize())
for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
Value vTmp;
state.callFunction(*args[0], *vCur, vTmp, pos);
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos);
}
else
v = *vCur;
state.forceValue(v);
}
static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
Value vTmp;
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
bool res = state.forceBool(vTmp);
if (res == any) {
mkBool(v, any);
return;
}
}
mkBool(v, !any);
}
static void prim_any(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
anyOrAll(true, state, pos, args, v);
}
static void prim_all(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
anyOrAll(false, state, pos, args, v);
}
static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
auto len = state.forceInt(*args[1], pos);
if (len < 0)
throw EvalError(format("cannot create list of size %1%, at %2%") % len % pos);
state.mkList(v, len);
for (unsigned int n = 0; n < len; ++n) {
Value * arg = state.allocValue();
mkInt(*arg, n);
mkApp(*(v.listElems()[n] = state.allocValue()), *args[0], *arg);
}
}
static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Value & v);
static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos);
state.forceList(*args[1], pos);
auto len = args[1]->listSize();
state.mkList(v, len);
for (unsigned int n = 0; n < len; ++n) {
state.forceValue(*args[1]->listElems()[n]);
v.listElems()[n] = args[1]->listElems()[n];
}
auto comparator = [&](Value * a, Value * b) {
/* Optimization: if the comparator is lessThan, bypass
callFunction. */
if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan)
return CompareValues()(a, b);
Value vTmp1, vTmp2;
state.callFunction(*args[0], *a, vTmp1, pos);
state.callFunction(vTmp1, *b, vTmp2, pos);
return state.forceBool(vTmp2);
};
/* FIXME: std::sort can segfault if the comparator is not a strict
weak ordering. What to do? std::stable_sort() seems more
resilient, but no guarantees... */
std::stable_sort(v.listElems(), v.listElems() + len, comparator);
}
@@ -1389,11 +1518,8 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po
string s = state.coerceToString(pos, *args[0], context);
PathSet context2;
foreach (PathSet::iterator, i, context) {
Path p = *i;
if (p.at(0) == '=') p = "~" + string(p, 1);
context2.insert(p);
}
for (auto & p : context)
context2.insert(p.at(0) == '=' ? "~" + string(p, 1) : p);
mkString(v, s, context2);
}
@@ -1434,13 +1560,68 @@ static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value
for (unsigned int n = 0; n < len; ++n) {
auto i = subs.find(n);
if (i == subs.end())
mkNull(*(v.list.elems[n] = state.allocValue()));
mkNull(*(v.listElems()[n] = state.allocValue()));
else
mkString(*(v.list.elems[n] = state.allocValue()), i->second);
mkString(*(v.listElems()[n] = state.allocValue()), i->second);
}
}
static void prim_concatStringSep(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
auto sep = state.forceString(*args[0], context, pos);
state.forceList(*args[1], pos);
string res;
res.reserve((args[1]->listSize() + 32) * sep.size());
bool first = true;
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
if (first) first = false; else res += sep;
res += state.coerceToString(pos, *args[1]->listElems()[n], context);
}
mkString(v, res, context);
}
static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
state.forceList(*args[0], pos);
state.forceList(*args[1], pos);
if (args[0]->listSize() != args[1]->listSize())
throw EvalError(format("from and to arguments to replaceStrings have different lengths, at %1%") % pos);
Strings from;
for (unsigned int n = 0; n < args[0]->listSize(); ++n)
from.push_back(state.forceStringNoCtx(*args[0]->listElems()[n], pos));
Strings to;
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
to.push_back(state.forceStringNoCtx(*args[1]->listElems()[n], pos));
PathSet context;
auto s = state.forceString(*args[2], context, pos);
string res;
for (size_t p = 0; p < s.size(); ) {
bool found = false;
for (auto i = from.begin(), j = to.begin(); i != from.end(); ++i, ++j)
if (s.compare(p, i->size(), *i) == 0) {
found = true;
p += i->size();
res += *j;
break;
}
if (!found) res += s[p++];
}
mkString(v, res, context);
}
/*************************************************************
* Versions
*************************************************************/
@@ -1465,6 +1646,55 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a
}
/*************************************************************
* Networking
*************************************************************/
void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
const string & who, bool unpack)
{
if (state.restricted) throw Error(format("%1% is not allowed in restricted mode") % who);
string url;
state.forceValue(*args[0]);
if (args[0]->type == tAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
string name(attr.name);
if (name == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos);
else
throw EvalError(format("unsupported argument %1% to %2%, at %3%") % attr.name % who % attr.pos);
}
if (url.empty())
throw EvalError(format("url argument required, at %1%") % pos);
} else
url = state.forceStringNoCtx(*args[0], pos);
Path res = downloadFileCached(url, unpack);
mkString(v, res, PathSet({res}));
}
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
fetch(state, pos, args, v, "fetchurl", false);
}
static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
fetch(state, pos, args, v, "fetchTarball", true);
}
/*************************************************************
* Primop registration
*************************************************************/
@@ -1493,12 +1723,15 @@ void EvalState::createBaseEnv()
mkInt(v, time(0));
addConstant("__currentTime", v);
mkString(v, settings.thisSystem.c_str());
mkString(v, settings.thisSystem);
addConstant("__currentSystem", v);
mkString(v, nixVersion.c_str());
mkString(v, nixVersion);
addConstant("__nixVersion", v);
mkString(v, settings.nixStore);
addConstant("__storeDir", 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 ?
@@ -1576,6 +1809,11 @@ void EvalState::createBaseEnv()
addPrimOp("__elem", 2, prim_elem);
addPrimOp("__concatLists", 1, prim_concatLists);
addPrimOp("__length", 1, prim_length);
addPrimOp("__foldl'", 3, prim_foldlStrict);
addPrimOp("__any", 2, prim_any);
addPrimOp("__all", 2, prim_all);
addPrimOp("__genList", 2, prim_genList);
addPrimOp("__sort", 2, prim_sort);
// Integer arithmetic
addPrimOp("__add", 2, prim_add);
@@ -1592,6 +1830,8 @@ void EvalState::createBaseEnv()
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
addPrimOp("__hashString", 2, prim_hashString);
addPrimOp("__match", 2, prim_match);
addPrimOp("__concatStringsSep", 2, prim_concatStringSep);
addPrimOp("__replaceStrings", 3, prim_replaceStrings);
// Versions
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
@@ -1600,10 +1840,13 @@ void EvalState::createBaseEnv()
// Derivations
addPrimOp("derivationStrict", 1, prim_derivationStrict);
// Networking
addPrimOp("__fetchurl", 1, prim_fetchurl);
addPrimOp("fetchTarball", 1, prim_fetchTarball);
/* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */
string path = findFile("nix/derivation.nix");
assert(!path.empty());
string path = settings.nixDataDir + "/nix/corepkgs/derivation.nix";
sDerivationNix = symbols.create(path);
evalFile(path, v);
addConstant("derivation", v);
@@ -1612,7 +1855,7 @@ void EvalState::createBaseEnv()
mkList(v, searchPath.size());
int n = 0;
for (auto & i : searchPath) {
v2 = v.list.elems[n++] = allocValue();
v2 = v.listElems()[n++] = allocValue();
mkAttrs(*v2, 2);
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
+17 -17
View File
@@ -12,14 +12,14 @@ namespace nix {
void escapeJSON(std::ostream & str, const string & s)
{
str << "\"";
foreach (string::const_iterator, i, s)
if (*i == '\"' || *i == '\\') str << "\\" << *i;
else if (*i == '\n') str << "\\n";
else if (*i == '\r') str << "\\r";
else if (*i == '\t') str << "\\t";
else if (*i >= 0 && *i < 32)
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
else str << *i;
for (auto & i : s)
if (i == '\"' || i == '\\') str << "\\" << i;
else if (i == '\n') str << "\\n";
else if (i == '\r') str << "\\r";
else if (i == '\t') str << "\\t";
else if (i >= 0 && i < 32)
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) i << std::dec;
else str << i;
str << "\"";
}
@@ -59,11 +59,11 @@ void printValueAsJSON(EvalState & state, bool strict,
if (i == v.attrs->end()) {
JSONObject json(str);
StringSet names;
foreach (Bindings::iterator, i, *v.attrs)
names.insert(i->name);
foreach (StringSet::iterator, i, names) {
Attr & a(*v.attrs->find(state.symbols.create(*i)));
json.attr(*i);
for (auto & j : *v.attrs)
names.insert(j.name);
for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j)));
json.attr(j);
printValueAsJSON(state, strict, *a.value, str, context);
}
} else
@@ -71,16 +71,16 @@ void printValueAsJSON(EvalState & state, bool strict,
break;
}
case tList: {
case tList1: case tList2: case tListN: {
JSONList json(str);
for (unsigned int n = 0; n < v.list.length; ++n) {
for (unsigned int n = 0; n < v.listSize(); ++n) {
json.elem();
printValueAsJSON(state, strict, *v.list.elems[n], str, context);
printValueAsJSON(state, strict, *v.listElems()[n], str, context);
}
break;
}
case tExternal:
case tExternal:
v.external->printValueAsJSON(state, strict, str, context);
break;
+23 -23
View File
@@ -8,7 +8,7 @@
namespace nix {
static XMLAttrs singletonAttrs(const string & name, const string & value)
{
XMLAttrs attrs;
@@ -33,17 +33,17 @@ static void showAttrs(EvalState & state, bool strict, bool location,
Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
{
StringSet names;
foreach (Bindings::iterator, i, attrs)
names.insert(i->name);
foreach (StringSet::iterator, i, names) {
Attr & a(*attrs.find(state.symbols.create(*i)));
for (auto & i : attrs)
names.insert(i.name);
for (auto & i : names) {
Attr & a(*attrs.find(state.symbols.create(i)));
XMLAttrs xmlAttrs;
xmlAttrs["name"] = *i;
xmlAttrs["name"] = i;
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location,
*a.value, doc, context, drvsSeen);
@@ -57,7 +57,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
checkInterrupt();
if (strict) state.forceValue(v);
switch (v.type) {
case tInt:
@@ -85,7 +85,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
case tAttrs:
if (state.isDerivation(v)) {
XMLAttrs xmlAttrs;
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
Path drvPath;
@@ -95,7 +95,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
if (a->value->type == tString)
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
}
a = v.attrs->find(state.sOutPath);
if (a != v.attrs->end()) {
if (strict) state.forceValue(*a->value);
@@ -116,13 +116,13 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
XMLOpenElement _(doc, "attrs");
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
}
break;
case tList: {
case tList1: case tList2: case tListN: {
XMLOpenElement _(doc, "list");
for (unsigned int n = 0; n < v.list.length; ++n)
printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
for (unsigned int n = 0; n < v.listSize(); ++n)
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
break;
}
@@ -130,17 +130,17 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
XMLAttrs xmlAttrs;
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
XMLOpenElement _(doc, "function", xmlAttrs);
if (v.lambda.fun->matchAttrs) {
XMLAttrs attrs;
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
XMLOpenElement _(doc, "attrspat", attrs);
foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
for (auto & i : v.lambda.fun->formals->formals)
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
} else
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
break;
}
@@ -166,9 +166,9 @@ void printValueAsXML(EvalState & state, bool strict, bool location,
{
XMLWriter doc(true, out);
XMLOpenElement root(doc, "expr");
PathSet drvsSeen;
PathSet drvsSeen;
printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
}
}
+26 -3
View File
@@ -12,7 +12,9 @@ typedef enum {
tPath,
tNull,
tAttrs,
tList,
tList1,
tList2,
tListN,
tThunk,
tApp,
tLambda,
@@ -119,9 +121,10 @@ struct Value
const char * path;
Bindings * attrs;
struct {
unsigned int length;
unsigned int size;
Value * * elems;
} list;
} bigList;
Value * smallList[2];
struct {
Env * env;
Expr * expr;
@@ -139,6 +142,26 @@ struct Value
} primOpApp;
ExternalValueBase * external;
};
bool isList() const
{
return type == tList1 || type == tList2 || type == tListN;
}
Value * * listElems()
{
return type == tList1 || type == tList2 ? smallList : bigList.elems;
}
const Value * const * listElems() const
{
return type == tList1 || type == tList2 ? smallList : bigList.elems;
}
unsigned int listSize() const
{
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
}
};
+38 -4
View File
@@ -90,6 +90,7 @@ static void setLogType(string lt)
if (lt == "pretty") logType = ltPretty;
else if (lt == "escapes") logType = ltEscapes;
else if (lt == "flat") logType = ltFlat;
else if (lt == "systemd") logType = ltSystemd;
else throw UsageError("unknown log type");
}
@@ -116,6 +117,9 @@ void initNix()
std::ios::sync_with_stdio(false);
if (getEnv("IN_SYSTEMD") == "1")
logType = ltSystemd;
settings.processEnvironment();
settings.loadConfFile();
@@ -239,6 +243,20 @@ void parseCmdLine(int argc, char * * argv,
void printVersion(const string & programName)
{
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
if (verbosity > lvlInfo) {
Strings cfg;
#if HAVE_BOEHMGC
cfg.push_back("gc");
#endif
#if HAVE_SODIUM
cfg.push_back("signed-caches");
#endif
std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
std::cout << "Configuration file: " << settings.nixConfDir + "/nix.conf" << "\n";
std::cout << "Store directory: " << settings.nixStore << "\n";
std::cout << "State directory: " << settings.nixStateDir << "\n";
std::cout << "Database directory: " << settings.nixDBPath << "\n";
}
throw Exit();
}
@@ -293,8 +311,9 @@ int handleExceptions(const string & programName, std::function<void()> fun)
RunPager::RunPager()
{
if (!isatty(STDOUT_FILENO)) return;
string pager = getEnv("PAGER", "default");
if (pager == "" || pager == "cat") return;
char * pager = getenv("NIX_PAGER");
if (!pager) pager = getenv("PAGER");
if (pager && ((string) pager == "" || (string) pager == "cat")) return;
/* Ignore SIGINT. The pager will handle it (and we'll get
SIGPIPE). */
@@ -314,8 +333,8 @@ RunPager::RunPager()
throw SysError("dupping stdin");
if (!getenv("LESS"))
setenv("LESS", "FRSXMK", 1);
if (pager != "default")
execl("/bin/sh", "sh", "-c", pager.c_str(), NULL);
if (pager)
execl("/bin/sh", "sh", "-c", pager, NULL);
execlp("pager", "pager", NULL);
execlp("less", "less", NULL);
execlp("more", "more", NULL);
@@ -341,4 +360,19 @@ RunPager::~RunPager()
}
string showBytes(unsigned long long bytes)
{
return (format("%.2f MiB") % (bytes / (1024.0 * 1024.0))).str();
}
PrintFreed::~PrintFreed()
{
if (show)
std::cout << format("%1% store paths deleted, %2% freed\n")
% results.paths.size()
% showBytes(results.bytesFreed);
}
}
+17
View File
@@ -83,4 +83,21 @@ private:
extern volatile ::sig_atomic_t blockInt;
/* GC helpers. */
string showBytes(unsigned long long bytes);
class GCResults;
struct PrintFreed
{
bool show;
const GCResults & results;
PrintFreed(bool show, const GCResults & results)
: show(show), results(results) { }
~PrintFreed();
};
}
+818 -384
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
#include "builtins.hh"
#include "download.hh"
namespace nix {
void builtinFetchurl(const BasicDerivation & drv)
{
auto url = drv.env.find("url");
if (url == drv.env.end()) throw Error("attribute url missing");
printMsg(lvlInfo, format("downloading %1%...") % url->second);
auto data = downloadFile(url->second); // FIXME: show progress
auto out = drv.env.find("out");
if (out == drv.env.end()) throw Error("attribute url missing");
writeFile(out->second, data.data);
auto executable = drv.env.find("executable");
if (executable != drv.env.end() && executable->second == "1") {
if (chmod(out->second.c_str(), 0755) == -1)
throw SysError(format("making %1% executable") % out->second);
}
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "derivations.hh"
namespace nix {
void builtinFetchurl(const BasicDerivation & drv);
}
+69 -19
View File
@@ -3,6 +3,7 @@
#include "globals.hh"
#include "util.hh"
#include "misc.hh"
#include "worker-protocol.hh"
namespace nix {
@@ -31,8 +32,8 @@ Path writeDerivation(StoreAPI & store,
{
PathSet references;
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
references.insert(i->first);
for (auto & i : drv.inputDrvs)
references.insert(i.first);
/* Note that the outputs of a derivation are *not* references
(that can be missing (of course) and should not necessarily be
held during a garbage collection). */
@@ -155,21 +156,21 @@ string unparseDerivation(const Derivation & drv)
s += "Derive([";
bool first = true;
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
for (auto & i : drv.outputs) {
if (first) first = false; else s += ',';
s += '('; printString(s, i->first);
s += ','; printString(s, i->second.path);
s += ','; printString(s, i->second.hashAlgo);
s += ','; printString(s, i->second.hash);
s += '('; printString(s, i.first);
s += ','; printString(s, i.second.path);
s += ','; printString(s, i.second.hashAlgo);
s += ','; printString(s, i.second.hash);
s += ')';
}
s += "],[";
first = true;
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
for (auto & i : drv.inputDrvs) {
if (first) first = false; else s += ',';
s += '('; printString(s, i->first);
s += ','; printStrings(s, i->second.begin(), i->second.end());
s += '('; printString(s, i.first);
s += ','; printStrings(s, i.second.begin(), i.second.end());
s += ')';
}
@@ -182,10 +183,10 @@ string unparseDerivation(const Derivation & drv)
s += ",[";
first = true;
foreach (StringPairs::const_iterator, i, drv.env) {
for (auto & i : drv.env) {
if (first) first = false; else s += ',';
s += '('; printString(s, i->first);
s += ','; printString(s, i->second);
s += '('; printString(s, i.first);
s += ','; printString(s, i.second);
s += ')';
}
@@ -246,15 +247,15 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
/* For other derivations, replace the inputs paths with recursive
calls to this function.*/
DerivationInputs inputs2;
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
Hash h = drvHashes[i->first];
for (auto & i : drv.inputDrvs) {
Hash h = drvHashes[i.first];
if (h.type == htUnknown) {
assert(store.isValidPath(i->first));
Derivation drv2 = readDerivation(i->first);
assert(store.isValidPath(i.first));
Derivation drv2 = readDerivation(i.first);
h = hashDerivationModulo(store, drv2);
drvHashes[i->first] = h;
drvHashes[i.first] = h;
}
inputs2[printHash(h)] = i->second;
inputs2[printHash(h)] = i.second;
}
drv.inputDrvs = inputs2;
@@ -285,4 +286,53 @@ bool wantOutput(const string & output, const std::set<string> & wanted)
}
PathSet outputPaths(const BasicDerivation & drv)
{
PathSet paths;
for (auto & i : drv.outputs)
paths.insert(i.second.path);
return paths;
}
Source & operator >> (Source & in, BasicDerivation & drv)
{
drv.outputs.clear();
auto nr = readInt(in);
for (unsigned int n = 0; n < nr; n++) {
auto name = readString(in);
DerivationOutput o;
in >> o.path >> o.hashAlgo >> o.hash;
assertStorePath(o.path);
drv.outputs[name] = o;
}
drv.inputSrcs = readStorePaths<PathSet>(in);
in >> drv.platform >> drv.builder;
drv.args = readStrings<Strings>(in);
nr = readInt(in);
for (unsigned int n = 0; n < nr; n++) {
auto key = readString(in);
auto value = readString(in);
drv.env[key] = value;
}
return in;
}
Sink & operator << (Sink & out, const BasicDerivation & drv)
{
out << drv.outputs.size();
for (auto & i : drv.outputs)
out << i.first << i.second.path << i.second.hashAlgo << i.second.hash;
out << drv.inputSrcs << drv.platform << drv.builder << drv.args;
out << drv.env.size();
for (auto & i : drv.env)
out << i.first << i.second;
return out;
}
}
+15 -2
View File
@@ -40,15 +40,21 @@ typedef std::map<Path, StringSet> DerivationInputs;
typedef std::map<string, string> StringPairs;
struct Derivation
struct BasicDerivation
{
DerivationOutputs outputs; /* keyed on symbolic IDs */
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
PathSet inputSrcs; /* inputs that are sources */
string platform;
Path builder;
Strings args;
StringPairs env;
virtual ~BasicDerivation() { };
};
struct Derivation : BasicDerivation
{
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
};
@@ -89,5 +95,12 @@ Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outpu
bool wantOutput(const string & output, const std::set<string> & wanted);
PathSet outputPaths(const BasicDerivation & drv);
struct Source;
struct Sink;
Source & operator >> (Source & in, BasicDerivation & drv);
Sink & operator << (Sink & out, const BasicDerivation & drv);
}
+236
View File
@@ -0,0 +1,236 @@
#include "download.hh"
#include "util.hh"
#include "globals.hh"
#include "hash.hh"
#include "store-api.hh"
#include <curl/curl.h>
namespace nix {
struct Curl
{
CURL * curl;
string data;
string etag, status, expectedETag;
struct curl_slist * requestHeaders;
static size_t writeCallback(void * contents, size_t size, size_t nmemb, void * userp)
{
Curl & c(* (Curl *) userp);
size_t realSize = size * nmemb;
c.data.append((char *) contents, realSize);
return realSize;
}
static size_t headerCallback(void * contents, size_t size, size_t nmemb, void * userp)
{
Curl & c(* (Curl *) userp);
size_t realSize = size * nmemb;
string line = string((char *) contents, realSize);
printMsg(lvlVomit, format("got header: %1%") % trim(line));
if (line.compare(0, 5, "HTTP/") == 0) { // new response starts
c.etag = "";
auto ss = tokenizeString<vector<string>>(line, " ");
c.status = ss.size() >= 2 ? ss[1] : "";
} else {
auto i = line.find(':');
if (i != string::npos) {
string name = trim(string(line, 0, i));
if (name == "ETag") { // FIXME: case
c.etag = trim(string(line, i + 1));
/* Hack to work around a GitHub bug: it sends
ETags, but ignores If-None-Match. So if we get
the expected ETag on a 200 response, then shut
down the connection because we already have the
data. */
printMsg(lvlDebug, format("got ETag: %1%") % c.etag);
if (c.etag == c.expectedETag && c.status == "200") {
printMsg(lvlDebug, format("shutting down on 200 HTTP response with expected ETag"));
return 0;
}
}
}
}
return realSize;
}
static int progressCallback(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
return _isInterrupted;
}
Curl()
{
requestHeaders = 0;
curl = curl_easy_init();
if (!curl) throw Error("unable to initialize curl");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
curl_easy_setopt(curl, CURLOPT_USERAGENT, ("Nix/" + nixVersion).c_str());
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &curl);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) &curl);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
}
~Curl()
{
if (curl) curl_easy_cleanup(curl);
if (requestHeaders) curl_slist_free_all(requestHeaders);
}
bool fetch(const string & url, const string & expectedETag = "")
{
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
data.clear();
if (requestHeaders) {
curl_slist_free_all(requestHeaders);
requestHeaders = 0;
}
if (!expectedETag.empty()) {
this->expectedETag = expectedETag;
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + expectedETag).c_str());
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeaders);
CURLcode res = curl_easy_perform(curl);
checkInterrupt();
if (res == CURLE_WRITE_ERROR && etag == expectedETag) return false;
if (res != CURLE_OK)
throw DownloadError(format("unable to download %1%: %2% (%3%)")
% url % curl_easy_strerror(res) % res);
long httpStatus = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpStatus);
if (httpStatus == 304) return false;
return true;
}
};
DownloadResult downloadFile(string url, string expectedETag)
{
DownloadResult res;
Curl curl;
if (curl.fetch(url, expectedETag)) {
res.cached = false;
res.data = curl.data;
} else
res.cached = true;
res.etag = curl.etag;
return res;
}
Path downloadFileCached(const string & url, bool unpack)
{
Path cacheDir = getEnv("XDG_CACHE_HOME", getEnv("HOME", "") + "/.cache") + "/nix/tarballs";
createDirs(cacheDir);
string urlHash = printHash32(hashString(htSHA256, url));
Path dataFile = cacheDir + "/" + urlHash + ".info";
Path fileLink = cacheDir + "/" + urlHash + "-file";
Path storePath;
string expectedETag;
int ttl = settings.get("tarball-ttl", 60 * 60);
bool skip = false;
if (pathExists(fileLink) && pathExists(dataFile)) {
storePath = readLink(fileLink);
store->addTempRoot(storePath);
if (store->isValidPath(storePath)) {
auto ss = tokenizeString<vector<string>>(readFile(dataFile), "\n");
if (ss.size() >= 3 && ss[0] == url) {
time_t lastChecked;
if (string2Int(ss[2], lastChecked) && lastChecked + ttl >= time(0))
skip = true;
else if (!ss[1].empty()) {
printMsg(lvlDebug, format("verifying previous ETag %1%") % ss[1]);
expectedETag = ss[1];
}
}
} else
storePath = "";
}
string name;
auto p = url.rfind('/');
if (p != string::npos) name = string(url, p + 1);
if (!skip) {
if (storePath.empty())
printMsg(lvlInfo, format("downloading %1%...") % url);
else
printMsg(lvlInfo, format("checking %1%...") % url);
try {
auto res = downloadFile(url, expectedETag);
if (!res.cached)
storePath = store->addTextToStore(name, res.data, PathSet(), false);
assert(!storePath.empty());
replaceSymlink(storePath, fileLink);
writeFile(dataFile, url + "\n" + res.etag + "\n" + int2String(time(0)) + "\n");
} catch (DownloadError & e) {
if (storePath.empty()) throw;
printMsg(lvlError, format("warning: %1%; using cached result") % e.msg());
}
}
if (unpack) {
Path unpackedLink = cacheDir + "/" + baseNameOf(storePath) + "-unpacked";
Path unpackedStorePath;
if (pathExists(unpackedLink)) {
unpackedStorePath = readLink(unpackedLink);
store->addTempRoot(unpackedStorePath);
if (!store->isValidPath(unpackedStorePath))
unpackedStorePath = "";
}
if (unpackedStorePath.empty()) {
printMsg(lvlInfo, format("unpacking %1%...") % url);
Path tmpDir = createTempDir();
AutoDelete autoDelete(tmpDir, true);
// FIXME: this requires GNU tar for decompression.
runProgram("tar", true, {"xf", storePath, "-C", tmpDir, "--strip-components", "1"}, "");
unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false);
}
replaceSymlink(unpackedStorePath, unpackedLink);
return unpackedStorePath;
}
return storePath;
}
bool isUri(const string & s)
{
size_t pos = s.find("://");
if (pos == string::npos) return false;
string scheme(s, 0, pos);
return scheme == "http" || scheme == "https" || scheme == "file";
}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "types.hh"
#include <string>
namespace nix {
struct DownloadResult
{
bool cached;
string data, etag;
};
DownloadResult downloadFile(string url, string expectedETag = "");
Path downloadFileCached(const string & url, bool unpack);
MakeError(DownloadError, Error)
bool isUri(const string & s);
}
+45 -31
View File
@@ -96,7 +96,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
"(are you running nix-build inside the store?)") % gcRoot);
if (indirect) {
/* Don't clobber the the link if it already exists and doesn't
/* Don't clobber the link if it already exists and doesn't
point to the Nix store. */
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
throw Error(format("cannot create symlink %1%; already exists") % gcRoot);
@@ -350,15 +350,14 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
StringSet paths = tokenizeString<StringSet>(result, "\n");
foreach (StringSet::iterator, i, paths) {
if (isInStore(*i)) {
Path path = toStorePath(*i);
for (auto & i : paths)
if (isInStore(i)) {
Path path = toStorePath(i);
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
debug(format("got additional root %1%") % path);
roots.insert(path);
}
}
}
}
@@ -376,6 +375,7 @@ struct LocalStore::GCState
bool gcKeepOutputs;
bool gcKeepDerivations;
unsigned long long bytesInvalidated;
bool moveToTrash = true;
Path trashDir;
bool shouldDelete;
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
@@ -407,8 +407,8 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
if (isValidPath(path)) {
PathSet referrers;
queryReferrers(path, referrers);
foreach (PathSet::iterator, i, referrers)
if (*i != path) deletePathRecursive(state, *i);
for (auto & i : referrers)
if (i != path) deletePathRecursive(state, i);
size = queryPathInfo(path).narSize;
invalidatePathChecked(path);
}
@@ -428,16 +428,23 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
not holding the global GC lock) we can delete the path without
being afraid that the path has become alive again. Otherwise
delete it right away. */
if (S_ISDIR(st.st_mode)) {
if (state.moveToTrash && S_ISDIR(st.st_mode)) {
// Estimate the amount freed using the narSize field. FIXME:
// if the path was not valid, need to determine the actual
// size.
state.bytesInvalidated += size;
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making %1% writable") % path);
Path tmp = state.trashDir + "/" + baseNameOf(path);
if (rename(path.c_str(), tmp.c_str()))
throw SysError(format("unable to rename %1% to %2%") % path % tmp);
try {
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making %1% writable") % path);
Path tmp = state.trashDir + "/" + baseNameOf(path);
if (rename(path.c_str(), tmp.c_str()))
throw SysError(format("unable to rename %1% to %2%") % path % tmp);
state.bytesInvalidated += size;
} catch (SysError & e) {
if (e.errNo == ENOSPC) {
printMsg(lvlInfo, format("note: can't create move %1%: %2%") % path % e.msg());
deleteGarbage(state, path);
}
}
} else
deleteGarbage(state, path);
@@ -479,22 +486,22 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p
don't delete the derivation if any of the outputs are alive. */
if (state.gcKeepDerivations && isDerivation(path)) {
PathSet outputs = queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (isValidPath(*i) && queryDeriver(*i) == path)
incoming.insert(*i);
for (auto & i : outputs)
if (isValidPath(i) && queryDeriver(i) == path)
incoming.insert(i);
}
/* If gc-keep-outputs is set, then don't delete this path if there
are derivers of this path that are not garbage. */
if (state.gcKeepOutputs) {
PathSet derivers = queryValidDerivers(path);
foreach (PathSet::iterator, i, derivers)
incoming.insert(*i);
for (auto & i : derivers)
incoming.insert(i);
}
foreach (PathSet::iterator, i, incoming)
if (*i != path)
if (canReachRoot(state, visited, *i)) {
for (auto & i : incoming)
if (i != path)
if (canReachRoot(state, visited, i)) {
state.alive.insert(path);
return true;
}
@@ -614,7 +621,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
printMsg(lvlError, format("finding garbage collector roots..."));
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
for (auto & i : rootMap) state.roots.insert(i.second);
/* Add additional roots returned by the program specified by the
NIX_ROOT_FINDER environment variable. This is typically used
@@ -636,7 +643,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (state.shouldDelete) {
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
createDirs(state.trashDir);
try {
createDirs(state.trashDir);
} catch (SysError & e) {
if (e.errNo == ENOSPC) {
printMsg(lvlInfo, format("note: can't create trash directory: %1%") % e.msg());
state.moveToTrash = false;
}
}
}
/* Now either delete all garbage paths, or just the specified
@@ -644,11 +658,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (options.action == GCOptions::gcDeleteSpecific) {
foreach (PathSet::iterator, i, options.pathsToDelete) {
assertStorePath(*i);
tryToDelete(state, *i);
if (state.dead.find(*i) == state.dead.end())
throw Error(format("cannot delete path %1% since it is still alive") % *i);
for (auto & i : options.pathsToDelete) {
assertStorePath(i);
tryToDelete(state, i);
if (state.dead.find(i) == state.dead.end())
throw Error(format("cannot delete path %1% since it is still alive") % i);
}
} else if (options.maxFreed > 0) {
@@ -692,8 +706,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
vector<Path> entries_(entries.begin(), entries.end());
random_shuffle(entries_.begin(), entries_.end());
foreach (vector<Path>::iterator, i, entries_)
tryToDelete(state, *i);
for (auto & i : entries_)
tryToDelete(state, i);
} catch (GCLimitReached & e) {
}
+19 -11
View File
@@ -41,13 +41,12 @@ Settings::Settings()
buildTimeout = 0;
useBuildHook = true;
printBuildTrace = false;
reservedSize = 1024 * 1024;
reservedSize = 8 * 1024 * 1024;
fsyncMetadata = true;
useSQLiteWAL = true;
syncBeforeRegistering = false;
useSubstitutes = true;
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
useChroot = false;
useSshSubstituter = true;
impersonateLinux26 = false;
keepLog = true;
@@ -68,6 +67,7 @@ Settings::Settings()
void Settings::processEnvironment()
{
nixPrefix = NIX_PREFIX;
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));
@@ -144,6 +144,14 @@ bool Settings::get(const string & name, bool def)
}
int Settings::get(const string & name, int def)
{
int res = def;
_get(res, name);
return res;
}
void Settings::update()
{
_get(tryFallback, "build-fallback");
@@ -158,7 +166,6 @@ void Settings::update()
_get(syncBeforeRegistering, "sync-before-registering");
_get(useSubstitutes, "build-use-substitutes");
_get(buildUsersGroup, "build-users-group");
_get(useChroot, "build-use-chroot");
_get(impersonateLinux26, "build-impersonate-linux-26");
_get(keepLog, "build-keep-log");
_get(compressLog, "build-compress-log");
@@ -175,6 +182,7 @@ void Settings::update()
_get(logServers, "log-servers");
_get(enableImportNative, "allow-unsafe-native-code-during-evaluation");
_get(useCaseHack, "use-case-hack");
_get(preBuildHook, "pre-build-hook");
string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") {
@@ -240,12 +248,12 @@ template<class N> void Settings::_get(N & res, const string & name)
string Settings::pack()
{
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)
for (auto & 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';
s += i.first; s += '='; s += i.second; s += '\n';
}
return s;
}
@@ -253,11 +261,11 @@ string Settings::pack()
void Settings::unpack(const string & pack) {
Strings lines = tokenizeString<Strings>(pack, "\n");
foreach (Strings::iterator, i, lines) {
string::size_type eq = i->find('=');
for (auto & i : lines) {
string::size_type eq = i.find('=');
if (eq == string::npos)
throw Error("illegal option name/value");
set(i->substr(0, eq), i->substr(eq + 1));
set(i.substr(0, eq), i.substr(eq + 1));
}
}
+8 -3
View File
@@ -27,6 +27,8 @@ struct Settings {
bool get(const string & name, bool def);
int get(const string & name, int def);
void update();
string pack();
@@ -40,6 +42,8 @@ struct Settings {
Path nixDataDir; /* !!! fix */
Path nixPrefix;
/* The directory where we log various operations. */
Path nixLogDir;
@@ -145,9 +149,6 @@ struct Settings {
/* The Unix group that contains the build users. */
string buildUsersGroup;
/* Whether to build in chroot. */
bool useChroot;
/* Set of ssh connection strings for the ssh substituter */
Strings sshSubstituterHosts;
@@ -205,6 +206,10 @@ struct Settings {
/* Whether the importNative primop should be enabled */
bool enableImportNative;
/* The hook to run just before a build to set derivation-specific
build settings */
Path preBuildHook;
private:
SettingsMap settings, overrides;
+106 -98
View File
@@ -13,6 +13,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <utime.h>
@@ -256,20 +257,23 @@ LocalStore::LocalStore(bool reserveSpace)
if (chmod(perUserDir.c_str(), 01777) == -1)
throw SysError(format("could not set permissions on %1% to 1777") % perUserDir);
mode_t perm = 01775;
struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
if (!gr)
throw Error(format("the group %1% specified in build-users-group does not exist")
printMsg(lvlError, format("warning: the group %1% specified in build-users-group does not exist")
% settings.buildUsersGroup);
else {
struct stat st;
if (stat(settings.nixStore.c_str(), &st))
throw SysError(format("getting attributes of path %1%") % settings.nixStore);
struct stat st;
if (stat(settings.nixStore.c_str(), &st))
throw SysError(format("getting attributes of path %1%") % settings.nixStore);
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != 01775) {
if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
throw SysError(format("changing ownership of path %1%") % settings.nixStore);
if (chmod(settings.nixStore.c_str(), 01775) == -1)
throw SysError(format("changing permissions on path %1%") % settings.nixStore);
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
throw SysError(format("changing ownership of path %1%") % settings.nixStore);
if (chmod(settings.nixStore.c_str(), perm) == -1)
throw SysError(format("changing permissions on path %1%") % settings.nixStore);
}
}
}
@@ -285,7 +289,17 @@ LocalStore::LocalStore(bool reserveSpace)
struct stat st;
if (stat(reservedPath.c_str(), &st) == -1 ||
st.st_size != settings.reservedSize)
writeFile(reservedPath, string(settings.reservedSize, 'X'));
{
AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT, 0600);
int res = -1;
#if HAVE_POSIX_FALLOCATE
res = posix_fallocate(fd, 0, settings.reservedSize);
#endif
if (res == -1) {
writeFull(fd, string(settings.reservedSize, 'X'));
ftruncate(fd, settings.reservedSize);
}
}
}
else
deletePath(reservedPath);
@@ -353,13 +367,13 @@ LocalStore::LocalStore(bool reserveSpace)
LocalStore::~LocalStore()
{
try {
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
if (i->second.disabled) continue;
i->second.to.close();
i->second.from.close();
i->second.error.close();
if (i->second.pid != -1)
i->second.pid.wait(true);
for (auto & i : runningSubstituters) {
if (i.second.disabled) continue;
i.second.to.close();
i.second.from.close();
i.second.error.close();
if (i.second.pid != -1)
i.second.pid.wait(true);
}
} catch (...) {
ignoreException();
@@ -657,19 +671,19 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
else {
Derivation drvCopy(drv);
foreach (DerivationOutputs::iterator, i, drvCopy.outputs) {
i->second.path = "";
drvCopy.env[i->first] = "";
for (auto & i : drvCopy.outputs) {
i.second.path = "";
drvCopy.env[i.first] = "";
}
Hash h = hashDerivationModulo(*this, drvCopy);
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
Path outPath = makeOutputPath(i->first, h, drvName);
StringPairs::const_iterator j = drv.env.find(i->first);
if (i->second.path != outPath || j == drv.env.end() || j->second != outPath)
for (auto & i : drv.outputs) {
Path outPath = makeOutputPath(i.first, h, drvName);
StringPairs::const_iterator j = drv.env.find(i.first);
if (i.second.path != outPath || j == drv.env.end() || j->second != outPath)
throw Error(format("derivation %1% has incorrect output %2%, should be %3%")
% drvPath % i->second.path % outPath);
% drvPath % i.second.path % outPath);
}
}
}
@@ -707,11 +721,11 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool che
registration above is undone. */
if (checkOutputs) checkDerivationOutputs(info.path, drv);
foreach (DerivationOutputs::iterator, i, drv.outputs) {
for (auto & i : drv.outputs) {
SQLiteStmtUse use(stmtAddDerivationOutput);
stmtAddDerivationOutput.bind(id);
stmtAddDerivationOutput.bind(i->first);
stmtAddDerivationOutput.bind(i->second.path);
stmtAddDerivationOutput.bind(i.first);
stmtAddDerivationOutput.bind(i.second.path);
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
throwSQLiteError(db, format("adding derivation output for %1% in database") % info.path);
}
@@ -782,11 +796,11 @@ void LocalStore::clearFailedPaths(const PathSet & paths)
retry_sqlite {
SQLiteTxn txn(db);
foreach (PathSet::const_iterator, i, paths) {
for (auto & i : paths) {
SQLiteStmtUse use(stmtClearFailedPath);
stmtClearFailedPath.bind(*i);
stmtClearFailedPath.bind(i);
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
throwSQLiteError(db, format("clearing failed path %1% in database") % *i);
throwSQLiteError(db, format("clearing failed path %1% in database") % i);
}
txn.commit();
@@ -909,8 +923,8 @@ PathSet LocalStore::queryValidPaths(const PathSet & paths)
{
retry_sqlite {
PathSet res;
foreach (PathSet::const_iterator, i, paths)
if (isValidPath_(*i)) res.insert(*i);
for (auto & i : paths)
if (isValidPath_(i)) res.insert(i);
return res;
} end_retry_sqlite;
}
@@ -1198,14 +1212,14 @@ template<class T> T LocalStore::getIntLineFromSubstituter(RunningSubstituter & r
PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
{
PathSet res;
foreach (Paths::iterator, i, settings.substituters) {
for (auto & i : settings.substituters) {
if (res.size() == paths.size()) break;
RunningSubstituter & run(runningSubstituters[*i]);
startSubstituter(*i, run);
RunningSubstituter & run(runningSubstituters[i]);
startSubstituter(i, run);
if (run.disabled) continue;
string s = "have ";
foreach (PathSet::const_iterator, j, paths)
if (res.find(*j) == res.end()) { s += *j; s += " "; }
for (auto & j : paths)
if (res.find(j) == res.end()) { s += j; s += " "; }
writeLine(run.to, s);
while (true) {
/* FIXME: we only read stderr when an error occurs, so
@@ -1229,8 +1243,8 @@ void LocalStore::querySubstitutablePathInfos(const Path & substituter,
if (run.disabled) return;
string s = "info ";
foreach (PathSet::const_iterator, i, paths)
if (infos.find(*i) == infos.end()) { s += *i; s += " "; }
for (auto & i : paths)
if (infos.find(i) == infos.end()) { s += i; s += " "; }
writeLine(run.to, s);
while (true) {
@@ -1258,9 +1272,9 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos)
{
PathSet todo = paths;
foreach (Paths::iterator, i, settings.substituters) {
for (auto & i : settings.substituters) {
if (todo.empty()) break;
querySubstitutablePathInfos(*i, todo, infos);
querySubstitutablePathInfos(i, todo, infos);
}
}
@@ -1290,30 +1304,30 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
SQLiteTxn txn(db);
PathSet paths;
foreach (ValidPathInfos::const_iterator, i, infos) {
assert(i->hash.type == htSHA256);
if (isValidPath_(i->path))
updatePathInfo(*i);
for (auto & i : infos) {
assert(i.hash.type == htSHA256);
if (isValidPath_(i.path))
updatePathInfo(i);
else
addValidPath(*i, false);
paths.insert(i->path);
addValidPath(i, false);
paths.insert(i.path);
}
foreach (ValidPathInfos::const_iterator, i, infos) {
unsigned long long referrer = queryValidPathId(i->path);
foreach (PathSet::iterator, j, i->references)
addReference(referrer, queryValidPathId(*j));
for (auto & i : infos) {
unsigned long long referrer = queryValidPathId(i.path);
for (auto & j : i.references)
addReference(referrer, queryValidPathId(j));
}
/* Check that the derivation outputs are correct. We can't do
this in addValidPath() above, because the references might
not be valid yet. */
foreach (ValidPathInfos::const_iterator, i, infos)
if (isDerivation(i->path)) {
for (auto & i : infos)
if (isDerivation(i.path)) {
// FIXME: inefficient; we already loaded the
// derivation in addValidPath().
Derivation drv = readDerivation(i->path);
checkDerivationOutputs(i->path, drv);
Derivation drv = readDerivation(i.path);
checkDerivationOutputs(i.path, drv);
}
/* Do a topological sort of the paths. This will throw an
@@ -1402,7 +1416,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
}
Path LocalStore::addToStore(const Path & _srcPath,
Path LocalStore::addToStore(const string & name, const Path & _srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
{
Path srcPath(absPath(_srcPath));
@@ -1417,7 +1431,7 @@ Path LocalStore::addToStore(const Path & _srcPath,
else
sink.s = readFile(srcPath);
return addToStoreFromDump(sink.s, baseNameOf(srcPath), recursive, hashAlgo, repair);
return addToStoreFromDump(sink.s, name, recursive, hashAlgo, repair);
}
@@ -1496,7 +1510,7 @@ void LocalStore::exportPath(const Path & path, bool sign,
{
assertStorePath(path);
printMsg(lvlInfo, format("exporting path %1%") % path);
printMsg(lvlTalkative, format("exporting path %1%") % path);
if (!isValidPath(path))
throw Error(format("path %1% is not valid") % path);
@@ -1514,22 +1528,14 @@ void LocalStore::exportPath(const Path & path, bool sign,
throw Error(format("hash of path %1% has changed from %2% to %3%!") % path
% printHash(storedHash) % printHash(hash));
writeInt(EXPORT_MAGIC, hashAndWriteSink);
writeString(path, hashAndWriteSink);
PathSet references;
queryReferences(path, references);
writeStrings(references, hashAndWriteSink);
Path deriver = queryDeriver(path);
writeString(deriver, hashAndWriteSink);
hashAndWriteSink << EXPORT_MAGIC << path << references << queryDeriver(path);
if (sign) {
Hash hash = hashAndWriteSink.currentHash();
writeInt(1, hashAndWriteSink);
Path tmpDir = createTempDir();
AutoDelete delTmp(tmpDir);
Path hashFile = tmpDir + "/hash";
@@ -1547,10 +1553,10 @@ void LocalStore::exportPath(const Path & path, bool sign,
args.push_back(hashFile);
string signature = runProgram(OPENSSL_PATH, true, args);
writeString(signature, hashAndWriteSink);
hashAndWriteSink << 1 << signature;
} else
writeInt(0, hashAndWriteSink);
hashAndWriteSink << 0;
}
@@ -1607,6 +1613,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
Path dstPath = readStorePath(hashAndReadSource);
printMsg(lvlTalkative, format("importing path %1%") % dstPath);
PathSet references = readStorePaths<PathSet>(hashAndReadSource);
Path deriver = readString(hashAndReadSource);
@@ -1747,8 +1755,8 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
PathSet validPaths2 = queryAllValidPaths(), validPaths, done;
foreach (PathSet::iterator, i, validPaths2)
verifyPath(*i, store, done, validPaths, repair, errors);
for (auto & i : validPaths2)
verifyPath(i, store, done, validPaths, repair, errors);
/* Release the GC lock so that checking content hashes (which can
take ages) doesn't block the GC or builds. */
@@ -1760,33 +1768,33 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
Hash nullHash(htSHA256);
foreach (PathSet::iterator, i, validPaths) {
for (auto & i : validPaths) {
try {
ValidPathInfo info = queryPathInfo(*i);
ValidPathInfo info = queryPathInfo(i);
/* Check the content hash (optionally - slow). */
printMsg(lvlTalkative, format("checking contents of %1%") % *i);
HashResult current = hashPath(info.hash.type, *i);
printMsg(lvlTalkative, format("checking contents of %1%") % i);
HashResult current = hashPath(info.hash.type, i);
if (info.hash != nullHash && info.hash != current.first) {
printMsg(lvlError, format("path %1% was modified! "
"expected hash %2%, got %3%")
% *i % printHash(info.hash) % printHash(current.first));
if (repair) repairPath(*i); else errors = true;
% i % printHash(info.hash) % printHash(current.first));
if (repair) repairPath(i); else errors = true;
} else {
bool update = false;
/* Fill in missing hashes. */
if (info.hash == nullHash) {
printMsg(lvlError, format("fixing missing hash on %1%") % *i);
printMsg(lvlError, format("fixing missing hash on %1%") % i);
info.hash = current.first;
update = true;
}
/* Fill in missing narSize fields (from old stores). */
if (info.narSize == 0) {
printMsg(lvlError, format("updating size field on %1% to %2%") % *i % current.second);
printMsg(lvlError, format("updating size field on %1% to %2%") % i % current.second);
info.narSize = current.second;
update = true;
}
@@ -1798,7 +1806,7 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
} catch (Error & e) {
/* It's possible that the path got GC'ed, so ignore
errors on invalid paths. */
if (isValidPath(*i))
if (isValidPath(i))
printMsg(lvlError, format("error: %1%") % e.msg());
else
printMsg(lvlError, format("warning: %1%") % e.msg());
@@ -1830,10 +1838,10 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
first, then we can invalidate this path as well. */
bool canInvalidate = true;
PathSet referrers; queryReferrers(path, referrers);
foreach (PathSet::iterator, i, referrers)
if (*i != path) {
verifyPath(*i, store, done, validPaths, repair, errors);
if (validPaths.find(*i) != validPaths.end())
for (auto & i : referrers)
if (i != path) {
verifyPath(i, store, done, validPaths, repair, errors);
if (validPaths.find(i) != validPaths.end())
canInvalidate = false;
}
@@ -1911,12 +1919,12 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
/* Parse it. */
Strings lines = tokenizeString<Strings>(info, "\n");
foreach (Strings::iterator, i, lines) {
string::size_type p = i->find(':');
for (auto & i : lines) {
string::size_type p = i.find(':');
if (p == string::npos)
throw Error(format("corrupt line in %1%: %2%") % infoFile % *i);
string name(*i, 0, p);
string value(*i, p + 2);
throw Error(format("corrupt line in %1%: %2%") % infoFile % i);
string name(i, 0, p);
string value(i, p + 2);
if (name == "References") {
Strings refs = tokenizeString<Strings>(value, " ");
res.references = PathSet(refs.begin(), refs.end());
@@ -1946,18 +1954,18 @@ void LocalStore::upgradeStore6()
SQLiteTxn txn(db);
foreach (PathSet::iterator, i, validPaths) {
addValidPath(queryPathInfoOld(*i), false);
for (auto & i : validPaths) {
addValidPath(queryPathInfoOld(i), false);
std::cerr << ".";
}
std::cerr << "|";
foreach (PathSet::iterator, i, validPaths) {
ValidPathInfo info = queryPathInfoOld(*i);
unsigned long long referrer = queryValidPathId(*i);
foreach (PathSet::iterator, j, info.references)
addReference(referrer, queryValidPathId(*j));
for (auto & i : validPaths) {
ValidPathInfo info = queryPathInfoOld(i);
unsigned long long referrer = queryValidPathId(i);
for (auto & j : info.references)
addReference(referrer, queryValidPathId(j));
std::cerr << ".";
}
+4 -1
View File
@@ -130,7 +130,7 @@ public:
void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos);
Path addToStore(const Path & srcPath,
Path addToStore(const string & name, const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, bool repair = false);
@@ -151,6 +151,9 @@ public:
void buildPaths(const PathSet & paths, BuildMode buildMode);
BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
BuildMode buildMode) override;
void ensurePath(const Path & path);
void addTempRoot(const Path & path);
+3 -2
View File
@@ -8,13 +8,14 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
libstore_LIBS = libutil libformat
libstore_LDFLAGS = -lsqlite3 -lbz2
libstore_LDFLAGS = -lsqlite3 -lbz2 -lcurl
ifeq ($(OS), SunOS)
libstore_LDFLAGS += -lsocket
endif
libstore_CXXFLAGS = \
-DNIX_PREFIX=\"$(prefix)\" \
-DNIX_STORE_DIR=\"$(storedir)\" \
-DNIX_DATA_DIR=\"$(datadir)\" \
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
@@ -22,7 +23,7 @@ libstore_CXXFLAGS = \
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
-DNIX_BIN_DIR=\"$(bindir)\" \
-DDEFAULT_CHROOT_DIRS="\"$(DEFAULT_CHROOT_DIRS)\""
-DBASH_PATH="\"$(bash)\""
$(d)/local-store.cc: $(d)/schema.sql.hh
+45 -45
View File
@@ -28,15 +28,15 @@ void computeFSClosure(StoreAPI & store, const Path & path,
if (includeOutputs) {
PathSet derivers = store.queryValidDerivers(path);
foreach (PathSet::iterator, i, derivers)
edges.insert(*i);
for (auto & i : derivers)
edges.insert(i);
}
if (includeDerivers && isDerivation(path)) {
PathSet outputs = store.queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (store.isValidPath(*i) && store.queryDeriver(*i) == path)
edges.insert(*i);
for (auto & i : outputs)
if (store.isValidPath(i) && store.queryDeriver(i) == path)
edges.insert(i);
}
} else {
@@ -44,8 +44,8 @@ void computeFSClosure(StoreAPI & store, const Path & path,
if (includeOutputs && isDerivation(path)) {
PathSet outputs = store.queryDerivationOutputs(path);
foreach (PathSet::iterator, i, outputs)
if (store.isValidPath(*i)) edges.insert(*i);
for (auto & i : outputs)
if (store.isValidPath(i)) edges.insert(i);
}
if (includeDerivers) {
@@ -54,15 +54,15 @@ void computeFSClosure(StoreAPI & store, const Path & path,
}
}
foreach (PathSet::iterator, i, edges)
computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
for (auto & i : edges)
computeFSClosure(store, i, paths, flipDirection, includeOutputs, includeDerivers);
}
Path findOutput(const Derivation & drv, string id)
{
foreach (DerivationOutputs::const_iterator, i, drv.outputs)
if (i->first == id) return i->second.path;
for (auto & i : drv.outputs)
if (i.first == id) return i.second.path;
throw Error(format("derivation has no output %1%") % id);
}
@@ -98,36 +98,36 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
PathSet query, todoDrv, todoNonDrv;
foreach (PathSet::iterator, i, todo) {
if (done.find(*i) != done.end()) continue;
done.insert(*i);
for (auto & i : todo) {
if (done.find(i) != done.end()) continue;
done.insert(i);
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
if (isDerivation(i2.first)) {
if (!store.isValidPath(i2.first)) {
// FIXME: we could try to substitute p.
unknown.insert(*i);
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);
for (auto & 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 && !willBuildLocally(drv))
todoDrv.insert(i);
if (settings.useSubstitutes && substitutesAllowed(drv))
query.insert(invalid.begin(), invalid.end());
}
else {
if (store.isValidPath(*i)) continue;
query.insert(*i);
todoNonDrv.insert(*i);
if (store.isValidPath(i)) continue;
query.insert(i);
todoNonDrv.insert(i);
}
}
@@ -136,22 +136,22 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
SubstitutablePathInfos infos;
store.querySubstitutablePathInfos(query, infos);
foreach (PathSet::iterator, i, todoDrv) {
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
for (auto & i : todoDrv) {
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
// FIXME: cache this
Derivation drv = derivationFromPath(store, i2.first);
PathSet outputs;
bool mustBuild = false;
if (settings.useSubstitutes && !willBuildLocally(drv)) {
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())
if (settings.useSubstitutes && substitutesAllowed(drv)) {
for (auto & 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);
outputs.insert(j.second.path);
}
}
} else
@@ -160,22 +160,22 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
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));
for (auto & 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);
for (auto & i : todoNonDrv) {
done.insert(i);
SubstitutablePathInfos::iterator info = infos.find(i);
if (info != infos.end()) {
willSubstitute.insert(*i);
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);
unknown.insert(i);
}
}
}
@@ -196,11 +196,11 @@ static void dfsVisit(StoreAPI & store, const PathSet & paths,
if (store.isValidPath(path))
store.queryReferences(path, references);
foreach (PathSet::iterator, i, references)
for (auto & i : references)
/* Don't traverse into paths that don't exist. That can
happen due to substitutes for non-existent paths. */
if (*i != path && paths.find(*i) != paths.end())
dfsVisit(store, paths, *i, visited, sorted, parents);
if (i != path && paths.find(i) != paths.end())
dfsVisit(store, paths, i, visited, sorted, parents);
sorted.push_front(path);
parents.erase(path);
@@ -211,8 +211,8 @@ Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
{
Paths sorted;
PathSet visited, parents;
foreach (PathSet::const_iterator, i, paths)
dfsVisit(store, paths, *i, visited, sorted, parents);
for (auto & i : paths)
dfsVisit(store, paths, i, visited, sorted, parents);
return sorted;
}
+3 -1
View File
@@ -32,7 +32,9 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize);
bool willBuildLocally(const Derivation & drv);
bool willBuildLocally(const BasicDerivation & drv);
bool substitutesAllowed(const BasicDerivation & drv);
}
+7 -7
View File
@@ -99,8 +99,8 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectoryIgnoringInodes(path, inodeHash);
foreach (Strings::iterator, i, names)
optimisePath_(stats, path + "/" + *i, inodeHash);
for (auto & i : names)
optimisePath_(stats, path + "/" + i, inodeHash);
return;
}
@@ -218,11 +218,11 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
PathSet paths = queryAllValidPaths();
InodeHash inodeHash = loadInodeHash();
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);
optimisePath_(stats, *i, inodeHash);
for (auto & i : paths) {
addTempRoot(i);
if (!isValidPath(i)) continue; /* path was GC'ed, probably */
startNest(nest, lvlChatty, format("hashing files in %1%") % i);
optimisePath_(stats, i, inodeHash);
}
}
+13 -14
View File
@@ -60,7 +60,7 @@ bool lockFile(int fd, LockType lockType, bool wait)
while (fcntl(fd, F_SETLK, &lock) != 0) {
checkInterrupt();
if (errno == EACCES || errno == EAGAIN) return false;
if (errno != EINTR)
if (errno != EINTR)
throw SysError(format("acquiring/releasing lock"));
}
}
@@ -94,7 +94,7 @@ bool PathLocks::lockPaths(const PathSet & _paths,
const string & waitMsg, bool wait)
{
assert(fds.empty());
/* Note that `fds' is built incrementally so that the destructor
will only release those locks that we have already acquired. */
@@ -102,11 +102,10 @@ bool PathLocks::lockPaths(const PathSet & _paths,
the same order, thus preventing deadlocks. */
Paths paths(_paths.begin(), _paths.end());
paths.sort();
/* Acquire the lock for each path. */
foreach (Paths::iterator, i, paths) {
for (auto & path : paths) {
checkInterrupt();
Path path = *i;
Path lockPath = path + ".lock";
debug(format("locking path %1%") % path);
@@ -115,11 +114,11 @@ bool PathLocks::lockPaths(const PathSet & _paths,
throw Error("deadlock: trying to re-acquire self-held lock");
AutoCloseFD fd;
while (1) {
/* Open/create the lock file. */
fd = openLockFile(lockPath, true);
fd = openLockFile(lockPath, true);
/* Acquire an exclusive lock. */
if (!lockFile(fd, ltWrite, false)) {
@@ -168,15 +167,15 @@ PathLocks::~PathLocks()
void PathLocks::unlock()
{
foreach (list<FDPair>::iterator, i, fds) {
if (deletePaths) deleteLockFile(i->second, i->first);
for (auto & i : fds) {
if (deletePaths) deleteLockFile(i.second, i.first);
lockedPaths.erase(i->second);
if (close(i->first) == -1)
lockedPaths.erase(i.second);
if (close(i.first) == -1)
printMsg(lvlError,
format("error (ignored): cannot close lock file on %1%") % i->second);
format("error (ignored): cannot close lock file on %1%") % i.second);
debug(format("lock released on %1%") % i->second);
debug(format("lock released on %1%") % i.second);
}
fds.clear();
@@ -195,5 +194,5 @@ bool pathIsLockedByMe(const Path & path)
return lockedPaths.find(lockPath) != lockedPaths.end();
}
}
@@ -80,7 +80,25 @@ Path createGeneration(Path profile, Path outPath)
previous ones. */
int dummy;
Generations gens = findGenerations(profile, dummy);
unsigned int num = gens.size() > 0 ? gens.back().number : 0;
unsigned int num;
if (gens.size() > 0) {
Generation last = gens.back();
if (readLink(last.path) == outPath) {
/* We only create a new generation symlink if it differs
from the last one.
This helps keeping gratuitous installs/rebuilds from piling
up uncontrolled numbers of generations, cluttering up the
UI like grub. */
return last.path;
}
num = gens.back().number;
} else {
num = 0;
}
/* Create the new generation. Note that addPermRoot() blocks if
the garbage collector is running to prevent the stuff we've
@@ -111,21 +129,94 @@ void deleteGeneration(const Path & profile, unsigned int gen)
}
static void deleteGeneration2(const Path & profile, unsigned int gen, bool dryRun)
{
if (dryRun)
printMsg(lvlInfo, format("would remove generation %1%") % gen);
else {
printMsg(lvlInfo, format("removing generation %1%") % gen);
deleteGeneration(profile, gen);
}
}
void deleteGenerations(const Path & profile, const std::set<unsigned int> & gensToDelete, bool dryRun)
{
PathLocks lock;
lockProfile(lock, profile);
int curGen;
Generations gens = findGenerations(profile, curGen);
if (gensToDelete.find(curGen) != gensToDelete.end())
throw Error(format("cannot delete current generation of profile %1%") % profile);
for (auto & i : gens) {
if (gensToDelete.find(i.number) == gensToDelete.end()) continue;
deleteGeneration2(profile, i.number, dryRun);
}
}
void deleteOldGenerations(const Path & profile, bool dryRun)
{
PathLocks lock;
lockProfile(lock, profile);
int curGen;
Generations gens = findGenerations(profile, curGen);
for (auto & i : gens)
if (i.number != curGen)
deleteGeneration2(profile, i.number, dryRun);
}
void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun)
{
PathLocks lock;
lockProfile(lock, profile);
int curGen;
Generations gens = findGenerations(profile, curGen);
bool canDelete = false;
for (auto i = gens.rbegin(); i != gens.rend(); ++i)
if (canDelete) {
assert(i->creationTime < t);
if (i->number != curGen)
deleteGeneration2(profile, i->number, dryRun);
} else if (i->creationTime < t) {
/* We may now start deleting generations, but we don't
delete this generation yet, because this generation was
still the one that was active at the requested point in
time. */
canDelete = true;
}
}
void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, bool dryRun)
{
time_t curTime = time(0);
string strDays = string(timeSpec, 0, timeSpec.size() - 1);
int days;
if (!string2Int(strDays, days) || days < 1)
throw Error(format("invalid number of days specifier %1%") % timeSpec);
time_t oldTime = curTime - days * 24 * 3600;
deleteGenerationsOlderThan(profile, oldTime, dryRun);
}
void switchLink(Path link, Path target)
{
/* Hacky. */
if (dirOf(target) == dirOf(link)) target = baseNameOf(target);
Path tmp = canonPath(dirOf(link) + "/.new_" + baseNameOf(link));
createSymlink(target, tmp);
/* The rename() system call is supposed to be essentially atomic
on Unix. That is, if we have links `current -> X' and
`new_current -> Y', and we rename new_current to current, a
process accessing current will see X or Y, but never a
file-not-found or other error condition. This is sufficient to
atomically switch user environments. */
if (rename(tmp.c_str(), link.c_str()) != 0)
throw SysError(format("renaming %1% to %2%") % tmp % link);
replaceSymlink(target, link);
}
@@ -30,11 +30,19 @@ typedef list<Generation> Generations;
/* Returns the list of currently present generations for the specified
profile, sorted by generation number. */
Generations findGenerations(Path profile, int & curGen);
Path createGeneration(Path profile, Path outPath);
void deleteGeneration(const Path & profile, unsigned int gen);
void deleteGenerations(const Path & profile, const std::set<unsigned int> & gensToDelete, bool dryRun);
void deleteOldGenerations(const Path & profile, bool dryRun);
void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun);
void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, bool dryRun);
void switchLink(Path link, Path target);
/* Ensure exclusive access to a profile. Any command that modifies
+10 -10
View File
@@ -13,7 +13,7 @@ namespace nix {
static unsigned int refLength = 32; /* characters */
static void search(const unsigned char * s, unsigned int len,
static void search(const unsigned char * s, unsigned int len,
StringSet & hashes, StringSet & seen)
{
static bool initialised = false;
@@ -24,7 +24,7 @@ static void search(const unsigned char * s, unsigned int len,
isBase32[(unsigned char) base32Chars[i]] = true;
initialised = true;
}
for (unsigned int i = 0; i + refLength <= len; ) {
int j;
bool match = true;
@@ -56,7 +56,7 @@ struct RefScanSink : Sink
string tail;
RefScanSink() : hashSink(htSHA256) { }
void operator () (const unsigned char * data, size_t len);
};
@@ -89,17 +89,17 @@ PathSet scanForReferences(const string & path,
/* For efficiency (and a higher hit rate), just search for the
hash part of the file name. (This assumes that all references
have the form `HASH-bla'). */
foreach (PathSet::const_iterator, i, refs) {
string baseName = baseNameOf(*i);
for (auto & i : refs) {
string baseName = baseNameOf(i);
string::size_type pos = baseName.find('-');
if (pos == string::npos)
throw Error(format("bad reference %1%") % *i);
throw Error(format("bad reference %1%") % i);
string s = string(baseName, 0, pos);
assert(s.size() == refLength);
assert(backMap.find(s) == backMap.end());
// parseHash(htSHA256, s);
sink.hashes.insert(s);
backMap[s] = *i;
backMap[s] = i;
}
/* Look for the hashes in the NAR dump of the path. */
@@ -107,14 +107,14 @@ PathSet scanForReferences(const string & path,
/* Map the hashes found back to their store paths. */
PathSet found;
foreach (StringSet::iterator, i, sink.seen) {
for (auto & i : sink.seen) {
std::map<string, Path>::iterator j;
if ((j = backMap.find(*i)) == backMap.end()) abort();
if ((j = backMap.find(i)) == backMap.end()) abort();
found.insert(j->second);
}
hash = sink.hashSink.finish();
return found;
}
+95 -109
View File
@@ -5,6 +5,7 @@
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"
#include "derivations.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -31,7 +32,7 @@ Path readStorePath(Source & from)
template<class T> T readStorePaths(Source & from)
{
T paths = readStrings<T>(from);
foreach (typename T::iterator, i, paths) assertStorePath(*i);
for (auto & i : paths) assertStorePath(i);
return paths;
}
@@ -63,7 +64,7 @@ void RemoteStore::openConnection(bool reserveSpace)
/* Send the magic greeting, check for the reply. */
try {
writeInt(WORKER_MAGIC_1, to);
to << WORKER_MAGIC_1;
to.flush();
unsigned int magic = readInt(from);
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
@@ -71,19 +72,18 @@ void RemoteStore::openConnection(bool reserveSpace)
daemonVersion = readInt(from);
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
throw Error("Nix daemon protocol version not supported");
writeInt(PROTOCOL_VERSION, to);
to << PROTOCOL_VERSION;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
if (cpu != -1) {
writeInt(1, to);
writeInt(cpu, to);
} else
writeInt(0, to);
if (cpu != -1)
to << 1 << cpu;
else
to << 0;
}
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
writeInt(reserveSpace, to);
to << reserveSpace;
processStderr();
}
@@ -141,35 +141,31 @@ RemoteStore::~RemoteStore()
void RemoteStore::setOptions()
{
writeInt(wopSetOptions, to);
writeInt(settings.keepFailed, to);
writeInt(settings.keepGoing, to);
writeInt(settings.tryFallback, to);
writeInt(verbosity, to);
writeInt(settings.maxBuildJobs, to);
writeInt(settings.maxSilentTime, to);
to << wopSetOptions
<< settings.keepFailed
<< settings.keepGoing
<< settings.tryFallback
<< verbosity
<< settings.maxBuildJobs
<< settings.maxSilentTime;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
writeInt(settings.useBuildHook, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
writeInt(settings.buildVerbosity, to);
writeInt(logType, to);
writeInt(settings.printBuildTrace, to);
}
to << settings.useBuildHook;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4)
to << settings.buildVerbosity
<< logType
<< settings.printBuildTrace;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
writeInt(settings.buildCores, to);
to << settings.buildCores;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
writeInt(settings.useSubstitutes, to);
to << settings.useSubstitutes;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) {
Settings::SettingsMap overrides = settings.getOverrides();
if (overrides["ssh-auth-sock"] == "")
overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK");
writeInt(overrides.size(), to);
foreach (Settings::SettingsMap::iterator, i, overrides) {
writeString(i->first, to);
writeString(i->second, to);
}
to << overrides.size();
for (auto & i : overrides)
to << i.first << i.second;
}
processStderr();
@@ -179,8 +175,7 @@ void RemoteStore::setOptions()
bool RemoteStore::isValidPath(const Path & path)
{
openConnection();
writeInt(wopIsValidPath, to);
writeString(path, to);
to << wopIsValidPath << path;
processStderr();
unsigned int reply = readInt(from);
return reply != 0;
@@ -192,12 +187,11 @@ PathSet RemoteStore::queryValidPaths(const PathSet & paths)
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
PathSet res;
foreach (PathSet::const_iterator, i, paths)
if (isValidPath(*i)) res.insert(*i);
for (auto & i : paths)
if (isValidPath(i)) res.insert(i);
return res;
} else {
writeInt(wopQueryValidPaths, to);
writeStrings(paths, to);
to << wopQueryValidPaths << paths;
processStderr();
return readStorePaths<PathSet>(from);
}
@@ -207,7 +201,7 @@ PathSet RemoteStore::queryValidPaths(const PathSet & paths)
PathSet RemoteStore::queryAllValidPaths()
{
openConnection();
writeInt(wopQueryAllValidPaths, to);
to << wopQueryAllValidPaths;
processStderr();
return readStorePaths<PathSet>(from);
}
@@ -218,16 +212,14 @@ PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths)
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
PathSet res;
foreach (PathSet::const_iterator, i, paths) {
writeInt(wopHasSubstitutes, to);
writeString(*i, to);
for (auto & i : paths) {
to << wopHasSubstitutes << i;
processStderr();
if (readInt(from)) res.insert(*i);
if (readInt(from)) res.insert(i);
}
return res;
} else {
writeInt(wopQuerySubstitutablePaths, to);
writeStrings(paths, to);
to << wopQuerySubstitutablePaths << paths;
processStderr();
return readStorePaths<PathSet>(from);
}
@@ -245,10 +237,9 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
foreach (PathSet::const_iterator, i, paths) {
for (auto & i : paths) {
SubstitutablePathInfo info;
writeInt(wopQuerySubstitutablePathInfo, to);
writeString(*i, to);
to << wopQuerySubstitutablePathInfo << i;
processStderr();
unsigned int reply = readInt(from);
if (reply == 0) continue;
@@ -257,13 +248,12 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
info.references = readStorePaths<PathSet>(from);
info.downloadSize = readLongLong(from);
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
infos[*i] = info;
infos[i] = info;
}
} else {
writeInt(wopQuerySubstitutablePathInfos, to);
writeStrings(paths, to);
to << wopQuerySubstitutablePathInfos << paths;
processStderr();
unsigned int count = readInt(from);
for (unsigned int n = 0; n < count; n++) {
@@ -283,8 +273,7 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
{
openConnection();
writeInt(wopQueryPathInfo, to);
writeString(path, to);
to << wopQueryPathInfo << path;
processStderr();
ValidPathInfo info;
info.path = path;
@@ -301,8 +290,7 @@ ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
Hash RemoteStore::queryPathHash(const Path & path)
{
openConnection();
writeInt(wopQueryPathHash, to);
writeString(path, to);
to << wopQueryPathHash << path;
processStderr();
string hash = readString(from);
return parseHash(htSHA256, hash);
@@ -313,8 +301,7 @@ void RemoteStore::queryReferences(const Path & path,
PathSet & references)
{
openConnection();
writeInt(wopQueryReferences, to);
writeString(path, to);
to << wopQueryReferences << path;
processStderr();
PathSet references2 = readStorePaths<PathSet>(from);
references.insert(references2.begin(), references2.end());
@@ -325,8 +312,7 @@ void RemoteStore::queryReferrers(const Path & path,
PathSet & referrers)
{
openConnection();
writeInt(wopQueryReferrers, to);
writeString(path, to);
to << wopQueryReferrers << path;
processStderr();
PathSet referrers2 = readStorePaths<PathSet>(from);
referrers.insert(referrers2.begin(), referrers2.end());
@@ -336,8 +322,7 @@ void RemoteStore::queryReferrers(const Path & path,
Path RemoteStore::queryDeriver(const Path & path)
{
openConnection();
writeInt(wopQueryDeriver, to);
writeString(path, to);
to << wopQueryDeriver << path;
processStderr();
Path drvPath = readString(from);
if (drvPath != "") assertStorePath(drvPath);
@@ -348,8 +333,7 @@ Path RemoteStore::queryDeriver(const Path & path)
PathSet RemoteStore::queryValidDerivers(const Path & path)
{
openConnection();
writeInt(wopQueryValidDerivers, to);
writeString(path, to);
to << wopQueryValidDerivers << path;
processStderr();
return readStorePaths<PathSet>(from);
}
@@ -358,8 +342,7 @@ PathSet RemoteStore::queryValidDerivers(const Path & path)
PathSet RemoteStore::queryDerivationOutputs(const Path & path)
{
openConnection();
writeInt(wopQueryDerivationOutputs, to);
writeString(path, to);
to << wopQueryDerivationOutputs << path;
processStderr();
return readStorePaths<PathSet>(from);
}
@@ -368,8 +351,7 @@ PathSet RemoteStore::queryDerivationOutputs(const Path & path)
PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
{
openConnection();
writeInt(wopQueryDerivationOutputNames, to);
writeString(path, to);
to << wopQueryDerivationOutputNames << path;
processStderr();
return readStrings<PathSet>(from);
}
@@ -378,8 +360,7 @@ PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
Path RemoteStore::queryPathFromHashPart(const string & hashPart)
{
openConnection();
writeInt(wopQueryPathFromHashPart, to);
writeString(hashPart, to);
to << wopQueryPathFromHashPart << hashPart;
processStderr();
Path path = readString(from);
if (!path.empty()) assertStorePath(path);
@@ -387,7 +368,7 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart)
}
Path RemoteStore::addToStore(const Path & _srcPath,
Path RemoteStore::addToStore(const string & name, const Path & _srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
@@ -396,12 +377,10 @@ Path RemoteStore::addToStore(const Path & _srcPath,
Path srcPath(absPath(_srcPath));
writeInt(wopAddToStore, to);
writeString(baseNameOf(srcPath), to);
/* backwards compatibility hack */
writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
writeInt(recursive ? 1 : 0, to);
writeString(printHashType(hashAlgo), to);
to << wopAddToStore << name
<< ((hashAlgo == htSHA256 && recursive) ? 0 : 1) /* backwards compatibility hack */
<< (recursive ? 1 : 0)
<< printHashType(hashAlgo);
try {
to.written = 0;
@@ -429,10 +408,7 @@ Path RemoteStore::addTextToStore(const string & name, const string & s,
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);
to << wopAddTextToStore << name << s << references;
processStderr();
return readStorePath(from);
@@ -443,9 +419,7 @@ void RemoteStore::exportPath(const Path & path, bool sign,
Sink & sink)
{
openConnection();
writeInt(wopExportPath, to);
writeString(path, to);
writeInt(sign ? 1 : 0, to);
to << wopExportPath << path << (sign ? 1 : 0);
processStderr(&sink); /* sink receives the actual data */
readInt(from);
}
@@ -454,7 +428,7 @@ void RemoteStore::exportPath(const Path & path, bool sign,
Paths RemoteStore::importPaths(bool requireSignature, Source & source)
{
openConnection();
writeInt(wopImportPaths, to);
to << wopImportPaths;
/* We ignore requireSignature, since the worker forces it to true
anyway. */
processStderr(0, &source);
@@ -466,27 +440,40 @@ void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
{
if (buildMode != bmNormal) throw Error("repairing or checking is not supported when building through the Nix daemon");
openConnection();
writeInt(wopBuildPaths, to);
to << wopBuildPaths;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
writeStrings(drvPaths, to);
to << drvPaths;
else {
/* For backwards compatibility with old daemons, strip output
identifiers. */
PathSet drvPaths2;
foreach (PathSet::const_iterator, i, drvPaths)
drvPaths2.insert(string(*i, 0, i->find('!')));
writeStrings(drvPaths2, to);
for (auto & i : drvPaths)
drvPaths2.insert(string(i, 0, i.find('!')));
to << drvPaths2;
}
processStderr();
readInt(from);
}
BuildResult RemoteStore::buildDerivation(const Path & drvPath, const BasicDerivation & drv,
BuildMode buildMode)
{
openConnection();
to << wopBuildDerivation << drvPath << drv << buildMode;
processStderr();
BuildResult res;
unsigned int status;
from >> status >> res.errorMsg;
res.status = (BuildResult::Status) status;
return res;
}
void RemoteStore::ensurePath(const Path & path)
{
openConnection();
writeInt(wopEnsurePath, to);
writeString(path, to);
to << wopEnsurePath << path;
processStderr();
readInt(from);
}
@@ -495,8 +482,7 @@ void RemoteStore::ensurePath(const Path & path)
void RemoteStore::addTempRoot(const Path & path)
{
openConnection();
writeInt(wopAddTempRoot, to);
writeString(path, to);
to << wopAddTempRoot << path;
processStderr();
readInt(from);
}
@@ -505,8 +491,7 @@ void RemoteStore::addTempRoot(const Path & path)
void RemoteStore::addIndirectRoot(const Path & path)
{
openConnection();
writeInt(wopAddIndirectRoot, to);
writeString(path, to);
to << wopAddIndirectRoot << path;
processStderr();
readInt(from);
}
@@ -515,7 +500,7 @@ void RemoteStore::addIndirectRoot(const Path & path)
void RemoteStore::syncWithGC()
{
openConnection();
writeInt(wopSyncWithGC, to);
to << wopSyncWithGC;
processStderr();
readInt(from);
}
@@ -524,7 +509,7 @@ void RemoteStore::syncWithGC()
Roots RemoteStore::findRoots()
{
openConnection();
writeInt(wopFindRoots, to);
to << wopFindRoots;
processStderr();
unsigned int count = readInt(from);
Roots result;
@@ -541,17 +526,11 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
{
openConnection(false);
writeInt(wopCollectGarbage, to);
writeInt(options.action, to);
writeStrings(options.pathsToDelete, to);
writeInt(options.ignoreLiveness, to);
writeLongLong(options.maxFreed, to);
writeInt(0, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) {
to << wopCollectGarbage << options.action << options.pathsToDelete << options.ignoreLiveness
<< options.maxFreed << 0;
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5)
/* removed options */
writeInt(0, to);
writeInt(0, to);
}
to << 0 << 0;
processStderr();
@@ -564,7 +543,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
PathSet RemoteStore::queryFailedPaths()
{
openConnection();
writeInt(wopQueryFailedPaths, to);
to << wopQueryFailedPaths;
processStderr();
return readStorePaths<PathSet>(from);
}
@@ -573,8 +552,7 @@ PathSet RemoteStore::queryFailedPaths()
void RemoteStore::clearFailedPaths(const PathSet & paths)
{
openConnection();
writeInt(wopClearFailedPaths, to);
writeStrings(paths, to);
to << wopClearFailedPaths << paths;
processStderr();
readInt(from);
}
@@ -582,11 +560,19 @@ void RemoteStore::clearFailedPaths(const PathSet & paths)
void RemoteStore::optimiseStore()
{
openConnection();
writeInt(wopOptimiseStore, to);
to << wopOptimiseStore;
processStderr();
readInt(from);
}
bool RemoteStore::verifyStore(bool checkContents, bool repair)
{
openConnection();
to << wopVerifyStore << checkContents << repair;
processStderr();
return readInt(from) != 0;
}
void RemoteStore::processStderr(Sink * sink, Source * source)
{
to.flush();
+18 -14
View File
@@ -21,15 +21,15 @@ public:
RemoteStore();
~RemoteStore();
/* Implementations of abstract store API methods. */
bool isValidPath(const Path & path);
PathSet queryValidPaths(const PathSet & paths);
PathSet queryAllValidPaths();
ValidPathInfo queryPathInfo(const Path & path);
Hash queryPathHash(const Path & path);
@@ -39,21 +39,21 @@ public:
void queryReferrers(const Path & path, PathSet & referrers);
Path queryDeriver(const Path & path);
PathSet queryValidDerivers(const Path & path);
PathSet queryDerivationOutputs(const Path & path);
StringSet queryDerivationOutputNames(const Path & path);
Path queryPathFromHashPart(const string & hashPart);
PathSet querySubstitutablePaths(const PathSet & paths);
void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos);
Path addToStore(const Path & srcPath,
Path addToStore(const string & name, const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, bool repair = false);
@@ -64,27 +64,31 @@ public:
Sink & sink);
Paths importPaths(bool requireSignature, Source & source);
void buildPaths(const PathSet & paths, BuildMode buildMode);
BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
BuildMode buildMode) override;
void ensurePath(const Path & path);
void addTempRoot(const Path & path);
void addIndirectRoot(const Path & path);
void syncWithGC();
Roots findRoots();
void collectGarbage(const GCOptions & options, GCResults & results);
PathSet queryFailedPaths();
void clearFailedPaths(const PathSet & paths);
void optimiseStore();
bool verifyStore(bool checkContents, bool repair);
private:
AutoCloseFD fdSocket;
FdSink to;
+24 -34
View File
@@ -2,20 +2,10 @@
#include "globals.hh"
#include "util.hh"
#include <climits>
namespace nix {
GCOptions::GCOptions()
{
action = gcDeleteDead;
ignoreLiveness = false;
maxFreed = ULLONG_MAX;
}
bool isInStore(const Path & path)
{
return isInDir(path, settings.nixStore);
@@ -82,14 +72,14 @@ void checkStoreName(const string & name)
reasons (e.g., "." and ".."). */
if (string(name, 0, 1) == ".")
throw Error(format("illegal name: %1%") % name);
foreach (string::const_iterator, i, name)
if (!((*i >= 'A' && *i <= 'Z') ||
(*i >= 'a' && *i <= 'z') ||
(*i >= '0' && *i <= '9') ||
validChars.find(*i) != string::npos))
for (auto & i : name)
if (!((i >= 'A' && i <= 'Z') ||
(i >= 'a' && i <= 'z') ||
(i >= '0' && i <= '9') ||
validChars.find(i) != string::npos))
{
throw Error(format("invalid character %1% in name %2%")
% *i % name);
% i % name);
}
}
@@ -101,22 +91,22 @@ void checkStoreName(const string & name)
where
<store> = the location of the Nix store, usually /nix/store
<name> = a human readable name for the path, typically obtained
from the name attribute of the derivation, or the name of the
source file from which the store path is created. For derivation
outputs other than the default "out" output, the string "-<id>"
is suffixed to <name>.
<h> = base-32 representation of the first 160 bits of a SHA-256
hash of <s>; the hash part of the store name
<s> = the string "<type>:sha256:<h2>:<store>:<name>";
note that it includes the location of the store as well as the
name to make sure that changes to either of those are reflected
in the hash (e.g. you won't get /nix/store/<h>-name1 and
/nix/store/<h>-name2 with equal hash parts).
<type> = one of:
"text:<r1>:<r2>:...<rN>"
for plain text files written to the store using
@@ -219,9 +209,9 @@ Path computeStorePathForText(const string & name, const string & s,
hacky, but we can't put them in `s' since that would be
ambiguous. */
string type = "text";
foreach (PathSet::const_iterator, i, references) {
for (auto & i : references) {
type += ":";
type += *i;
type += i;
}
return makeStorePath(type, hash, name);
}
@@ -234,11 +224,11 @@ string StoreAPI::makeValidityRegistration(const PathSet & paths,
bool showDerivers, bool showHash)
{
string s = "";
foreach (PathSet::iterator, i, paths) {
s += *i + "\n";
ValidPathInfo info = queryPathInfo(*i);
for (auto & i : paths) {
s += i + "\n";
ValidPathInfo info = queryPathInfo(i);
if (showHash) {
s += printHash(info.hash) + "\n";
@@ -250,8 +240,8 @@ string StoreAPI::makeValidityRegistration(const PathSet & paths,
s += (format("%1%\n") % info.references.size()).str();
foreach (PathSet::iterator, j, info.references)
s += *j + "\n";
for (auto & j : info.references)
s += j + "\n";
}
return s;
@@ -286,9 +276,9 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
string showPaths(const PathSet & paths)
{
string s;
foreach (PathSet::const_iterator, i, paths) {
for (auto & i : paths) {
if (s.size() != 0) s += ", ";
s += "" + *i + "";
s += "" + i + "";
}
return s;
}
@@ -297,11 +287,11 @@ string showPaths(const PathSet & paths)
void exportPaths(StoreAPI & store, const Paths & paths,
bool sign, Sink & sink)
{
foreach (Paths::const_iterator, i, paths) {
writeInt(1, sink);
store.exportPath(*i, sign, sink);
for (auto & i : paths) {
sink << 1;
store.exportPath(i, sign, sink);
}
writeInt(0, sink);
sink << 0;
}
+50 -15
View File
@@ -4,6 +4,7 @@
#include "serialise.hh"
#include <string>
#include <limits>
#include <map>
#include <memory>
@@ -36,25 +37,23 @@ struct GCOptions
gcDeleteSpecific,
} GCAction;
GCAction action;
GCAction action{gcDeleteDead};
/* If `ignoreLiveness' is set, then reachability from the roots is
ignored (dangerous!). However, the paths must still be
unreferenced *within* the store (i.e., there can be no other
store paths that depend on them). */
bool ignoreLiveness;
bool ignoreLiveness{false};
/* For `gcDeleteSpecific', the paths to delete. */
PathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. */
unsigned long long maxFreed;
GCOptions();
unsigned long long maxFreed{std::numeric_limits<unsigned long long>::max()};
};
struct GCResults
struct GCResults
{
/* Depending on the action, the GC roots, or the paths that would
be or have been deleted. */
@@ -82,7 +81,7 @@ struct SubstitutablePathInfo
typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos;
struct ValidPathInfo
struct ValidPathInfo
{
Path path;
Path deriver;
@@ -100,13 +99,39 @@ typedef list<ValidPathInfo> ValidPathInfos;
enum BuildMode { bmNormal, bmRepair, bmCheck };
class StoreAPI
struct BuildResult
{
enum Status {
Built = 0,
Substituted,
AlreadyValid,
PermanentFailure,
InputRejected,
OutputRejected,
TransientFailure, // possibly transient
CachedFailure,
TimedOut,
MiscFailure,
DependencyFailed
} status = MiscFailure;
std::string errorMsg;
//time_t startTime = 0, stopTime = 0;
bool success() {
return status == Built || status == Substituted || status == AlreadyValid;
}
};
struct BasicDerivation;
class StoreAPI
{
public:
virtual ~StoreAPI() { }
/* Check whether a path is valid. */
/* Check whether a path is valid. */
virtual bool isValidPath(const Path & path) = 0;
/* Query which of the given paths is valid. */
@@ -118,7 +143,7 @@ public:
/* Query information about a valid path. */
virtual ValidPathInfo queryPathInfo(const Path & path) = 0;
/* Query the hash of a valid path. */
/* Query the hash of a valid path. */
virtual Hash queryPathHash(const Path & path) = 0;
/* Query the set of outgoing FS references for a store path. The
@@ -150,7 +175,7 @@ public:
/* Query the full store path given the hash part of a valid store
path, or "" if the path doesn't exist. */
virtual Path queryPathFromHashPart(const string & hashPart) = 0;
/* Query which of the given paths have substitutes. */
virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0;
@@ -159,12 +184,12 @@ public:
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.
The function object `filter' can be used to exclude files (see
libutil/archive.hh). */
virtual Path addToStore(const Path & srcPath,
virtual Path addToStore(const string & name, const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
@@ -194,6 +219,12 @@ public:
not derivations, substitute them. */
virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) = 0;
/* Build a single non-materialized derivation (i.e. not from an
on-disk .drv file). Note that drvPath is only used for
informational purposes. */
virtual BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
BuildMode buildMode = bmNormal) = 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
path). */
@@ -254,6 +285,10 @@ public:
/* Optimise the disk space usage of the Nix store by hard-linking files
with the same contents. */
virtual void optimiseStore() = 0;
/* Check the integrity of the Nix store. Returns true if errors
remain. */
virtual bool verifyStore(bool checkContents, bool repair) = 0;
};
@@ -267,7 +302,7 @@ bool isStorePath(const Path & path);
/* Extract the name part of the given store path. */
string storePathToName(const Path & path);
void checkStoreName(const string & name);
@@ -288,7 +323,7 @@ Path followLinksToStorePath(const Path & path);
/* Constructs a unique store path name. */
Path makeStorePath(const string & type,
const Hash & hash, const string & name);
Path makeOutputPath(const string & id,
const Hash & hash, const string & name);
+3 -1
View File
@@ -42,7 +42,9 @@ typedef enum {
wopQueryValidPaths = 31,
wopQuerySubstitutablePaths = 32,
wopQueryValidDerivers = 33,
wopOptimiseStore = 34
wopOptimiseStore = 34,
wopVerifyStore = 35,
wopBuildDerivation = 36,
} WorkerOp;
+12 -25
View File
@@ -39,8 +39,7 @@ PathFilter defaultPathFilter;
static void dumpContents(const Path & path, size_t size,
Sink & sink)
{
writeString("contents", sink);
writeLongLong(size, sink);
sink << "contents" << size;
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file %1%") % path);
@@ -65,21 +64,17 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path %1%") % path);
writeString("(", sink);
sink << "(";
if (S_ISREG(st.st_mode)) {
writeString("type", sink);
writeString("regular", sink);
if (st.st_mode & S_IXUSR) {
writeString("executable", sink);
writeString("", sink);
}
sink << "type" << "regular";
if (st.st_mode & S_IXUSR)
sink << "executable" << "";
dumpContents(path, (size_t) st.st_size, sink);
}
else if (S_ISDIR(st.st_mode)) {
writeString("type", sink);
writeString("directory", sink);
sink << "type" << "directory";
/* If we're on a case-insensitive system like Mac OS X, undo
the case hack applied by restorePath(). */
@@ -101,32 +96,24 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
for (auto & i : unhacked)
if (filter(path + "/" + i.first)) {
writeString("entry", sink);
writeString("(", sink);
writeString("name", sink);
writeString(i.first, sink);
writeString("node", sink);
sink << "entry" << "(" << "name" << i.first << "node";
dump(path + "/" + i.second, sink, filter);
writeString(")", sink);
sink << ")";
}
}
else if (S_ISLNK(st.st_mode)) {
writeString("type", sink);
writeString("symlink", sink);
writeString("target", sink);
writeString(readLink(path), sink);
}
else if (S_ISLNK(st.st_mode))
sink << "type" << "symlink" << "target" << readLink(path);
else throw Error(format("file %1% has an unsupported type") % path);
writeString(")", sink);
sink << ")";
}
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
{
writeString(archiveVersion1, sink);
sink << archiveVersion1;
dump(path, sink, filter);
}

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