Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71a5161365 | ||
|
|
6e06a18d1b | ||
|
|
73332fd342 | ||
|
|
d711fe8f5e | ||
|
|
ce97523a7c | ||
|
|
e12cf82782 | ||
|
|
eadb86f447 | ||
|
|
1d29db2a9f | ||
|
|
26221e44eb | ||
|
|
984c5cdc50 | ||
|
|
3b0f60e5c2 | ||
|
|
896428c818 | ||
|
|
34dfbd9394 | ||
|
|
30d19a2bdc | ||
|
|
3db950aab7 | ||
|
|
2bac04c5ff | ||
|
|
75837651f1 | ||
|
|
76cc8e97a2 | ||
|
|
50807f3dd5 | ||
|
|
f3dda728a4 | ||
|
|
d6d5885c15 | ||
|
|
2e8fd4c5cd | ||
|
|
cb4320c1a0 | ||
|
|
c8bb2371eb | ||
|
|
16c9935fa9 | ||
|
|
19eddecc0f | ||
|
|
b83801f8b3 | ||
|
|
14be783676 | ||
|
|
39e27a04b8 | ||
|
|
1ed55234d9 | ||
|
|
61af14a921 | ||
|
|
887bb5fa5a | ||
|
|
1993b10d11 | ||
|
|
0a2bee307b | ||
|
|
eda2f36c2a | ||
|
|
ccf31dbc25 | ||
|
|
db55940d9e | ||
|
|
b3491c781c | ||
|
|
6bd2c7bb38 | ||
|
|
1511aa9f48 | ||
|
|
f39979c6d3 | ||
|
|
7c9d0a5969 | ||
|
|
5845ffdf13 | ||
|
|
db21cfa688 | ||
|
|
467977f203 | ||
|
|
65e4dcd69b | ||
|
|
dd48c06bb6 | ||
|
|
9aed117395 | ||
|
|
e012c126db | ||
|
|
ff4de4cb27 | ||
|
|
2bc9c84327 | ||
|
|
ba63ec6f39 | ||
|
|
65f17cd330 | ||
|
|
0d4d92fcf9 | ||
|
|
a33b4bc01b | ||
|
|
39b3d9698e | ||
|
|
c48617671d | ||
|
|
d1e4b76a3a | ||
|
|
b65875f859 | ||
|
|
f90e9b65d6 | ||
|
|
7c4501886d | ||
|
|
f2b67fbf2a | ||
|
|
bf8cc4e9b6 | ||
|
|
7d148ad543 | ||
|
|
b64988bb35 | ||
|
|
b190f771e7 | ||
|
|
07d7e7df84 | ||
|
|
94378910fb | ||
|
|
a64da5915d | ||
|
|
f0f30f594c | ||
|
|
90aec21d76 | ||
|
|
898703e006 | ||
|
|
d8ddf994e7 | ||
|
|
7106bb0611 | ||
|
|
b755752f76 | ||
|
|
53dd97bb9d | ||
|
|
b8b571cfc1 | ||
|
|
bc51175dc0 | ||
|
|
da196ec68f | ||
|
|
c1323b53e3 | ||
|
|
1c88e100e7 | ||
|
|
feca5cb67f | ||
|
|
a80f11bf7b | ||
|
|
99c2c14f50 | ||
|
|
d68941d89d | ||
|
|
07c69aa03b | ||
|
|
b2798902ea | ||
|
|
920f5fd4dd | ||
|
|
7a411e01cf | ||
|
|
e0a068cb97 | ||
|
|
4ca5a9dcfd | ||
|
|
22b1a8d43f | ||
|
|
13493ef97c | ||
|
|
4441e4cc13 | ||
|
|
8d813fe3e0 | ||
|
|
a1c1bf3a56 | ||
|
|
12a888894b | ||
|
|
ea39c98d41 | ||
|
|
1529db702b | ||
|
|
3fac75bf29 | ||
|
|
46a56ea622 | ||
|
|
9c6328a7bf | ||
|
|
3d83188702 | ||
|
|
a010c0ae05 | ||
|
|
9233ac7c56 | ||
|
|
dad754843a | ||
|
|
000de699e9 | ||
|
|
71083f9e5e | ||
|
|
8972f7c14a | ||
|
|
6519f06f39 | ||
|
|
0705d04dfa | ||
|
|
9451ef3731 | ||
|
|
35d30d67eb | ||
|
|
deb8668a0e | ||
|
|
1f795f9f44 | ||
|
|
96dcc006e9 | ||
|
|
4bbcfaf87f | ||
|
|
ab2b3d6668 | ||
|
|
8a84bd8c8b | ||
|
|
4d652875bd | ||
|
|
fd6774e285 | ||
|
|
9b1866b721 | ||
|
|
035aeb9547 | ||
|
|
76f985b92d | ||
|
|
1e4a4a2e9f | ||
|
|
4ed2187377 | ||
|
|
1fc905ad4c | ||
|
|
c1f04fae35 | ||
|
|
60340ce3e2 | ||
|
|
1711679ea5 | ||
|
|
afa433e58c | ||
|
|
be1ff23352 | ||
|
|
000b5a000f | ||
|
|
7ea6ecf855 | ||
|
|
5114a07d95 | ||
|
|
fd89f97be9 | ||
|
|
5ce50cd99e | ||
|
|
6f0c6e20e0 | ||
|
|
b005e63ccf | ||
|
|
726f7f7fc9 | ||
|
|
da6b704b19 | ||
|
|
fa47279440 | ||
|
|
7a84143910 | ||
|
|
e659978ced | ||
|
|
17c71334e1 | ||
|
|
5badc8f975 | ||
|
|
9f3eb56b46 | ||
|
|
71b0a3a86b | ||
|
|
75ede65e3d | ||
|
|
c2699be93b | ||
|
|
336c4270c6 | ||
|
|
66d612f1da | ||
|
|
cffa7f80ab | ||
|
|
885bebf13b | ||
|
|
99897f6979 | ||
|
|
15d2d3c34e | ||
|
|
47bdc52c1b | ||
|
|
a8494de0be | ||
|
|
7bd8299c6c | ||
|
|
c33244d7c1 | ||
|
|
c4653afbcd | ||
|
|
e3e38a048e | ||
|
|
4b7c9f834c | ||
|
|
9bedd9b09b | ||
|
|
4646e94610 | ||
|
|
175935e053 | ||
|
|
e0953d53de | ||
|
|
1816ac0db1 | ||
|
|
dc7e8fae48 | ||
|
|
bb10010582 | ||
|
|
147deb236e | ||
|
|
bd91064150 | ||
|
|
a70d275f3d | ||
|
|
29e1ff675b | ||
|
|
f19b4abfb2 | ||
|
|
8c8750ae66 | ||
|
|
5451b8db9d | ||
|
|
b0bad3e615 | ||
|
|
d53735c823 | ||
|
|
b4e7eec16a | ||
|
|
1c972cba14 | ||
|
|
5d9cd27dce | ||
|
|
a596c525ad | ||
|
|
70cae879e3 | ||
|
|
2be7f79fd4 | ||
|
|
de8ed5c1d3 | ||
|
|
d66d9e8425 | ||
|
|
f3a5930488 | ||
|
|
e0def5bc4b | ||
|
|
0d1dafa0c4 | ||
|
|
db2ec59903 | ||
|
|
3688db3d43 | ||
|
|
73bf32ce94 | ||
|
|
de91a42c6e | ||
|
|
f46e329a13 | ||
|
|
f6716e95bb | ||
|
|
c2a8b5c42d | ||
|
|
a5e2c8e560 | ||
|
|
79ca503332 | ||
|
|
fcf57aad27 | ||
|
|
100961e370 | ||
|
|
f1151a3373 | ||
|
|
c23d67920e | ||
|
|
2a3b1df423 | ||
|
|
57d64d24aa | ||
|
|
57b82256b0 | ||
|
|
ed56ea980b | ||
|
|
27b7b94923 | ||
|
|
128538ef06 | ||
|
|
b76589206a | ||
|
|
a957893b26 | ||
|
|
7ba0e9cb48 | ||
|
|
153a943de7 | ||
|
|
6fec43ccb3 | ||
|
|
4d5c9d85ea | ||
|
|
df05f49dcd | ||
|
|
1b167c964f | ||
|
|
14fb7378df | ||
|
|
2c052278d2 | ||
|
|
8027083c3a | ||
|
|
8b88d25cda | ||
|
|
bbd45ac80f | ||
|
|
411b237ee5 | ||
|
|
3d97b8d1e7 | ||
|
|
bd0f362d2f | ||
|
|
f16b8786a2 | ||
|
|
2162a9c1c7 | ||
|
|
ccde347eb8 | ||
|
|
4579a44617 | ||
|
|
c9bd6a1de4 | ||
|
|
0ee1ca628a |
@@ -112,6 +112,8 @@ Makefile.config
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
*.dll
|
||||
*.exe
|
||||
*.dep
|
||||
*~
|
||||
*.pc
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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@
|
||||
|
||||
@@ -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]]),
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.,
|
||||
they’re 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>
|
||||
|
||||
@@ -36,8 +36,20 @@ nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
|
||||
<literal><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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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 <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 <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>
|
||||
|
||||
@@ -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 what’s available)</lineannotation>
|
||||
</para>
|
||||
|
||||
<para>To compare installed versions to what’s 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 < 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 <nixpkgs> {};
|
||||
|
||||
runCommand "dummy" { buildInputs = [ python pythonPackages.prettytable ]; } ""
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -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
|
||||
it’s 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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 they’re not already in your Nix store, and then start
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -36,7 +36,7 @@ it’s 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:
|
||||
|
||||
|
||||
@@ -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 wouldn’t have
|
||||
<para>You generally wouldn’t 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" />
|
||||
|
||||
@@ -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 doesn’t
|
||||
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><nix/fetchurl.nix></literal> now
|
||||
uses a <emphasis>builtin</emphasis> builder (i.e. it doesn’t
|
||||
require starting an external process; the download is performed by
|
||||
Nix itself). This ensures that derivation paths don’t 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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 won’t
|
||||
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 <- Network.HTTP.simpleHTTP (getRequest "http://nixos.org/")
|
||||
body <- 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><...></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>
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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 }' ";
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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: $?";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ MakeError(ThrownError, AssertionError)
|
||||
MakeError(Abort, EvalError)
|
||||
MakeError(TypeError, EvalError)
|
||||
MakeError(UndefinedVarError, Error)
|
||||
MakeError(RestrictedPathError, Error)
|
||||
|
||||
|
||||
/* Position objects. */
|
||||
|
||||
+37
-37
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "derivations.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void builtinFetchurl(const BasicDerivation & drv);
|
||||
|
||||
}
|
||||
+69
-19
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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 << ".";
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -42,7 +42,9 @@ typedef enum {
|
||||
wopQueryValidPaths = 31,
|
||||
wopQuerySubstitutablePaths = 32,
|
||||
wopQueryValidDerivers = 33,
|
||||
wopOptimiseStore = 34
|
||||
wopOptimiseStore = 34,
|
||||
wopVerifyStore = 35,
|
||||
wopBuildDerivation = 36,
|
||||
} WorkerOp;
|
||||
|
||||
|
||||
|
||||
+12
-25
@@ -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
Reference in New Issue
Block a user