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
|
*.a
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
*.dep
|
*.dep
|
||||||
*~
|
*~
|
||||||
*.pc
|
*.pc
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ makefiles = \
|
|||||||
src/nix-instantiate/local.mk \
|
src/nix-instantiate/local.mk \
|
||||||
src/nix-env/local.mk \
|
src/nix-env/local.mk \
|
||||||
src/nix-daemon/local.mk \
|
src/nix-daemon/local.mk \
|
||||||
|
src/nix-collect-garbage/local.mk \
|
||||||
src/download-via-ssh/local.mk \
|
src/download-via-ssh/local.mk \
|
||||||
src/nix-log2xml/local.mk \
|
src/nix-log2xml/local.mk \
|
||||||
src/bsdiff-4.3/local.mk \
|
src/bsdiff-4.3/local.mk \
|
||||||
@@ -25,7 +26,7 @@ makefiles = \
|
|||||||
|
|
||||||
GLOBAL_CXXFLAGS += -std=c++0x -g -Wall
|
GLOBAL_CXXFLAGS += -std=c++0x -g -Wall
|
||||||
|
|
||||||
include Makefile.config
|
-include Makefile.config
|
||||||
|
|
||||||
OPTIMIZE = 1
|
OPTIMIZE = 1
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ CFLAGS = @CFLAGS@
|
|||||||
CXX = @CXX@
|
CXX = @CXX@
|
||||||
CXXFLAGS = @CXXFLAGS@
|
CXXFLAGS = @CXXFLAGS@
|
||||||
HAVE_OPENSSL = @HAVE_OPENSSL@
|
HAVE_OPENSSL = @HAVE_OPENSSL@
|
||||||
|
HAVE_SODIUM = @HAVE_SODIUM@
|
||||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||||
PACKAGE_NAME = @PACKAGE_NAME@
|
PACKAGE_NAME = @PACKAGE_NAME@
|
||||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||||
|
SODIUM_LIBS = @SODIUM_LIBS@
|
||||||
bash = @bash@
|
bash = @bash@
|
||||||
bindir = @bindir@
|
bindir = @bindir@
|
||||||
bsddiff_compat_include = @bsddiff_compat_include@
|
bsddiff_compat_include = @bsddiff_compat_include@
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
|
|||||||
# include <sys/param.h>
|
# include <sys/param.h>
|
||||||
# endif
|
# endif
|
||||||
])
|
])
|
||||||
|
AC_CHECK_HEADERS([sys/syscall.h])
|
||||||
|
|
||||||
|
|
||||||
# Check for lutimes, optionally used for changing the mtime of
|
# 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"])
|
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.
|
# Whether to use the Boehm garbage collector.
|
||||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=no]]),
|
[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.
|
# network traffic, so don't do that.
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
|
|
||||||
# Don't build in a chroot because Nix's dependencies may not be there.
|
inherit chrootDeps;
|
||||||
__noChroot = true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -2,7 +2,7 @@ let
|
|||||||
fromEnv = var: def:
|
fromEnv = var: def:
|
||||||
let val = builtins.getEnv var; in
|
let val = builtins.getEnv var; in
|
||||||
if val != "" then val else def;
|
if val != "" then val else def;
|
||||||
in {
|
in rec {
|
||||||
perl = "@perl@";
|
perl = "@perl@";
|
||||||
shell = "@bash@";
|
shell = "@bash@";
|
||||||
coreutils = "@coreutils@";
|
coreutils = "@coreutils@";
|
||||||
@@ -12,6 +12,15 @@ in {
|
|||||||
tar = "@tar@";
|
tar = "@tar@";
|
||||||
tarFlags = "@tarFlags@";
|
tarFlags = "@tarFlags@";
|
||||||
tr = "@tr@";
|
tr = "@tr@";
|
||||||
curl = "@curl@";
|
|
||||||
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
|
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
|
||||||
|
[ ];
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-16
@@ -5,20 +5,9 @@ with import <nix/config.nix>;
|
|||||||
assert (outputHash != "" && outputHashAlgo != "")
|
assert (outputHash != "" && outputHashAlgo != "")
|
||||||
|| md5 != "" || sha1 != "" || sha256 != "";
|
|| 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 {
|
derivation {
|
||||||
name = baseNameOf (toString url);
|
name = baseNameOf (toString url);
|
||||||
builder = shell;
|
builder = "builtin:fetchurl";
|
||||||
args = [ "-e" builder ];
|
|
||||||
|
|
||||||
# New-style output content requirements.
|
# New-style output content requirements.
|
||||||
outputHashAlgo = if outputHashAlgo != "" then outputHashAlgo else
|
outputHashAlgo = if outputHashAlgo != "" then outputHashAlgo else
|
||||||
@@ -27,14 +16,11 @@ derivation {
|
|||||||
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
|
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
|
||||||
outputHashMode = if executable then "recursive" else "flat";
|
outputHashMode = if executable then "recursive" else "flat";
|
||||||
|
|
||||||
inherit system url;
|
inherit system url executable;
|
||||||
|
|
||||||
# No need to double the amount of network traffic
|
# No need to double the amount of network traffic
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
|
|
||||||
# Don't build in a chroot because Nix's dependencies may not be there.
|
|
||||||
__noChroot = true;
|
|
||||||
|
|
||||||
impureEnvVars = [
|
impureEnvVars = [
|
||||||
# We borrow these environment variables from the caller to allow
|
# We borrow these environment variables from the caller to allow
|
||||||
# easy proxy configuration. This is impure, but a fixed-output
|
# easy proxy configuration. This is impure, but a fixed-output
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
attrs @ { drvPath, outputs, ... }:
|
attrs @ { drvPath, outputs, name, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
commonAttrs = (builtins.listToAttrs outputsList) //
|
commonAttrs = (builtins.listToAttrs outputsList) //
|
||||||
{ all = map (x: x.value) outputsList;
|
{ all = map (x: x.value) outputsList;
|
||||||
inherit drvPath;
|
inherit drvPath name;
|
||||||
type = "derivation";
|
type = "derivation";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -41,9 +41,8 @@ derivation {
|
|||||||
args = [ "-e" builder ];
|
args = [ "-e" builder ];
|
||||||
inherit storePath hashAlgo compressionType;
|
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
|
# Remote machines may not have ${nixBinDir} or ${coreutils} in the same prefixes
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
|
|
||||||
|
inherit chrootDeps;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,5 @@ derivation {
|
|||||||
# No point in doing this remotely.
|
# No point in doing this remotely.
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
|
|
||||||
# Don't build in a chroot because Nix's dependencies may not be there.
|
inherit chrootDeps;
|
||||||
__noChroot = true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,23 +227,32 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
|||||||
<varlistentry><term><literal>build-use-chroot</literal></term>
|
<varlistentry><term><literal>build-use-chroot</literal></term>
|
||||||
|
|
||||||
<listitem><para>If set to <literal>true</literal>, builds will be
|
<listitem><para>If set to <literal>true</literal>, builds will be
|
||||||
performed in a <emphasis>chroot environment</emphasis>, i.e., the
|
performed in a <emphasis>chroot environment</emphasis>, i.e.,
|
||||||
build will be isolated from the normal file system hierarchy and
|
they’re isolated from the normal file system hierarchy and will
|
||||||
will only see its dependencies in the Nix store, the temporary
|
only see their dependencies in the Nix store, the temporary build
|
||||||
build directory, private versions of <filename>/proc</filename>,
|
directory, private versions of <filename>/proc</filename>,
|
||||||
<filename>/dev</filename>, <filename>/dev/shm</filename> and
|
<filename>/dev</filename>, <filename>/dev/shm</filename> and
|
||||||
<filename>/dev/pts</filename>, and the paths configured with the
|
<filename>/dev/pts</filename>, and the paths configured with the
|
||||||
<link linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
|
<link linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
|
||||||
option</link>. This is useful to prevent undeclared dependencies
|
option</link>. This is useful to prevent undeclared dependencies
|
||||||
on files in directories such as
|
on files in directories such as <filename>/usr/bin</filename>. In
|
||||||
<filename>/usr/bin</filename>.</para>
|
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
|
<para>Currently, chroots only work on Linux and Mac OS X. The use
|
||||||
should use the <link linkend='conf-build-users-group'>“build
|
of a chroot requires that Nix is run as root (so you should use
|
||||||
users” feature</link> to perform the actual builds under different
|
the <link linkend='conf-build-users-group'>“build users”
|
||||||
users than root). Currently, chroot builds only work on Linux
|
feature</link> to perform the actual builds under different users
|
||||||
because Nix uses “bind mounts” to make the Nix store and other
|
than root).</para>
|
||||||
directories available inside the chroot.</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>
|
</listitem>
|
||||||
|
|
||||||
@@ -392,6 +401,26 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
|
||||||
|
|
||||||
<listitem><para>The maximum number of parallel HTTP connections
|
<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>
|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
|
|||||||
@@ -36,8 +36,20 @@ nixpkgs=/home/eelco/Dev/nixpkgs-branch:/etc/nixos</screen>
|
|||||||
<literal><nixpkgs/<replaceable>path</replaceable>></literal> in
|
<literal><nixpkgs/<replaceable>path</replaceable>></literal> in
|
||||||
<filename>/home/eelco/Dev/nixpkgs-branch/<replaceable>path</replaceable></filename>
|
<filename>/home/eelco/Dev/nixpkgs-branch/<replaceable>path</replaceable></filename>
|
||||||
and
|
and
|
||||||
<filename>/etc/nixos/nixpkgs/<replaceable>path</replaceable></filename>.
|
<filename>/etc/nixos/nixpkgs/<replaceable>path</replaceable></filename>.</para>
|
||||||
</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
|
<para>The search path can be extended using the <option
|
||||||
linkend="opt-I">-I</option> option, which takes precedence over
|
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>
|
<command>nix-build</command> will use <filename>default.nix</filename>
|
||||||
in the current directory, if it exists.</para>
|
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
|
<para><command>nix-build</command> is essentially a wrapper around
|
||||||
<link
|
<link
|
||||||
linkend="sec-nix-instantiate"><command>nix-instantiate</command></link>
|
linkend="sec-nix-instantiate"><command>nix-instantiate</command></link>
|
||||||
@@ -175,6 +182,15 @@ bar
|
|||||||
|
|
||||||
</para>
|
</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>
|
</refsection>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<command>nix-channel</command>
|
<command>nix-channel</command>
|
||||||
<group choice='req'>
|
<group choice='req'>
|
||||||
<arg choice='plain'><option>--add</option> <replaceable>url</replaceable> <arg choice='opt'><replaceable>name</replaceable></arg></arg>
|
<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>--list</option></arg>
|
||||||
<arg choice='plain'><option>--update</option> <arg rep='repeat'><replaceable>names</replaceable></arg></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>
|
<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>
|
<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
|
<listitem><para><command>nix-channel</command> uses a
|
||||||
<command>nix-env</command> profile to keep track of previous
|
<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>
|
<varlistentry><term><filename>~/.nix-defexpr/channels</filename></term>
|
||||||
|
|
||||||
<listitem><para>This is a symlink to
|
<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
|
ensures that <command>nix-env</command> can find your channels. In
|
||||||
a multi-user installation, you may also have
|
a multi-user installation, you may also have
|
||||||
<filename>~/.nix-defexpr/channels_root</filename>, which links to
|
<filename>~/.nix-defexpr/channels_root</filename>, which links to
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<arg><option>--include-outputs</option></arg>
|
<arg><option>--include-outputs</option></arg>
|
||||||
<arg><option>--use-substitutes</option></arg>
|
<arg><option>--use-substitutes</option></arg>
|
||||||
<arg><option>-s</option></arg>
|
<arg><option>-s</option></arg>
|
||||||
|
<arg><option>-v</option></arg>
|
||||||
<arg choice='plain'>
|
<arg choice='plain'>
|
||||||
<replaceable>user@</replaceable><replaceable>machine</replaceable>
|
<replaceable>user@</replaceable><replaceable>machine</replaceable>
|
||||||
</arg>
|
</arg>
|
||||||
@@ -138,6 +139,12 @@ those paths. If this bothers you, use
|
|||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry><term><option>-v</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Show verbose output.</para></listitem>
|
||||||
|
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsection>
|
</refsection>
|
||||||
|
|||||||
@@ -154,7 +154,15 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
|||||||
<option>--install</option>, <option>--upgrade</option>, and
|
<option>--install</option>, <option>--upgrade</option>, and
|
||||||
<option>--query --available</option> operations to obtain
|
<option>--query --available</option> operations to obtain
|
||||||
derivations. The default is
|
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>
|
</varlistentry>
|
||||||
|
|
||||||
@@ -416,7 +424,7 @@ $ nix-env --install gcc-3.3.2
|
|||||||
installing `gcc-3.3.2'
|
installing `gcc-3.3.2'
|
||||||
uninstalling `gcc-3.1'</screen>
|
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>
|
<option>--preserve-installed</option> was not specified.</para>
|
||||||
|
|
||||||
<para>To install an arbitrary version:
|
<para>To install an arbitrary version:
|
||||||
@@ -496,6 +504,18 @@ the following paths will be substituted:
|
|||||||
|
|
||||||
</para>
|
</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>
|
||||||
|
|
||||||
</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>
|
<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>
|
<refsection><title>Examples</title>
|
||||||
|
|
||||||
|
<para>To show installed packages:
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
$ nix-env -q <lineannotation>(show installed derivations)</lineannotation>
|
$ nix-env -q
|
||||||
bison-1.875c
|
bison-1.875c
|
||||||
docbook-xml-4.2
|
docbook-xml-4.2
|
||||||
firefox-1.0.4
|
firefox-1.0.4
|
||||||
MPlayer-1.0pre7
|
MPlayer-1.0pre7
|
||||||
ORBit2-2.8.3
|
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
|
firefox-1.0.7
|
||||||
GConf-2.4.0.1
|
GConf-2.4.0.1
|
||||||
MPlayer-1.0pre7
|
MPlayer-1.0pre7
|
||||||
ORBit2-2.8.3
|
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>
|
-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 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>
|
--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>
|
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
|
$ nix-env -f ./foo.nix -qa
|
||||||
foo-1.2.3
|
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>
|
<replaceable>...</replaceable>
|
||||||
acrobat-reader-7.0 - ? <lineannotation>(package is not available at all)</lineannotation>
|
acrobat-reader-7.0 - ? <lineannotation>(package is not available at all)</lineannotation>
|
||||||
autoconf-2.59 = 2.59 <lineannotation>(same version)</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>
|
firefox-1.0.4 < 1.0.7 <lineannotation>(a more recent version is available)</lineannotation>
|
||||||
<replaceable>...</replaceable>
|
<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
|
bzip2-1.0.6
|
||||||
gzip-1.6
|
gzip-1.6
|
||||||
zip-3.0
|
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-37.0.2062.94
|
||||||
chromium-beta-38.0.2125.24
|
chromium-beta-38.0.2125.24
|
||||||
firefox-32.0.3
|
firefox-32.0.3
|
||||||
firefox-with-plugins-13.0.1
|
firefox-with-plugins-13.0.1
|
||||||
<replaceable>...</replaceable>
|
<replaceable>…</replaceable>
|
||||||
</screen>
|
</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>
|
||||||
|
|
||||||
</refsection>
|
</refsection>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<arg><option>--manifest</option></arg>
|
<arg><option>--manifest</option></arg>
|
||||||
<arg><option>--manifest-path</option> <replaceable>filename</replaceable></arg>
|
<arg><option>--manifest-path</option> <replaceable>filename</replaceable></arg>
|
||||||
<arg><option>--url-prefix</option> <replaceable>url</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>
|
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@@ -101,9 +102,9 @@ automatically.</para>
|
|||||||
<varlistentry><term><option>--bzip2</option></term>
|
<varlistentry><term><option>--bzip2</option></term>
|
||||||
|
|
||||||
<listitem><para>Compress NARs using <command>bzip2</command>
|
<listitem><para>Compress NARs using <command>bzip2</command>
|
||||||
instead of <command>xz -9</command>. The latter compresses about
|
instead of <command>xz</command>. The latter compresses about 30%
|
||||||
30% better on typical archives, decompresses about twice as fast,
|
better on typical archives, decompresses about twice as fast, but
|
||||||
but compresses a lot slower and is not supported by Nix prior to
|
compresses a lot slower and is not supported by Nix prior to
|
||||||
version 1.2.</para></listitem>
|
version 1.2.</para></listitem>
|
||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@@ -155,6 +156,19 @@ automatically.</para>
|
|||||||
|
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</refsection>
|
</refsection>
|
||||||
@@ -203,6 +217,40 @@ $ nix-pull http://example.org/cache
|
|||||||
|
|
||||||
to cause the binaries to be used by subsequent Nix operations.</para>
|
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>
|
</refsection>
|
||||||
|
|
||||||
|
|
||||||
@@ -303,6 +351,7 @@ NarHash: sha256:0s491y1h9hxj5ghiizlxk7ax6jwbha00zwn7lpyd5xg5bhf60vzg
|
|||||||
NarSize: 109521136
|
NarSize: 109521136
|
||||||
References: 2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16 ...
|
References: 2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16 ...
|
||||||
Deriver: 7akyyc87ka32xwmqza9dvyg5pwx3j212-glibc-2.7.drv
|
Deriver: 7akyyc87ka32xwmqza9dvyg5pwx3j212-glibc-2.7.drv
|
||||||
|
Sig: cache.example.org-1:WepnSp2UT0odDpR3NRjPVhJBHmdBgSBSTbHpdh4SCz92nGXwFY82bkPEmISoC0hGqBXDXEmB6y3Ohgna3mMgDg==
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
The fields are as follows:
|
The fields are as follows:
|
||||||
@@ -381,6 +430,22 @@ The fields are as follows:
|
|||||||
|
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
<replaceable>attrPath</replaceable>
|
<replaceable>attrPath</replaceable>
|
||||||
</arg>
|
</arg>
|
||||||
<arg><option>--command</option> <replaceable>cmd</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>--exclude</option> <replaceable>regexp</replaceable></arg>
|
||||||
<arg><option>--pure</option></arg>
|
<arg><option>--pure</option></arg>
|
||||||
<group choice='req'>
|
<group choice='req'>
|
||||||
@@ -60,6 +61,13 @@ derivation for development.</para>
|
|||||||
<filename>shell.nix</filename> if it exists, and
|
<filename>shell.nix</filename> if it exists, and
|
||||||
<filename>default.nix</filename> otherwise.</para>
|
<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
|
<para>If the derivation defines the variable
|
||||||
<varname>shellHook</varname>, it will be evaluated after
|
<varname>shellHook</varname>, it will be evaluated after
|
||||||
<literal>$stdenv/setup</literal> has been sourced. Since this hook is
|
<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>
|
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
|
||||||
|
|
||||||
<listitem><para>In the environment of the derivation, run the
|
<listitem><para>In the environment of the derivation, run the
|
||||||
shell command <replaceable>cmd</replaceable> instead of starting
|
shell command <replaceable>cmd</replaceable>. This command is
|
||||||
an interactive shell. However, if you end the shell command with
|
executed in an interactive shell. (Use <option>--run</option> to
|
||||||
<literal>return</literal>, you still get an interactive shell.
|
use a non-interactive shell instead.) However, a call to
|
||||||
This can be useful for doing any additional
|
<literal>exit</literal> is implicitly added to the command, so the
|
||||||
initialisation.</para></listitem>
|
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>
|
</varlistentry>
|
||||||
|
|
||||||
@@ -135,6 +156,15 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
|||||||
|
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
<para>The following common options are supported:</para>
|
<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 …
|
… -L/nix/store/j1zg5v…-sqlite-3.8.0.2/lib -L/nix/store/0gmcz9…-libX11-1.6.1/lib …
|
||||||
</screen>
|
</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>
|
</para>
|
||||||
|
|
||||||
</refsection>
|
</refsection>
|
||||||
|
|||||||
@@ -1338,6 +1338,56 @@ $ nix-store --clear-failed-paths *
|
|||||||
</refsection>
|
</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>
|
<refsection condition="manpage"><title>Environment variables</title>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ allowedReferences = [];
|
|||||||
recursively. For example,
|
recursively. For example,
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
allowedReferences = [ foobar ];
|
allowedRequisites = [ foobar ];
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
enforces that the output of a derivation cannot have any other
|
enforces that the output of a derivation cannot have any other
|
||||||
@@ -90,6 +90,33 @@ derivation {
|
|||||||
</varlistentry>
|
</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">
|
<varlistentry xml:id="fixed-output-drvs">
|
||||||
<term><varname>outputHash</varname></term>
|
<term><varname>outputHash</varname></term>
|
||||||
<term><varname>outputHashAlgo</varname></term>
|
<term><varname>outputHashAlgo</varname></term>
|
||||||
@@ -215,29 +242,29 @@ stdenv.mkDerivation {
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
<varlistentry><term><varname>impureEnvVars</varname></term>
|
<varlistentry><term><varname>passAsFile</varname></term>
|
||||||
|
|
||||||
<listitem><para>This attribute allows you to specify a list of
|
<listitem><para>A list of names of attributes that should be
|
||||||
environment variables that should be passed from the environment
|
passed via files rather than environment variables. For example,
|
||||||
of the calling user to the builder. Usually, the environment is
|
if you have
|
||||||
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>
|
<programlisting>
|
||||||
impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
|
passAsFile = ["big"];
|
||||||
</programlisting>
|
big = "a very long string";
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
to make it use the proxy server configuration specified by the
|
then when the builder runs, the environment variable
|
||||||
user in the environment variables <envar>http_proxy</envar> and
|
<envar>bigPath</envar> will contain the absolute path to a
|
||||||
friends.</para>
|
temporary file containing <literal>a very long
|
||||||
|
string</literal>. That is, for any attribute
|
||||||
<para>This attribute is only allowed in <link
|
<replaceable>x</replaceable> listed in
|
||||||
linkend="fixed-output-drvs">fixed-output derivations</link>, where
|
<varname>passAsFile</varname>, Nix will pass an environment
|
||||||
impurities such as these are okay since (the hash of) the output
|
variable <envar><replaceable>x</replaceable>Path</envar> holding
|
||||||
is known in advance. It is ignored for all other
|
the path of the file containing the value of attribute
|
||||||
derivations.</para></listitem>
|
<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>
|
</varlistentry>
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,28 @@ available as <function>builtins.derivation</function>.</para>
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><function>builtins.attrNames</function>
|
||||||
<replaceable>set</replaceable></term>
|
<replaceable>set</replaceable></term>
|
||||||
|
|
||||||
@@ -211,6 +233,45 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><function>builtins.filter</function>
|
||||||
<replaceable>f</replaceable> <replaceable>xs</replaceable></term>
|
<replaceable>f</replaceable> <replaceable>xs</replaceable></term>
|
||||||
|
|
||||||
@@ -274,6 +335,19 @@ stdenv.mkDerivation {
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term>
|
||||||
|
|
||||||
<listitem><para>Convert a JSON string to a Nix
|
<listitem><para>Convert a JSON string to a Nix
|
||||||
@@ -290,6 +364,24 @@ builtins.fromJSON ''{"x": [1, 2, 3], "y": null}''
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><function>builtins.getAttr</function>
|
||||||
<replaceable>s</replaceable> <replaceable>set</replaceable></term>
|
<replaceable>s</replaceable> <replaceable>set</replaceable></term>
|
||||||
|
|
||||||
@@ -624,7 +716,7 @@ in config.someSetting</programlisting>
|
|||||||
./A</literal> will return the set
|
./A</literal> will return the set
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
{ A = "regular"; B = "directory"; }</programlisting>
|
{ B = "regular"; C = "directory"; }</programlisting>
|
||||||
|
|
||||||
The possible values for the file type are
|
The possible values for the file type are
|
||||||
<literal>"regular"</literal>, <literal>"directory"</literal>,
|
<literal>"regular"</literal>, <literal>"directory"</literal>,
|
||||||
@@ -659,6 +751,23 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><function>builtins.seq</function>
|
||||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||||
|
|
||||||
@@ -670,6 +779,29 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry><term><function>builtins.stringLength</function>
|
||||||
<replaceable>e</replaceable></term>
|
<replaceable>e</replaceable></term>
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,14 @@ stdenv.mkDerivation {
|
|||||||
expression that contained it. For instance, if a Nix expression in
|
expression that contained it. For instance, if a Nix expression in
|
||||||
<filename>/foo/bar/bla.nix</filename> refers to
|
<filename>/foo/bar/bla.nix</filename> refers to
|
||||||
<filename>../xyzzy/fnord.nix</filename>, the absolute path is
|
<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
|
<listitem><para><emphasis>Booleans</emphasis> with values
|
||||||
<literal>true</literal> and
|
<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
|
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>
|
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>
|
</simplesect>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ collection</emphasis> (Nixpkgs).</para>
|
|||||||
<simplesect><title>Managing build environments</title>
|
<simplesect><title>Managing build environments</title>
|
||||||
|
|
||||||
<para>Nix is extremely useful for developers as it makes it easy to
|
<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
|
Nix expression that describes the dependencies of your package, the
|
||||||
command <command>nix-shell</command> will build or download those
|
command <command>nix-shell</command> will build or download those
|
||||||
dependencies if they’re not already in your Nix store, and then start
|
dependencies if they’re not already in your Nix store, and then start
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
version="5.0">
|
version="5.0">
|
||||||
|
|
||||||
<info>
|
<info>
|
||||||
|
|
||||||
<title>Nix Package Manager Guide</title>
|
<title>Nix Package Manager Guide</title>
|
||||||
|
<subtitle>Version <xi:include href="version.txt" parse="text" /></subtitle>
|
||||||
<edition>Version <xi:include href="version.txt" parse="text" /></edition>
|
|
||||||
|
|
||||||
<author>
|
<author>
|
||||||
<personname>
|
<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
|
<emphasis>channel</emphasis>, since it makes it easy to stay up to
|
||||||
date with new versions of Nixpkgs. (Channels are described in more
|
date with new versions of Nixpkgs. (Channels are described in more
|
||||||
detail in <xref linkend="sec-channels"/>.) Nixpkgs is automatically
|
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
|
Nix. If this is not the case for some reason, you can add it as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
|
|||||||
@@ -120,8 +120,7 @@ can also see all available generations:
|
|||||||
<screen>
|
<screen>
|
||||||
$ nix-env --list-generations</screen></para>
|
$ nix-env --list-generations</screen></para>
|
||||||
|
|
||||||
<para>Actually, there is another level of indirection not shown in the
|
<para>You generally wouldn’t have
|
||||||
figure above. You generally wouldn’t have
|
|
||||||
<filename>/nix/var/nix/profiles/<replaceable>some-profile</replaceable>/bin</filename>
|
<filename>/nix/var/nix/profiles/<replaceable>some-profile</replaceable>/bin</filename>
|
||||||
in your <envar>PATH</envar>. Rather, there is a symlink
|
in your <envar>PATH</envar>. Rather, there is a symlink
|
||||||
<filename>~/.nix-profile</filename> that points to your current
|
<filename>~/.nix-profile</filename> that points to your current
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
</partintro>
|
</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.8.xml" />
|
||||||
<xi:include href="rl-1.7.xml" />
|
<xi:include href="rl-1.7.xml" />
|
||||||
<xi:include href="rl-1.6.1.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>
|
</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>
|
</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
|
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)))
|
$(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;
|
package Nix::Config;
|
||||||
|
|
||||||
|
use MIME::Base64;
|
||||||
|
|
||||||
$version = "@PACKAGE_VERSION@";
|
$version = "@PACKAGE_VERSION@";
|
||||||
|
|
||||||
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||||
@@ -19,24 +21,33 @@ $useBindings = "@perlbindings@" eq "yes";
|
|||||||
|
|
||||||
%config = ();
|
%config = ();
|
||||||
|
|
||||||
|
%binaryCachePublicKeys = ();
|
||||||
|
|
||||||
|
$defaultPublicKeys = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=";
|
||||||
|
|
||||||
sub readConfig {
|
sub readConfig {
|
||||||
if (defined $ENV{'_NIX_OPTIONS'}) {
|
if (defined $ENV{'_NIX_OPTIONS'}) {
|
||||||
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
||||||
my ($n, $v) = split '=', $s, 2;
|
my ($n, $v) = split '=', $s, 2;
|
||||||
$config{$n} = $v;
|
$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";
|
foreach my $s (split(/ /, $config{"binary-cache-public-keys"} // $defaultPublicKeys)) {
|
||||||
return unless -f $config;
|
my ($keyName, $publicKey) = split ":", $s;
|
||||||
|
next unless defined $keyName && defined $publicKey;
|
||||||
open CONFIG, "<$config" or die "cannot open ‘$config’";
|
$binaryCachePublicKeys{$keyName} = decode_base64($publicKey);
|
||||||
while (<CONFIG>) {
|
|
||||||
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
|
|
||||||
$config{$1} = $2;
|
|
||||||
}
|
}
|
||||||
close CONFIG;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
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::stat;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
use Fcntl ':flock';
|
use Fcntl ':flock';
|
||||||
|
use MIME::Base64;
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Crypto;
|
use Nix::Store;
|
||||||
|
|
||||||
our @ISA = qw(Exporter);
|
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 {
|
sub addNAR {
|
||||||
@@ -376,7 +377,6 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Delete all old manifests downloaded from a given URL.
|
# Delete all old manifests downloaded from a given URL.
|
||||||
sub deleteOldManifests {
|
sub deleteOldManifests {
|
||||||
my ($url, $curUrlFile) = @_;
|
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.
|
# Parse a NAR info file.
|
||||||
sub parseNARInfo {
|
sub parseNARInfo {
|
||||||
my ($storePath, $content, $requireValidSig, $location) = @_;
|
my ($storePath, $content, $requireValidSig, $location) = @_;
|
||||||
|
|
||||||
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system, $sig);
|
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system, $sig);
|
||||||
my $signedData = "";
|
|
||||||
my $compression = "bzip2";
|
my $compression = "bzip2";
|
||||||
my @refs;
|
my @refs;
|
||||||
|
|
||||||
@@ -415,8 +432,7 @@ sub parseNARInfo {
|
|||||||
elsif ($1 eq "References") { @refs = split / /, $2; }
|
elsif ($1 eq "References") { @refs = split / /, $2; }
|
||||||
elsif ($1 eq "Deriver") { $deriver = $2; }
|
elsif ($1 eq "Deriver") { $deriver = $2; }
|
||||||
elsif ($1 eq "System") { $system = $2; }
|
elsif ($1 eq "System") { $system = $2; }
|
||||||
elsif ($1 eq "Signature") { $sig = $2; last; }
|
elsif ($1 eq "Sig") { $sig = $2; }
|
||||||
$signedData .= "$line\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
|
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
|
||||||
@@ -434,30 +450,37 @@ sub parseNARInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ($requireValidSig) {
|
if ($requireValidSig) {
|
||||||
|
# FIXME: might be useful to support multiple signatures per .narinfo.
|
||||||
|
|
||||||
if (!defined $sig) {
|
if (!defined $sig) {
|
||||||
warn "NAR info file ‘$location’ lacks a signature; ignoring\n";
|
warn "NAR info file ‘$location’ lacks a signature; ignoring\n";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
my ($sigVersion, $keyName, $sig64) = split ";", $sig;
|
my ($keyName, $sig64) = split ":", $sig;
|
||||||
$sigVersion //= 0;
|
|
||||||
if ($sigVersion != 1) {
|
|
||||||
warn "NAR info file ‘$location’ has unsupported version $sigVersion; ignoring\n";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
return undef unless defined $keyName && defined $sig64;
|
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";
|
warn "NAR info file ‘$location’ is signed by unknown key ‘$keyName’; ignoring\n";
|
||||||
return undef;
|
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;
|
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;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
$res->{signedBy} = $keyName;
|
$res->{signedBy} = $keyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
|
|||||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
||||||
|
|
||||||
our @EXPORT = qw(
|
our @EXPORT = qw(
|
||||||
|
setVerbosity
|
||||||
isValidPath queryReferences queryPathInfo queryDeriver queryPathHash
|
isValidPath queryReferences queryPathInfo queryDeriver queryPathHash
|
||||||
queryPathFromHashPart
|
queryPathFromHashPart
|
||||||
topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
|
topoSortPaths computeFSClosure followLinksToStorePath exportPaths importPaths
|
||||||
hashPath hashFile hashString
|
hashPath hashFile hashString convertHash
|
||||||
|
signString checkSignature
|
||||||
addToStore makeFixedOutputPath
|
addToStore makeFixedOutputPath
|
||||||
derivationFromPath
|
derivationFromPath
|
||||||
);
|
);
|
||||||
|
|||||||
+67
-1
@@ -11,6 +11,10 @@
|
|||||||
#include <misc.hh>
|
#include <misc.hh>
|
||||||
#include <util.hh>
|
#include <util.hh>
|
||||||
|
|
||||||
|
#if HAVE_SODIUM
|
||||||
|
#include <sodium.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
@@ -44,6 +48,11 @@ void init()
|
|||||||
doInit();
|
doInit();
|
||||||
|
|
||||||
|
|
||||||
|
void setVerbosity(int level)
|
||||||
|
CODE:
|
||||||
|
verbosity = (Verbosity) level;
|
||||||
|
|
||||||
|
|
||||||
int isValidPath(char * path)
|
int isValidPath(char * path)
|
||||||
CODE:
|
CODE:
|
||||||
try {
|
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)
|
SV * addToStore(char * srcPath, int recursive, char * algo)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
doInit();
|
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)));
|
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak(e.what());
|
croak(e.what());
|
||||||
|
|||||||
+6
-5
@@ -5,8 +5,7 @@ nix_perl_sources := \
|
|||||||
$(d)/lib/Nix/SSH.pm \
|
$(d)/lib/Nix/SSH.pm \
|
||||||
$(d)/lib/Nix/CopyClosure.pm \
|
$(d)/lib/Nix/CopyClosure.pm \
|
||||||
$(d)/lib/Nix/Config.pm.in \
|
$(d)/lib/Nix/Config.pm.in \
|
||||||
$(d)/lib/Nix/Utils.pm \
|
$(d)/lib/Nix/Utils.pm
|
||||||
$(d)/lib/Nix/Crypto.pm
|
|
||||||
|
|
||||||
nix_perl_modules := $(nix_perl_sources:.in=)
|
nix_perl_modules := $(nix_perl_sources:.in=)
|
||||||
|
|
||||||
@@ -23,16 +22,18 @@ ifeq ($(perlbindings), yes)
|
|||||||
|
|
||||||
Store_SOURCES := $(Store_DIR)/Store.cc
|
Store_SOURCES := $(Store_DIR)/Store.cc
|
||||||
|
|
||||||
Store_LIBS = libstore libutil
|
|
||||||
|
|
||||||
Store_CXXFLAGS = \
|
Store_CXXFLAGS = \
|
||||||
-I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
|
-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
|
-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)))
|
ifeq (CYGWIN,$(findstring CYGWIN,$(OS)))
|
||||||
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
archlib = $(shell perl -E 'use Config; print $$Config{archlib};')
|
||||||
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
libperl = $(shell perl -E 'use Config; print $$Config{libperl};')
|
||||||
Store_LDFLAGS = $(shell find ${archlib} -name ${libperl})
|
Store_LDFLAGS += $(shell find ${archlib} -name ${libperl})
|
||||||
endif
|
endif
|
||||||
|
|
||||||
Store_ALLOW_UNDEFINED = 1
|
Store_ALLOW_UNDEFINED = 1
|
||||||
|
|||||||
+45
-33
@@ -24,7 +24,7 @@ let
|
|||||||
|
|
||||||
buildInputs =
|
buildInputs =
|
||||||
[ curl bison flex perl libxml2 libxslt bzip2
|
[ curl bison flex perl libxml2 libxslt bzip2
|
||||||
tetex dblatex nukeReferences pkgconfig sqlite
|
tetex dblatex nukeReferences pkgconfig sqlite libsodium
|
||||||
docbook5 docbook5_xsl
|
docbook5 docbook5_xsl
|
||||||
] ++ lib.optional (!lib.inNixShell) git;
|
] ++ lib.optional (!lib.inNixShell) git;
|
||||||
|
|
||||||
@@ -80,7 +80,9 @@ let
|
|||||||
name = "nix";
|
name = "nix";
|
||||||
src = tarball;
|
src = tarball;
|
||||||
|
|
||||||
buildInputs = [ curl perl bzip2 openssl pkgconfig sqlite boehmgc ];
|
buildInputs =
|
||||||
|
[ curl perl bzip2 openssl pkgconfig sqlite boehmgc ]
|
||||||
|
++ lib.optional stdenv.isLinux libsodium;
|
||||||
|
|
||||||
configureFlags = ''
|
configureFlags = ''
|
||||||
--disable-init-state
|
--disable-init-state
|
||||||
@@ -91,14 +93,6 @@ let
|
|||||||
--sysconfdir=/etc
|
--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;
|
enableParallelBuilding = true;
|
||||||
|
|
||||||
makeFlags = "profiledir=$(out)/etc/profile.d";
|
makeFlags = "profiledir=$(out)/etc/profile.d";
|
||||||
@@ -181,23 +175,33 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
rpm_fedora20i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora20i386);
|
rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) [];
|
||||||
rpm_fedora20x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora20x86_64);
|
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_debian7i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian7i386) [];
|
||||||
deb_debian7x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian7x86_64);
|
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_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) [];
|
||||||
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64);
|
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) [];
|
||||||
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386);
|
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) [];
|
||||||
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64);
|
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) [];
|
||||||
deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386);
|
deb_ubuntu1310i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1310i386) [];
|
||||||
deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64);
|
deb_ubuntu1310x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1310x86_64) [];
|
||||||
deb_ubuntu1404i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1404i386);
|
deb_ubuntu1404i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1404i386) [];
|
||||||
deb_ubuntu1404x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1404x86_64);
|
deb_ubuntu1404x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1404x86_64) [];
|
||||||
deb_ubuntu1410i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1410i386);
|
deb_ubuntu1410i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1410i386) [];
|
||||||
deb_ubuntu1410x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1410x86_64);
|
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.
|
# System tests.
|
||||||
@@ -245,12 +249,14 @@ let
|
|||||||
binaryTarball.x86_64-linux
|
binaryTarball.x86_64-linux
|
||||||
deb_debian7i386
|
deb_debian7i386
|
||||||
deb_debian7x86_64
|
deb_debian7x86_64
|
||||||
deb_ubuntu1404i386
|
deb_ubuntu1404i386 # LTS
|
||||||
deb_ubuntu1404x86_64
|
deb_ubuntu1404x86_64 # LTS
|
||||||
deb_ubuntu1410i386
|
deb_ubuntu1504i386
|
||||||
deb_ubuntu1410x86_64
|
deb_ubuntu1504x86_64
|
||||||
rpm_fedora20i386
|
rpm_fedora20i386
|
||||||
rpm_fedora20x86_64
|
rpm_fedora20x86_64
|
||||||
|
rpm_fedora21i386
|
||||||
|
rpm_fedora21x86_64
|
||||||
tests.remoteBuilds
|
tests.remoteBuilds
|
||||||
tests.nix-copy-closure
|
tests.nix-copy-closure
|
||||||
tests.binaryTarball
|
tests.binaryTarball
|
||||||
@@ -264,7 +270,7 @@ let
|
|||||||
makeRPM_x86_64 = makeRPM "x86_64-linux";
|
makeRPM_x86_64 = makeRPM "x86_64-linux";
|
||||||
|
|
||||||
makeRPM =
|
makeRPM =
|
||||||
system: diskImageFun:
|
system: diskImageFun: extraPackages:
|
||||||
|
|
||||||
with import <nixpkgs> { inherit system; };
|
with import <nixpkgs> { inherit system; };
|
||||||
|
|
||||||
@@ -272,7 +278,9 @@ let
|
|||||||
name = "nix-rpm";
|
name = "nix-rpm";
|
||||||
src = jobs.tarball;
|
src = jobs.tarball;
|
||||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
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;
|
memSize = 1024;
|
||||||
meta.schedulingPriority = 50;
|
meta.schedulingPriority = 50;
|
||||||
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
|
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
|
||||||
@@ -283,7 +291,7 @@ let
|
|||||||
makeDeb_x86_64 = makeDeb "x86_64-linux";
|
makeDeb_x86_64 = makeDeb "x86_64-linux";
|
||||||
|
|
||||||
makeDeb =
|
makeDeb =
|
||||||
system: diskImageFun:
|
system: diskImageFun: extraPackages:
|
||||||
|
|
||||||
with import <nixpkgs> { inherit system; };
|
with import <nixpkgs> { inherit system; };
|
||||||
|
|
||||||
@@ -291,11 +299,15 @@ let
|
|||||||
name = "nix-deb";
|
name = "nix-deb";
|
||||||
src = jobs.tarball;
|
src = jobs.tarball;
|
||||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
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;
|
memSize = 1024;
|
||||||
meta.schedulingPriority = 50;
|
meta.schedulingPriority = 50;
|
||||||
configureFlags = "--sysconfdir=/etc";
|
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>";
|
debMaintainer = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
|
||||||
doInstallCheck = true;
|
doInstallCheck = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ if (defined $conf && -e $conf) {
|
|||||||
push @machines,
|
push @machines,
|
||||||
{ hostName => $tokens[0]
|
{ hostName => $tokens[0]
|
||||||
, systemTypes => [ split(/,/, $tokens[1]) ]
|
, systemTypes => [ split(/,/, $tokens[1]) ]
|
||||||
, sshKeys => $tokens[2]
|
, sshKey => $tokens[2]
|
||||||
, maxJobs => int($tokens[3])
|
, maxJobs => int($tokens[3])
|
||||||
, speedFactor => 1.0 * (defined $tokens[4] ? int($tokens[4]) : 1)
|
, speedFactor => 1.0 * (defined $tokens[4] ? int($tokens[4]) : 1)
|
||||||
, supportedFeatures => [ @supportedFeatures, @mandatoryFeatures ]
|
, supportedFeatures => [ @supportedFeatures, @mandatoryFeatures ]
|
||||||
@@ -201,7 +201,7 @@ REQ: while (1) {
|
|||||||
|
|
||||||
|
|
||||||
# Connect to the selected machine.
|
# Connect to the selected machine.
|
||||||
my @sshOpts = ("-i", $machine->{sshKeys});
|
my @sshOpts = ("-i", $machine->{sshKey});
|
||||||
$hostName = $machine->{hostName};
|
$hostName = $machine->{hostName};
|
||||||
eval {
|
eval {
|
||||||
($from, $to) = connectToRemoteNix($hostName, \@sshOpts, "2>&4");
|
($from, $to) = connectToRemoteNix($hostName, \@sshOpts, "2>&4");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
|||||||
|
|
||||||
|
|
||||||
STDOUT->autoflush(1);
|
STDOUT->autoflush(1);
|
||||||
|
binmode STDERR, ":encoding(utf8)";
|
||||||
|
|
||||||
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
|
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ elsif ($ARGV[0] eq "--substitute") {
|
|||||||
my ($store, $sourcePath) = findStorePath $storePath;
|
my ($store, $sourcePath) = findStorePath $storePath;
|
||||||
die unless $store;
|
die unless $store;
|
||||||
print STDERR "\n*** Copying ‘$storePath’ from ‘$sourcePath’\n\n";
|
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’";
|
or die "cannot copy ‘$sourcePath’ to ‘$storePath’";
|
||||||
print "\n"; # no hash to verify
|
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 $userName = getpwuid($<) || $ENV{"USER"} or die "cannot figure out user name";
|
||||||
|
|
||||||
|
my $userAgent = "Nix/$Nix::Config::version";
|
||||||
|
|
||||||
sub isTrue {
|
sub isTrue {
|
||||||
my ($x) = @_;
|
my ($x) = @_;
|
||||||
return $x eq "true" || $x eq "1";
|
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 $requireSignedBinaryCaches = ($Nix::Config::config{"signed-binary-caches"} // "0") ne "0";
|
||||||
|
|
||||||
my $curlConnectTimeout = int(
|
my $curlConnectTimeout = int(
|
||||||
@@ -75,7 +81,7 @@ sub addRequest {
|
|||||||
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
||||||
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
$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_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_NOBODY, 1) if $head;
|
||||||
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
||||||
$curl->setopt(CURLOPT_CONNECTTIMEOUT, $curlConnectTimeout);
|
$curl->setopt(CURLOPT_CONNECTTIMEOUT, $curlConnectTimeout);
|
||||||
@@ -555,7 +561,7 @@ sub downloadBinary {
|
|||||||
die if $requireSignedBinaryCaches && !defined $info->{signedBy};
|
die if $requireSignedBinaryCaches && !defined $info->{signedBy};
|
||||||
print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n";
|
print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n";
|
||||||
checkURL $url;
|
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";
|
warn "download of ‘$url’ failed" . ($! ? ": $!" : "") . "\n";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ mkdir -p $dest/store
|
|||||||
|
|
||||||
echo -n "copying Nix to $dest/store..." >&2
|
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
|
echo -n "." >&2
|
||||||
i_tmp="$dest/store/$i.$$"
|
i_tmp="$dest/store/$i.$$"
|
||||||
if [ -e "$i_tmp" ]; then
|
if [ -e "$i_tmp" ]; then
|
||||||
@@ -75,7 +75,7 @@ fi
|
|||||||
# Install an SSL certificate bundle.
|
# Install an SSL certificate bundle.
|
||||||
if [ -z "$SSL_CERT_FILE" -o ! -f "$SSL_CERT_FILE" ]; then
|
if [ -z "$SSL_CERT_FILE" -o ! -f "$SSL_CERT_FILE" ]; then
|
||||||
$nix/bin/nix-env -i "$cacert"
|
$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
|
fi
|
||||||
|
|
||||||
# Subscribe the user to the Nixpkgs channel and fetch it.
|
# Subscribe the user to the Nixpkgs channel and fetch it.
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
nix_bin_scripts := \
|
nix_bin_scripts := \
|
||||||
$(d)/nix-build \
|
$(d)/nix-build \
|
||||||
$(d)/nix-channel \
|
$(d)/nix-channel \
|
||||||
$(d)/nix-collect-garbage \
|
|
||||||
$(d)/nix-copy-closure \
|
$(d)/nix-copy-closure \
|
||||||
$(d)/nix-generate-patches \
|
$(d)/nix-generate-patches \
|
||||||
$(d)/nix-install-package \
|
$(d)/nix-install-package \
|
||||||
|
|||||||
+67
-4
@@ -5,6 +5,8 @@ use strict;
|
|||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Store;
|
use Nix::Store;
|
||||||
use Nix::Utils;
|
use Nix::Utils;
|
||||||
|
use File::Basename;
|
||||||
|
use Cwd;
|
||||||
|
|
||||||
binmode STDERR, ":encoding(utf8)";
|
binmode STDERR, ":encoding(utf8)";
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ my $runEnv = $0 =~ /nix-shell$/;
|
|||||||
my $pure = 0;
|
my $pure = 0;
|
||||||
my $fromArgs = 0;
|
my $fromArgs = 0;
|
||||||
my $packages = 0;
|
my $packages = 0;
|
||||||
|
my $interactive = 1;
|
||||||
|
|
||||||
my @instArgs = ();
|
my @instArgs = ();
|
||||||
my @buildArgs = ();
|
my @buildArgs = ();
|
||||||
@@ -25,6 +28,9 @@ my @envExclude = ();
|
|||||||
|
|
||||||
my $myName = $runEnv ? "nix-shell" : "nix-build";
|
my $myName = $runEnv ? "nix-shell" : "nix-build";
|
||||||
|
|
||||||
|
my $inShebang = 0;
|
||||||
|
my $script;
|
||||||
|
my @savedArgs;
|
||||||
|
|
||||||
my $tmpDir = mkTempDir($myName);
|
my $tmpDir = mkTempDir($myName);
|
||||||
|
|
||||||
@@ -35,6 +41,30 @@ my $drvLink = "$tmpDir/derivation";
|
|||||||
$SIG{'INT'} = sub { exit 1 };
|
$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++) {
|
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||||
my $arg = $ARGV[$n];
|
my $arg = $ARGV[$n];
|
||||||
|
|
||||||
@@ -131,10 +161,11 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
|||||||
$runEnv = 1;
|
$runEnv = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($arg eq "--command") {
|
elsif ($arg eq "--command" || $arg eq "--run") {
|
||||||
$n++;
|
$n++;
|
||||||
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
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") {
|
elsif ($arg eq "--exclude") {
|
||||||
@@ -155,15 +186,39 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
|||||||
$packages = 1;
|
$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 "-") {
|
elsif (substr($arg, 0, 1) eq "-") {
|
||||||
push @buildArgs, $arg;
|
push @buildArgs, $arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
elsif ($arg eq "-Q" || $arg eq "--no-build-output") {
|
||||||
|
push @buildArgs, $arg;
|
||||||
|
push @instArgs, $arg;
|
||||||
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
push @exprs, $arg;
|
push @exprs, $arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
die "$0: ‘-p’ and ‘-E’ are mutually exclusive\n" if $packages && $fromArgs;
|
||||||
|
|
||||||
if ($packages) {
|
if ($packages) {
|
||||||
push @instArgs, "--expr";
|
push @instArgs, "--expr";
|
||||||
@exprs = (
|
@exprs = (
|
||||||
@@ -182,6 +237,11 @@ foreach my $expr (@exprs) {
|
|||||||
# Instantiate.
|
# Instantiate.
|
||||||
my @drvPaths;
|
my @drvPaths;
|
||||||
if ($expr !~ /^\/.*\.drv$/) {
|
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.
|
# !!! 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;
|
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||||
@@ -232,17 +292,20 @@ foreach my $expr (@exprs) {
|
|||||||
($pure ? '' : 'p=$PATH; ' ) .
|
($pure ? '' : 'p=$PATH; ' ) .
|
||||||
'dontAddDisableDepTrack=1; ' .
|
'dontAddDisableDepTrack=1; ' .
|
||||||
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
||||||
'if [ "$(type -t runHook)" = function ]; then runHook shellHook; fi; ' .
|
|
||||||
($pure ? '' : 'PATH=$PATH:$p; unset p; ') .
|
($pure ? '' : 'PATH=$PATH:$p; unset p; ') .
|
||||||
'set +e; ' .
|
'set +e; ' .
|
||||||
'[ -n "$PS1" ] && PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
'[ -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_ENFORCE_PURITY; ' .
|
||||||
'unset NIX_INDENT_MAKE; ' .
|
'unset NIX_INDENT_MAKE; ' .
|
||||||
'shopt -u nullglob; ' .
|
'shopt -u nullglob; ' .
|
||||||
'unset TZ; ' . (defined $ENV{'TZ'} ? "export TZ='${ENV{'TZ'}}'; " : '') .
|
'unset TZ; ' . (defined $ENV{'TZ'} ? "export TZ='${ENV{'TZ'}}'; " : '') .
|
||||||
$envCommand);
|
$envCommand);
|
||||||
$ENV{BASH_ENV} = $rcfile;
|
$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;
|
die;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-35
@@ -6,6 +6,7 @@ use File::Basename;
|
|||||||
use File::Path qw(mkpath);
|
use File::Path qw(mkpath);
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Manifest;
|
use Nix::Manifest;
|
||||||
|
use File::Temp qw(tempdir);
|
||||||
|
|
||||||
binmode STDERR, ":encoding(utf8)";
|
binmode STDERR, ":encoding(utf8)";
|
||||||
|
|
||||||
@@ -98,42 +99,14 @@ sub update {
|
|||||||
my $url = $channels{$name};
|
my $url = $channels{$name};
|
||||||
my $origUrl = "$url/MANIFEST";
|
my $origUrl = "$url/MANIFEST";
|
||||||
|
|
||||||
# Check if $url is a redirect. If so, follow it now to ensure
|
# We want to download the url to a file to see if it's a tarball while also checking if we
|
||||||
# consistency if the redirection is changed between
|
# got redirected in the process, so that we can grab the various parts of a nix channel
|
||||||
# downloading the manifest and the tarball.
|
# definition from a consistent location if the redirect changes mid-download.
|
||||||
my $headers = `$Nix::Config::curl --silent --head '$url'`;
|
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;
|
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
|
# If the URL contains a version number, append it to the name
|
||||||
# attribute (so that "nix-env -q" on the channels profile
|
# attribute (so that "nix-env -q" on the channels profile
|
||||||
@@ -141,6 +114,52 @@ sub update {
|
|||||||
my $cname = $name;
|
my $cname = $name;
|
||||||
$cname .= $1 if basename($url) =~ /(-\d.*)$/;
|
$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 }' ";
|
$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 $includeOutputs = 0;
|
||||||
my $dryRun = 0;
|
my $dryRun = 0;
|
||||||
my $useSubstitutes = 0;
|
my $useSubstitutes = 0;
|
||||||
|
my $verbosity = 1;
|
||||||
|
|
||||||
|
|
||||||
# !!! Copied from nix-pack-closure, should put this in a module.
|
# !!! 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") {
|
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
|
||||||
$useSubstitutes = 1;
|
$useSubstitutes = 1;
|
||||||
}
|
}
|
||||||
|
elsif ($arg eq "-v") {
|
||||||
|
$verbosity++;
|
||||||
|
setVerbosity($verbosity);
|
||||||
|
}
|
||||||
elsif (!defined $sshHost) {
|
elsif (!defined $sshHost) {
|
||||||
$sshHost = $arg;
|
$sshHost = $arg;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ sub barf {
|
|||||||
my $pkgFile = $source;
|
my $pkgFile = $source;
|
||||||
if ($fromURL) {
|
if ($fromURL) {
|
||||||
$pkgFile = "$tmpDir/tmp.nixpkg";
|
$pkgFile = "$tmpDir/tmp.nixpkg";
|
||||||
system("@curl@", "--silent", $source, "-o", $pkgFile) == 0
|
system("@curl@", "-L", "--silent", $source, "-o", $pkgFile) == 0
|
||||||
or barf "curl failed: $?";
|
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
|
export NIX_PATH=${NIX_PATH:+$NIX_PATH:}nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs
|
||||||
|
|
||||||
# Set $SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
# Set $SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||||
if [ -e /etc/ssl/certs/ca-bundle.crt ]; then # Fedora, NixOS
|
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||||
export SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt
|
|
||||||
elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # Ubuntu, Debian
|
|
||||||
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
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"
|
export SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
+17
-12
@@ -6,11 +6,11 @@ use File::Basename;
|
|||||||
use File::Path qw(mkpath);
|
use File::Path qw(mkpath);
|
||||||
use File::stat;
|
use File::stat;
|
||||||
use File::Copy;
|
use File::Copy;
|
||||||
|
use MIME::Base64;
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
use Nix::Store;
|
use Nix::Store;
|
||||||
use Nix::Manifest;
|
use Nix::Manifest;
|
||||||
use Nix::Utils;
|
use Nix::Utils;
|
||||||
use Nix::Crypto;
|
|
||||||
|
|
||||||
binmode STDERR, ":encoding(utf8)";
|
binmode STDERR, ":encoding(utf8)";
|
||||||
|
|
||||||
@@ -27,9 +27,9 @@ my $writeManifest = 0;
|
|||||||
my $manifestPath;
|
my $manifestPath;
|
||||||
my $archivesURL;
|
my $archivesURL;
|
||||||
my $link = 0;
|
my $link = 0;
|
||||||
my $privateKeyFile;
|
my $secretKeyFile;
|
||||||
my $keyName;
|
|
||||||
my @roots;
|
my @roots;
|
||||||
|
my @buildArgs;
|
||||||
|
|
||||||
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||||
my $arg = $ARGV[$n];
|
my $arg = $ARGV[$n];
|
||||||
@@ -61,14 +61,14 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
|||||||
$archivesURL = $ARGV[$n];
|
$archivesURL = $ARGV[$n];
|
||||||
} elsif ($arg eq "--link") {
|
} elsif ($arg eq "--link") {
|
||||||
$link = 1;
|
$link = 1;
|
||||||
} elsif ($arg eq "--key") {
|
} elsif ($arg eq "--key-file") {
|
||||||
$n++;
|
$n++;
|
||||||
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
||||||
$privateKeyFile = $ARGV[$n];
|
$secretKeyFile = $ARGV[$n];
|
||||||
} elsif ($arg eq "--key-name") {
|
} elsif ($arg eq "--max-jobs" || $arg eq "-j") {
|
||||||
$n++;
|
$n++;
|
||||||
die "$0: ‘$arg’ requires an argument\n" unless $n < scalar @ARGV;
|
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 "-") {
|
} elsif (substr($arg, 0, 1) eq "-") {
|
||||||
die "$0: unknown flag ‘$arg’\n";
|
die "$0: unknown flag ‘$arg’\n";
|
||||||
} else {
|
} else {
|
||||||
@@ -110,7 +110,7 @@ my %narFiles;
|
|||||||
foreach my $storePath (@storePaths) {
|
foreach my $storePath (@storePaths) {
|
||||||
my $pathHash = substr(basename($storePath), 0, 32);
|
my $pathHash = substr(basename($storePath), 0, 32);
|
||||||
my $narInfoFile = "$destDir/$pathHash.narinfo";
|
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 $narInfo = parseNARInfo($storePath, readFile($narInfoFile), 0, $narInfoFile) or die "cannot read ‘$narInfoFile’\n";
|
||||||
my $narFile = "$destDir/$narInfo->{url}";
|
my $narFile = "$destDir/$narInfo->{url}";
|
||||||
if (-e $narFile) {
|
if (-e $narFile) {
|
||||||
@@ -158,7 +158,7 @@ close NIX;
|
|||||||
# Build the Nix expression.
|
# Build the Nix expression.
|
||||||
print STDERR "building compressed archives...\n";
|
print STDERR "building compressed archives...\n";
|
||||||
my @narPaths;
|
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";
|
or die "cannot run nix-build";
|
||||||
while (<READ>) {
|
while (<READ>) {
|
||||||
chomp;
|
chomp;
|
||||||
@@ -257,9 +257,14 @@ for (my $n = 0; $n < scalar @storePaths2; $n++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined $privateKeyFile && defined $keyName) {
|
if (defined $secretKeyFile) {
|
||||||
my $sig = signString($privateKeyFile, $info);
|
my $s = readFile $secretKeyFile;
|
||||||
$info .= "Signature: 1;$keyName;$sig\n";
|
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);
|
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)
|
static void substitute(std::pair<FdSink, FdSource> & pipes, Path storePath, Path destPath)
|
||||||
{
|
{
|
||||||
writeInt(cmdDumpStorePath, pipes.first);
|
pipes.first << cmdDumpStorePath << storePath;
|
||||||
writeString(storePath, pipes.first);
|
|
||||||
pipes.first.flush();
|
pipes.first.flush();
|
||||||
restorePath(destPath, pipes.second);
|
restorePath(destPath, pipes.second);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
@@ -58,17 +57,17 @@ static void query(std::pair<FdSink, FdSource> & pipes)
|
|||||||
string cmd = tokenized.front();
|
string cmd = tokenized.front();
|
||||||
tokenized.pop_front();
|
tokenized.pop_front();
|
||||||
if (cmd == "have") {
|
if (cmd == "have") {
|
||||||
writeInt(cmdQueryValidPaths, pipes.first);
|
pipes.first
|
||||||
writeInt(0, pipes.first); // don't lock
|
<< cmdQueryValidPaths
|
||||||
writeInt(0, pipes.first); // don't substitute
|
<< 0 // don't lock
|
||||||
writeStrings(tokenized, pipes.first);
|
<< 0 // don't substitute
|
||||||
|
<< tokenized;
|
||||||
pipes.first.flush();
|
pipes.first.flush();
|
||||||
PathSet paths = readStrings<PathSet>(pipes.second);
|
PathSet paths = readStrings<PathSet>(pipes.second);
|
||||||
foreach (PathSet::iterator, i, paths)
|
for (auto & i : paths)
|
||||||
std::cout << *i << std::endl;
|
std::cout << i << std::endl;
|
||||||
} else if (cmd == "info") {
|
} else if (cmd == "info") {
|
||||||
writeInt(cmdQueryPathInfos, pipes.first);
|
pipes.first << cmdQueryPathInfos << tokenized;
|
||||||
writeStrings(tokenized, pipes.first);
|
|
||||||
pipes.first.flush();
|
pipes.first.flush();
|
||||||
while (1) {
|
while (1) {
|
||||||
Path path = readString(pipes.second);
|
Path path = readString(pipes.second);
|
||||||
@@ -80,8 +79,8 @@ static void query(std::pair<FdSink, FdSource> & pipes)
|
|||||||
std::cout << deriver << std::endl;
|
std::cout << deriver << std::endl;
|
||||||
PathSet references = readStorePaths<PathSet>(pipes.second);
|
PathSet references = readStorePaths<PathSet>(pipes.second);
|
||||||
std::cout << references.size() << std::endl;
|
std::cout << references.size() << std::endl;
|
||||||
foreach (PathSet::iterator, i, references)
|
for (auto & i : references)
|
||||||
std::cout << *i << std::endl;
|
std::cout << i << std::endl;
|
||||||
std::cout << readLongLong(pipes.second) << std::endl;
|
std::cout << readLongLong(pipes.second) << 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);
|
std::pair<FdSink, FdSource> pipes = connect(host);
|
||||||
|
|
||||||
/* Exchange the greeting */
|
/* Exchange the greeting */
|
||||||
writeInt(SERVE_MAGIC_1, pipes.first);
|
pipes.first << SERVE_MAGIC_1;
|
||||||
pipes.first.flush();
|
pipes.first.flush();
|
||||||
unsigned int magic = readInt(pipes.second);
|
unsigned int magic = readInt(pipes.second);
|
||||||
if (magic != SERVE_MAGIC_2)
|
if (magic != SERVE_MAGIC_2)
|
||||||
throw Error("protocol mismatch");
|
throw Error("protocol mismatch");
|
||||||
readInt(pipes.second); // Server version, unused for now
|
readInt(pipes.second); // Server version, unused for now
|
||||||
writeInt(SERVE_PROTOCOL_VERSION, pipes.first);
|
pipes.first << SERVE_PROTOCOL_VERSION;
|
||||||
pipes.first.flush();
|
pipes.first.flush();
|
||||||
|
|
||||||
string arg = argv[1];
|
string arg = argv[1];
|
||||||
|
|||||||
@@ -42,11 +42,10 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
|||||||
|
|
||||||
Value * v = &vIn;
|
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;
|
enum { apAttr, apIndex } apType = apAttr;
|
||||||
string attr = *i;
|
|
||||||
unsigned int attrIndex;
|
unsigned int attrIndex;
|
||||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||||
|
|
||||||
@@ -77,15 +76,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
|||||||
|
|
||||||
else if (apType == apIndex) {
|
else if (apType == apIndex) {
|
||||||
|
|
||||||
if (v->type != tList)
|
if (!v->isList())
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
format("the expression selected by the selection path ‘%1%’ should be a list but is %2%")
|
format("the expression selected by the selection path ‘%1%’ should be a list but is %2%")
|
||||||
% attrPath % showType(*v));
|
% 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);
|
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 "common-opts.hh"
|
||||||
#include "../libmain/shared.hh"
|
#include "shared.hh"
|
||||||
|
#include "download.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +54,9 @@ bool parseSearchPathArg(Strings::iterator & i,
|
|||||||
|
|
||||||
Path lookupFileArg(EvalState & state, string s)
|
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);
|
Path p = s.substr(1, s.size() - 2);
|
||||||
return state.findFile(p);
|
return state.findFile(p);
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
namespace nix {
|
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))
|
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) {
|
if (v.type == tThunk) {
|
||||||
Env * env = v.thunk.env;
|
Env * env = v.thunk.env;
|
||||||
@@ -43,7 +43,7 @@ void EvalState::forceValue(Value & v)
|
|||||||
else if (v.type == tApp)
|
else if (v.type == tApp)
|
||||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||||
else if (v.type == tBlackhole)
|
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)
|
inline void EvalState::forceList(Value & v)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
if (v.type != tList)
|
if (!v.isList())
|
||||||
throwTypeError("value is %1% while a list was expected", v);
|
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)
|
inline void EvalState::forceList(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
if (v.type != tList)
|
if (!v.isList())
|
||||||
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
|
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+266
-186
@@ -21,9 +21,6 @@
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define GC_STRDUP strdup
|
|
||||||
#define GC_MALLOC malloc
|
|
||||||
|
|
||||||
#define NEW new
|
#define NEW new
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -32,14 +29,36 @@
|
|||||||
namespace nix {
|
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)
|
static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
if (active.find(&v) != active.end()) {
|
if (active.find(&v) != active.end()) {
|
||||||
str << "<CYCLE>";
|
str << "<CYCLE>";
|
||||||
return;
|
return;
|
||||||
@@ -73,8 +92,8 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
|||||||
str << "{ ";
|
str << "{ ";
|
||||||
typedef std::map<string, Value *> Sorted;
|
typedef std::map<string, Value *> Sorted;
|
||||||
Sorted sorted;
|
Sorted sorted;
|
||||||
foreach (Bindings::iterator, i, *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
sorted[i->name] = i->value;
|
sorted[i.name] = i.value;
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
str << i.first << " = ";
|
str << i.first << " = ";
|
||||||
printValue(str, active, *i.second);
|
printValue(str, active, *i.second);
|
||||||
@@ -83,10 +102,12 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
|||||||
str << "}";
|
str << "}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case tList:
|
case tList1:
|
||||||
|
case tList2:
|
||||||
|
case tListN:
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||||
printValue(str, active, *v.list.elems[n]);
|
printValue(str, active, *v.listElems()[n]);
|
||||||
str << " ";
|
str << " ";
|
||||||
}
|
}
|
||||||
str << "]";
|
str << "]";
|
||||||
@@ -132,7 +153,7 @@ string showType(const Value & v)
|
|||||||
case tPath: return "a path";
|
case tPath: return "a path";
|
||||||
case tNull: return "null";
|
case tNull: return "null";
|
||||||
case tAttrs: return "a set";
|
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 tThunk: return "a thunk";
|
||||||
case tApp: return "a function application";
|
case tApp: return "a function application";
|
||||||
case tLambda: return "a function";
|
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)
|
EvalState::EvalState(const Strings & _searchPath)
|
||||||
: sWith(symbols.create("<with>"))
|
: sWith(symbols.create("<with>"))
|
||||||
, sOutPath(symbols.create("outPath"))
|
, sOutPath(symbols.create("outPath"))
|
||||||
@@ -185,61 +261,25 @@ EvalState::EvalState(const Strings & _searchPath)
|
|||||||
, sLine(symbols.create("line"))
|
, sLine(symbols.create("line"))
|
||||||
, sColumn(symbols.create("column"))
|
, sColumn(symbols.create("column"))
|
||||||
, sFunctor(symbols.create("__functor"))
|
, sFunctor(symbols.create("__functor"))
|
||||||
, repair(false)
|
|
||||||
, baseEnv(allocEnv(128))
|
, baseEnv(allocEnv(128))
|
||||||
, staticBaseEnv(false, 0)
|
, 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";
|
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
restricted = settings.get("restrict-eval", false);
|
||||||
static bool gcInitialised = false;
|
|
||||||
if (!gcInitialised) {
|
|
||||||
|
|
||||||
/* Initialise the Boehm garbage collector. This isn't
|
assert(gcInitialised);
|
||||||
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
|
|
||||||
|
|
||||||
/* Initialise the Nix expression search path. */
|
/* 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 : _searchPath) addToSearchPath(i, true);
|
||||||
for (auto & i : paths) addToSearchPath(i);
|
for (auto & i : paths) addToSearchPath(i);
|
||||||
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
||||||
|
|
||||||
|
clearValue(vEmptySet);
|
||||||
|
vEmptySet.type = tAttrs;
|
||||||
|
vEmptySet.attrs = allocBindings(0);
|
||||||
|
|
||||||
createBaseEnv();
|
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)
|
void EvalState::addConstant(const string & name, Value & v)
|
||||||
{
|
{
|
||||||
Value * v2 = allocValue();
|
Value * v2 = allocValue();
|
||||||
@@ -291,11 +358,6 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
|||||||
throw EvalError(format(s) % 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))
|
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const Pos & pos))
|
||||||
{
|
{
|
||||||
throw EvalError(format(s) % s2 % 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)
|
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()) {
|
if (!context.empty()) {
|
||||||
unsigned int n = 0;
|
unsigned int n = 0;
|
||||||
v.string.context = (const char * *)
|
v.string.context = (const char * *)
|
||||||
GC_MALLOC((context.size() + 1) * sizeof(char *));
|
allocBytes((context.size() + 1) * sizeof(char *));
|
||||||
foreach (PathSet::const_iterator, i, context)
|
for (auto & i : context)
|
||||||
v.string.context[n++] = GC_STRDUP(i->c_str());
|
v.string.context[n++] = dupString(i.c_str());
|
||||||
v.string.context[n] = 0;
|
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)
|
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()
|
Value * EvalState::allocValue()
|
||||||
{
|
{
|
||||||
nrValues++;
|
nrValues++;
|
||||||
return (Value *) GC_MALLOC(sizeof(Value));
|
return (Value *) allocBytes(sizeof(Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -422,10 +484,10 @@ Env & EvalState::allocEnv(unsigned int size)
|
|||||||
|
|
||||||
nrEnvs++;
|
nrEnvs++;
|
||||||
nrValuesInEnvs += size;
|
nrValuesInEnvs += size;
|
||||||
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
|
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||||
env->size = size;
|
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)
|
for (unsigned i = 0; i < size; ++i)
|
||||||
env->values[i] = 0;
|
env->values[i] = 0;
|
||||||
|
|
||||||
@@ -433,37 +495,19 @@ Env & EvalState::allocEnv(unsigned int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
void EvalState::mkList(Value & v, unsigned int size)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
clearValue(v);
|
clearValue(v);
|
||||||
v.type = tList;
|
if (size == 1)
|
||||||
v.list.length = length;
|
v.type = tList1;
|
||||||
v.list.elems = length ? (Value * *) GC_MALLOC(length * sizeof(Value *)) : 0;
|
else if (size == 2)
|
||||||
nrListElems += length;
|
v.type = tList2;
|
||||||
}
|
else {
|
||||||
|
v.type = tListN;
|
||||||
|
v.bigList.size = size;
|
||||||
void EvalState::mkAttrs(Value & v, unsigned int expected)
|
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
|
||||||
{
|
}
|
||||||
clearValue(v);
|
nrListElems += size;
|
||||||
v.type = tAttrs;
|
|
||||||
v.attrs = allocBindings(expected);
|
|
||||||
nrAttrsets++;
|
|
||||||
nrAttrsInAttrsets += expected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -555,7 +599,7 @@ void EvalState::evalFile(const Path & path, Value & v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
startNest(nest, lvlTalkative, format("evaluating file ‘%1%’") % path2);
|
startNest(nest, lvlTalkative, format("evaluating file ‘%1%’") % path2);
|
||||||
Expr * e = parseExprFromFile(path2);
|
Expr * e = parseExprFromFile(checkSourcePath(path2));
|
||||||
try {
|
try {
|
||||||
eval(e, v);
|
eval(e, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
@@ -651,15 +695,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||||||
environment, while the inherited attributes are evaluated
|
environment, while the inherited attributes are evaluated
|
||||||
in the original environment. */
|
in the original environment. */
|
||||||
unsigned int displ = 0;
|
unsigned int displ = 0;
|
||||||
foreach (AttrDefs::iterator, i, attrs) {
|
for (auto & i : attrs) {
|
||||||
Value * vAttr;
|
Value * vAttr;
|
||||||
if (hasOverrides && !i->second.inherited) {
|
if (hasOverrides && !i.second.inherited) {
|
||||||
vAttr = state.allocValue();
|
vAttr = state.allocValue();
|
||||||
mkThunk(*vAttr, env2, i->second.e);
|
mkThunk(*vAttr, env2, i.second.e);
|
||||||
} else
|
} 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;
|
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
|
/* If the rec contains an attribute called `__overrides', then
|
||||||
@@ -690,25 +734,25 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
foreach (AttrDefs::iterator, i, attrs)
|
for (auto & i : attrs)
|
||||||
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
|
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), &i.second.pos));
|
||||||
|
|
||||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||||
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
Value nameVal;
|
Value nameVal;
|
||||||
i->nameExpr->eval(state, *dynamicEnv, nameVal);
|
i.nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||||
state.forceValue(nameVal);
|
state.forceValue(nameVal, i.pos);
|
||||||
if (nameVal.type == tNull)
|
if (nameVal.type == tNull)
|
||||||
continue;
|
continue;
|
||||||
state.forceStringNoCtx(nameVal);
|
state.forceStringNoCtx(nameVal);
|
||||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
||||||
Bindings::iterator j = v.attrs->find(nameSym);
|
Bindings::iterator j = v.attrs->find(nameSym);
|
||||||
if (j != v.attrs->end())
|
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 */
|
/* 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
|
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
|
while the inherited attributes are evaluated in the original
|
||||||
environment. */
|
environment. */
|
||||||
unsigned int displ = 0;
|
unsigned int displ = 0;
|
||||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||||
|
|
||||||
body->eval(state, env2, v);
|
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)
|
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
state.mkList(v, elems.size());
|
state.mkList(v, elems.size());
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
for (unsigned int n = 0; n < elems.size(); ++n)
|
||||||
v.list.elems[n] = elems[n]->maybeThunk(state, env);
|
v.listElems()[n] = elems[n]->maybeThunk(state, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value * v2 = state.lookupVar(&env, *this, false);
|
Value * v2 = state.lookupVar(&env, *this, false);
|
||||||
state.forceValue(*v2);
|
state.forceValue(*v2, pos);
|
||||||
v = *v2;
|
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;
|
unsigned long nrLookups = 0;
|
||||||
|
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
@@ -760,12 +821,12 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
for (auto & i : attrPath) {
|
||||||
nrLookups++;
|
nrLookups++;
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(*i, state, env);
|
Symbol name = getName(i, state, env);
|
||||||
if (def) {
|
if (def) {
|
||||||
state.forceValue(*vAttrs);
|
state.forceValue(*vAttrs, pos);
|
||||||
if (vAttrs->type != tAttrs ||
|
if (vAttrs->type != tAttrs ||
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
{
|
{
|
||||||
@@ -774,28 +835,20 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.forceAttrs(*vAttrs, pos);
|
state.forceAttrs(*vAttrs, pos);
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
AttrPath staticPath;
|
throwEvalError("attribute ‘%1%’ missing, at %2%", name, pos);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
pos2 = j->pos;
|
pos2 = j->pos;
|
||||||
if (state.countCalls && pos2) state.attrSelects[*pos2]++;
|
if (state.countCalls && pos2) state.attrSelects[*pos2]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.forceValue(*vAttrs);
|
state.forceValue(*vAttrs, ( pos2 != NULL ? *pos2 : this->pos ) );
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (pos2 && pos2->file != state.sDerivationNix)
|
if (pos2 && pos2->file != state.sDerivationNix)
|
||||||
addErrorPrefix(e, "while evaluating the attribute ‘%1%’ at %2%:\n",
|
addErrorPrefix(e, "while evaluating the attribute ‘%1%’ at %2%:\n",
|
||||||
showAttrPath(attrPath), *pos2);
|
showAttrPath(state, env, attrPath), *pos2);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -810,10 +863,10 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||||||
|
|
||||||
e->eval(state, env, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
|
||||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
for (auto & i : attrPath) {
|
||||||
state.forceValue(*vAttrs);
|
state.forceValue(*vAttrs);
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(*i, state, env);
|
Symbol name = getName(i, state, env);
|
||||||
if (vAttrs->type != tAttrs ||
|
if (vAttrs->type != tAttrs ||
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
(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) {
|
if (fun.type == tAttrs) {
|
||||||
auto found = fun.attrs->find(sFunctor);
|
auto found = fun.attrs->find(sFunctor);
|
||||||
if (found != fun.attrs->end()) {
|
if (found != fun.attrs->end()) {
|
||||||
forceValue(*found->value);
|
forceValue(*found->value, pos);
|
||||||
Value * v2 = allocValue();
|
Value * v2 = allocValue();
|
||||||
callFunction(*found->value, fun, *v2, pos);
|
callFunction(*found->value, fun, *v2, pos);
|
||||||
forceValue(*v2);
|
forceValue(*v2, pos);
|
||||||
return callFunction(*v2, arg, v, 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
|
there is no matching actual argument but the formal
|
||||||
argument has a default, use the default. */
|
argument has a default, use the default. */
|
||||||
unsigned int attrsUsed = 0;
|
unsigned int attrsUsed = 0;
|
||||||
foreach (Formals::Formals_::iterator, i, lambda.formals->formals) {
|
for (auto & i : lambda.formals->formals) {
|
||||||
Bindings::iterator j = arg.attrs->find(i->name);
|
Bindings::iterator j = arg.attrs->find(i.name);
|
||||||
if (j == arg.attrs->end()) {
|
if (j == arg.attrs->end()) {
|
||||||
if (!i->def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
|
if (!i.def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
|
||||||
lambda, i->name, pos);
|
lambda, i.name, pos);
|
||||||
env2.values[displ++] = i->def->maybeThunk(*this, env2);
|
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||||
} else {
|
} else {
|
||||||
attrsUsed++;
|
attrsUsed++;
|
||||||
env2.values[displ++] = j->value;
|
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()) {
|
if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) {
|
||||||
/* Nope, so show the first unexpected argument to the
|
/* Nope, so show the first unexpected argument to the
|
||||||
user. */
|
user. */
|
||||||
foreach (Bindings::iterator, i, *arg.attrs)
|
for (auto & i : *arg.attrs)
|
||||||
if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end())
|
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
|
||||||
throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i->name, pos);
|
throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i.name, pos);
|
||||||
abort(); // can't happen
|
abort(); // can't happen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -987,12 +1040,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||||||
Value * actualArgs = allocValue();
|
Value * actualArgs = allocValue();
|
||||||
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
|
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
|
||||||
|
|
||||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
for (auto & i : fun.lambda.fun->formals->formals) {
|
||||||
Bindings::iterator j = args.find(i->name);
|
Bindings::iterator j = args.find(i.name);
|
||||||
if (j != args.end())
|
if (j != args.end())
|
||||||
actualArgs->attrs->push_back(*j);
|
actualArgs->attrs->push_back(*j);
|
||||||
else if (!i->def)
|
else if (!i.def)
|
||||||
throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i->name);
|
throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
actualArgs->attrs->sort();
|
actualArgs->attrs->sort();
|
||||||
@@ -1120,20 +1173,21 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, co
|
|||||||
unsigned int len = 0;
|
unsigned int len = 0;
|
||||||
for (unsigned int n = 0; n < nrLists; ++n) {
|
for (unsigned int n = 0; n < nrLists; ++n) {
|
||||||
forceList(*lists[n], pos);
|
forceList(*lists[n], pos);
|
||||||
unsigned int l = lists[n]->list.length;
|
unsigned int l = lists[n]->listSize();
|
||||||
len += l;
|
len += l;
|
||||||
if (l) nonEmpty = lists[n];
|
if (l) nonEmpty = lists[n];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nonEmpty && len == nonEmpty->list.length) {
|
if (nonEmpty && len == nonEmpty->listSize()) {
|
||||||
v = *nonEmpty;
|
v = *nonEmpty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mkList(v, len);
|
mkList(v, len);
|
||||||
|
auto out = v.listElems();
|
||||||
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
|
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
|
||||||
unsigned int l = lists[n]->list.length;
|
unsigned int l = lists[n]->listSize();
|
||||||
memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *));
|
memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
|
||||||
pos += l;
|
pos += l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1148,9 +1202,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||||||
bool first = !forceString;
|
bool first = !forceString;
|
||||||
ValueType firstType = tString;
|
ValueType firstType = tString;
|
||||||
|
|
||||||
foreach (vector<Expr *>::iterator, i, *es) {
|
for (auto & i : *es) {
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
(*i)->eval(state, env, vTmp);
|
i->eval(state, env, vTmp);
|
||||||
|
|
||||||
/* If the first element is a path, then the result will also
|
/* 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,
|
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) {
|
else if (firstType == tPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throwEvalError("a string that refers to a store path cannot be appended to a path, at %1%", pos);
|
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
|
} else
|
||||||
mkString(v, s.str(), context);
|
mkString(v, s.str(), context);
|
||||||
}
|
}
|
||||||
@@ -1199,13 +1254,18 @@ void EvalState::forceValueDeep(Value & v)
|
|||||||
forceValue(v);
|
forceValue(v);
|
||||||
|
|
||||||
if (v.type == tAttrs) {
|
if (v.type == tAttrs) {
|
||||||
foreach (Bindings::iterator, i, *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
recurse(*i->value);
|
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) {
|
else if (v.isList()) {
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
recurse(*v.list.elems[n]);
|
recurse(*v.listElems()[n]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1215,7 +1275,7 @@ void EvalState::forceValueDeep(Value & v)
|
|||||||
|
|
||||||
NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v, pos);
|
||||||
if (v.type != tInt)
|
if (v.type != tInt)
|
||||||
throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
|
throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
|
||||||
return v.integer;
|
return v.integer;
|
||||||
@@ -1241,7 +1301,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
|||||||
|
|
||||||
string EvalState::forceString(Value & v, const Pos & pos)
|
string EvalState::forceString(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v, pos);
|
||||||
if (v.type != tString) {
|
if (v.type != tString) {
|
||||||
if (pos)
|
if (pos)
|
||||||
throwTypeError("value is %1% while a string was expected, at %2%", v, 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 == tInt) return int2String(v.integer);
|
||||||
if (v.type == tNull) return "";
|
if (v.type == tNull) return "";
|
||||||
|
|
||||||
if (v.type == tList) {
|
if (v.isList()) {
|
||||||
string result;
|
string result;
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||||
result += coerceToString(pos, *v.list.elems[n],
|
result += coerceToString(pos, *v.listElems()[n],
|
||||||
context, coerceMore, copyToStore);
|
context, coerceMore, copyToStore);
|
||||||
if (n < v.list.length - 1
|
if (n < v.listSize() - 1
|
||||||
/* !!! not quite correct */
|
/* !!! 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 += " ";
|
result += " ";
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -1357,8 +1417,8 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
|||||||
dstPath = srcToStore[path];
|
dstPath = srcToStore[path];
|
||||||
else {
|
else {
|
||||||
dstPath = settings.readOnlyMode
|
dstPath = settings.readOnlyMode
|
||||||
? computeStorePathForPath(path).first
|
? computeStorePathForPath(checkSourcePath(path)).first
|
||||||
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
|
: store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
|
||||||
srcToStore[path] = dstPath;
|
srcToStore[path] = dstPath;
|
||||||
printMsg(lvlChatty, format("copied source ‘%1%’ -> ‘%2%’")
|
printMsg(lvlChatty, format("copied source ‘%1%’ -> ‘%2%’")
|
||||||
% path % dstPath);
|
% path % dstPath);
|
||||||
@@ -1407,10 +1467,12 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||||||
case tNull:
|
case tNull:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case tList:
|
case tList1:
|
||||||
if (v1.list.length != v2.list.length) return false;
|
case tList2:
|
||||||
for (unsigned int n = 0; n < v1.list.length; ++n)
|
case tListN:
|
||||||
if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
|
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;
|
return true;
|
||||||
|
|
||||||
case tAttrs: {
|
case tAttrs: {
|
||||||
@@ -1466,6 +1528,7 @@ void EvalState::printStats()
|
|||||||
|
|
||||||
printMsg(v, format(" time elapsed: %1%") % cpuTime);
|
printMsg(v, format(" time elapsed: %1%") % cpuTime);
|
||||||
printMsg(v, format(" size of a value: %1%") % sizeof(Value));
|
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(" environments allocated: %1% (%2% bytes)") % nrEnvs % bEnvs);
|
||||||
printMsg(v, format(" list elements: %1% (%2% bytes)") % nrListElems % bLists);
|
printMsg(v, format(" list elements: %1% (%2% bytes)") % nrListElems % bLists);
|
||||||
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
|
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(" number of function calls: %1%") % nrFunctionCalls);
|
||||||
printMsg(v, format(" total allocations: %1% bytes") % (bEnvs + bLists + bValues + bAttrsets));
|
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) {
|
if (countCalls) {
|
||||||
v = lvlInfo;
|
v = lvlInfo;
|
||||||
|
|
||||||
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
||||||
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
||||||
PrimOpCalls_ primOpCalls_;
|
PrimOpCalls_ primOpCalls_;
|
||||||
foreach (PrimOpCalls::iterator, i, primOpCalls)
|
for (auto & i : primOpCalls)
|
||||||
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
|
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i.second, i.first));
|
||||||
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
|
for (auto i = primOpCalls_.rbegin(); i != primOpCalls_.rend(); ++i)
|
||||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||||
|
|
||||||
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
|
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
|
||||||
typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
|
typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
|
||||||
FunctionCalls_ functionCalls_;
|
FunctionCalls_ functionCalls_;
|
||||||
foreach (FunctionCalls::iterator, i, functionCalls)
|
for (auto & i : functionCalls)
|
||||||
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
|
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i.second, i.first));
|
||||||
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
|
for (auto i = functionCalls_.rbegin(); i != functionCalls_.rend(); ++i)
|
||||||
printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
|
printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
|
||||||
|
|
||||||
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
|
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
|
||||||
typedef std::multimap<unsigned int, Pos> AttrSelects_;
|
typedef std::multimap<unsigned int, Pos> AttrSelects_;
|
||||||
AttrSelects_ attrSelects_;
|
AttrSelects_ attrSelects_;
|
||||||
foreach (AttrSelects::iterator, i, attrSelects)
|
for (auto & i : attrSelects)
|
||||||
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
attrSelects_.insert(std::pair<unsigned int, Pos>(i.second, i.first));
|
||||||
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
|
for (auto i = attrSelects_.rbegin(); i != attrSelects_.rend(); ++i)
|
||||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1543,12 +1613,22 @@ size_t valueSize(Value & v)
|
|||||||
sz += doString(v.path);
|
sz += doString(v.path);
|
||||||
break;
|
break;
|
||||||
case tAttrs:
|
case tAttrs:
|
||||||
for (auto & i : *v.attrs)
|
if (seen.find(v.attrs) == seen.end()) {
|
||||||
sz += doValue(*i.value);
|
seen.insert(v.attrs);
|
||||||
|
sz += sizeof(Bindings) + sizeof(Attr) * v.attrs->capacity();
|
||||||
|
for (auto & i : *v.attrs)
|
||||||
|
sz += doValue(*i.value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case tList:
|
case tList1:
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
case tList2:
|
||||||
sz += doValue(*v.list.elems[n]);
|
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;
|
break;
|
||||||
case tThunk:
|
case tThunk:
|
||||||
sz += doEnv(*v.thunk.env);
|
sz += doEnv(*v.thunk.env);
|
||||||
@@ -1580,7 +1660,7 @@ size_t valueSize(Value & v)
|
|||||||
if (seen.find(&env) != seen.end()) return 0;
|
if (seen.find(&env) != seen.end()) return 0;
|
||||||
seen.insert(&env);
|
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)
|
for (unsigned int i = 0; i < env.size; ++i)
|
||||||
if (env.values[i])
|
if (env.values[i])
|
||||||
|
|||||||
+29
-78
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "attr-set.hh"
|
||||||
#include "value.hh"
|
#include "value.hh"
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
@@ -18,68 +19,6 @@ namespace nix {
|
|||||||
class EvalState;
|
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);
|
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;
|
typedef list<std::pair<string, Path> > SearchPath;
|
||||||
|
|
||||||
|
|
||||||
|
/* Initialise the Boehm GC, if applicable. */
|
||||||
|
void initGC();
|
||||||
|
|
||||||
|
|
||||||
class EvalState
|
class EvalState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -131,7 +74,13 @@ public:
|
|||||||
|
|
||||||
/* If set, force copying files to the Nix store even if they
|
/* If set, force copying files to the Nix store even if they
|
||||||
already exist there. */
|
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:
|
private:
|
||||||
SrcToStore srcToStore;
|
SrcToStore srcToStore;
|
||||||
@@ -153,6 +102,8 @@ public:
|
|||||||
|
|
||||||
void addToSearchPath(const string & s, bool warn = false);
|
void addToSearchPath(const string & s, bool warn = false);
|
||||||
|
|
||||||
|
Path checkSourcePath(const Path & path);
|
||||||
|
|
||||||
/* Parse a Nix expression from the specified file. */
|
/* Parse a Nix expression from the specified file. */
|
||||||
Expr * parseExprFromFile(const Path & path);
|
Expr * parseExprFromFile(const Path & path);
|
||||||
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
||||||
@@ -169,7 +120,7 @@ public:
|
|||||||
|
|
||||||
/* Look up a file in the search path. */
|
/* Look up a file in the search path. */
|
||||||
Path findFile(const string & 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
|
/* Evaluate an expression to normal form, storing the result in
|
||||||
value `v'. */
|
value `v'. */
|
||||||
@@ -185,7 +136,7 @@ public:
|
|||||||
of the evaluation of the thunk. If `v' is a delayed function
|
of the evaluation of the thunk. If `v' is a delayed function
|
||||||
application, call the function and overwrite `v' with the
|
application, call the function and overwrite `v' with the
|
||||||
result. Otherwise, this is a no-op. */
|
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
|
/* Force a value, then recursively force list elements and
|
||||||
attributes. */
|
attributes. */
|
||||||
@@ -232,7 +183,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned int baseEnvDispl;
|
unsigned int baseEnvDispl = 0;
|
||||||
|
|
||||||
void createBaseEnv();
|
void createBaseEnv();
|
||||||
|
|
||||||
@@ -278,7 +229,7 @@ public:
|
|||||||
Bindings * allocBindings(Bindings::size_t capacity);
|
Bindings * allocBindings(Bindings::size_t capacity);
|
||||||
|
|
||||||
void mkList(Value & v, unsigned int length);
|
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 mkThunk_(Value & v, Expr * expr);
|
||||||
void mkPos(Value & v, Pos * pos);
|
void mkPos(Value & v, Pos * pos);
|
||||||
|
|
||||||
@@ -289,17 +240,17 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned long nrEnvs;
|
unsigned long nrEnvs = 0;
|
||||||
unsigned long nrValuesInEnvs;
|
unsigned long nrValuesInEnvs = 0;
|
||||||
unsigned long nrValues;
|
unsigned long nrValues = 0;
|
||||||
unsigned long nrListElems;
|
unsigned long nrListElems = 0;
|
||||||
unsigned long nrAttrsets;
|
unsigned long nrAttrsets = 0;
|
||||||
unsigned long nrAttrsInAttrsets;
|
unsigned long nrAttrsInAttrsets = 0;
|
||||||
unsigned long nrOpUpdates;
|
unsigned long nrOpUpdates = 0;
|
||||||
unsigned long nrOpUpdateValuesCopied;
|
unsigned long nrOpUpdateValuesCopied = 0;
|
||||||
unsigned long nrListConcats;
|
unsigned long nrListConcats = 0;
|
||||||
unsigned long nrPrimOpCalls;
|
unsigned long nrPrimOpCalls = 0;
|
||||||
unsigned long nrFunctionCalls;
|
unsigned long nrFunctionCalls = 0;
|
||||||
|
|
||||||
bool countCalls;
|
bool countCalls;
|
||||||
|
|
||||||
|
|||||||
+19
-19
@@ -39,9 +39,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs()
|
|||||||
state->forceList(*i->value, *i->pos);
|
state->forceList(*i->value, *i->pos);
|
||||||
|
|
||||||
/* For each output... */
|
/* 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. */
|
/* 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));
|
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
||||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||||
state->forceAttrs(*out->value);
|
state->forceAttrs(*out->value);
|
||||||
@@ -85,8 +85,8 @@ StringSet DrvInfo::queryMetaNames()
|
|||||||
{
|
{
|
||||||
StringSet res;
|
StringSet res;
|
||||||
if (!getMeta()) return res;
|
if (!getMeta()) return res;
|
||||||
foreach (Bindings::iterator, i, *meta)
|
for (auto & i : *meta)
|
||||||
res.insert(i->name);
|
res.insert(i.name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,16 +94,16 @@ StringSet DrvInfo::queryMetaNames()
|
|||||||
bool DrvInfo::checkMeta(Value & v)
|
bool DrvInfo::checkMeta(Value & v)
|
||||||
{
|
{
|
||||||
state->forceValue(v);
|
state->forceValue(v);
|
||||||
if (v.type == tList) {
|
if (v.isList()) {
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
if (!checkMeta(*v.list.elems[n])) return false;
|
if (!checkMeta(*v.listElems()[n])) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (v.type == tAttrs) {
|
else if (v.type == tAttrs) {
|
||||||
Bindings::iterator i = v.attrs->find(state->sOutPath);
|
Bindings::iterator i = v.attrs->find(state->sOutPath);
|
||||||
if (i != v.attrs->end()) return false;
|
if (i != v.attrs->end()) return false;
|
||||||
foreach (Bindings::iterator, i, *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
if (!checkMeta(*i->value)) return false;
|
if (!checkMeta(*i.value)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else return v.type == tInt || v.type == tBool || v.type == tString;
|
else return v.type == tInt || v.type == tBool || v.type == tString;
|
||||||
@@ -255,13 +255,13 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||||||
precedence). */
|
precedence). */
|
||||||
typedef std::map<string, Symbol> SortedSymbols;
|
typedef std::map<string, Symbol> SortedSymbols;
|
||||||
SortedSymbols attrs;
|
SortedSymbols attrs;
|
||||||
foreach (Bindings::iterator, i, *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
attrs.insert(std::pair<string, Symbol>(i->name, i->name));
|
attrs.insert(std::pair<string, Symbol>(i.name, i.name));
|
||||||
|
|
||||||
foreach (SortedSymbols::iterator, i, attrs) {
|
for (auto & i : attrs) {
|
||||||
startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i->first);
|
startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i.first);
|
||||||
string pathPrefix2 = addToPath(pathPrefix, i->first);
|
string pathPrefix2 = addToPath(pathPrefix, i.first);
|
||||||
Value & v2(*v.attrs->find(i->second)->value);
|
Value & v2(*v.attrs->find(i.second)->value);
|
||||||
if (combineChannels)
|
if (combineChannels)
|
||||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
else if (getDerivation(state, v2, pathPrefix2, 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) {
|
else if (v.isList()) {
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||||
startNest(nest, lvlDebug,
|
startNest(nest, lvlDebug,
|
||||||
format("evaluating list element"));
|
format("evaluating list element"));
|
||||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, 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++;
|
s++;
|
||||||
state.mkList(v, values.size());
|
state.mkList(v, values.size());
|
||||||
for (size_t n = 0; n < values.size(); ++n)
|
for (size_t n = 0; n < values.size(); ++n)
|
||||||
v.list.elems[n] = values[n];
|
v.listElems()[n] = values[n];
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (*s == '{') {
|
else if (*s == '{') {
|
||||||
|
|||||||
+22
-33
@@ -1,6 +1,9 @@
|
|||||||
%option reentrant bison-bridge bison-locations
|
%option reentrant bison-bridge bison-locations
|
||||||
%option noyywrap
|
%option noyywrap
|
||||||
%option never-interactive
|
%option never-interactive
|
||||||
|
%option stack
|
||||||
|
%option nodefault
|
||||||
|
%option nounput noyy_top_state
|
||||||
|
|
||||||
|
|
||||||
%x STRING
|
%x STRING
|
||||||
@@ -74,12 +77,16 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
|||||||
#define YY_USER_INIT initLoc(yylloc)
|
#define YY_USER_INIT initLoc(yylloc)
|
||||||
#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng);
|
#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\_\'\-]*
|
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
|
||||||
INT [0-9]+
|
INT [0-9]+
|
||||||
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
|
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
|
||||||
|
HPATH \~(\/[a-zA-Z0-9\.\_\-\+]+)+
|
||||||
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[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\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
|
URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
|
||||||
|
|
||||||
@@ -117,23 +124,24 @@ or { return OR_KW; }
|
|||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
\$\{ { return DOLLAR_CURLY; }
|
\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
|
||||||
|
\{ { PUSH_STATE(INITIAL); return '{'; }
|
||||||
|
\} { POP_STATE(); return '}'; }
|
||||||
|
|
||||||
\" { BEGIN(STRING); return '"'; }
|
\" { PUSH_STATE(STRING); return '"'; }
|
||||||
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
|
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" |
|
||||||
/* !!! Not quite right: we want a follow restriction on
|
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ {
|
||||||
"$", it shouldn't be followed by a "{". Right now
|
/* It is impossible to match strings ending with '$' with one
|
||||||
"$\"" will be consumed as part of a string, rather
|
regex because trailing contexts are only valid at the end
|
||||||
than a "$" followed by the string terminator.
|
of a rule. (A sane but undocumented limitation.) */
|
||||||
Disallow "$\"" for now. */
|
|
||||||
yylval->e = unescapeStr(data->symbols, yytext);
|
yylval->e = unescapeStr(data->symbols, yytext);
|
||||||
return STR;
|
return STR;
|
||||||
}
|
}
|
||||||
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
<STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
|
||||||
<STRING>\" { BEGIN(INITIAL); return '"'; }
|
<STRING>\" { POP_STATE(); return '"'; }
|
||||||
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
<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>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||||
yylval->e = new ExprIndStr(yytext);
|
yylval->e = new ExprIndStr(yytext);
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
@@ -150,8 +158,8 @@ or { return OR_KW; }
|
|||||||
yylval->e = unescapeStr(data->symbols, yytext + 2);
|
yylval->e = unescapeStr(data->symbols, yytext + 2);
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
<IND_STRING>\$\{ { PUSH_STATE(INITIAL); return DOLLAR_CURLY; }
|
||||||
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
|
||||||
<IND_STRING>\' {
|
<IND_STRING>\' {
|
||||||
yylval->e = new ExprIndStr("'");
|
yylval->e = new ExprIndStr("'");
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
@@ -159,6 +167,7 @@ or { return OR_KW; }
|
|||||||
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||||
|
|
||||||
{PATH} { yylval->path = strdup(yytext); return PATH; }
|
{PATH} { yylval->path = strdup(yytext); return PATH; }
|
||||||
|
{HPATH} { yylval->path = strdup(yytext); return HPATH; }
|
||||||
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
|
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
|
||||||
{URI} { yylval->uri = strdup(yytext); return URI; }
|
{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 drvNamesFromArgs(const Strings & opArgs)
|
||||||
{
|
{
|
||||||
DrvNames result;
|
DrvNames result;
|
||||||
foreach (Strings::const_iterator, i, opArgs)
|
for (auto & i : opArgs)
|
||||||
result.push_back(DrvName(*i));
|
result.push_back(DrvName(i));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+52
-55
@@ -97,21 +97,21 @@ void ExprAttrs::show(std::ostream & str)
|
|||||||
{
|
{
|
||||||
if (recursive) str << "rec ";
|
if (recursive) str << "rec ";
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
foreach (AttrDefs::iterator, i, attrs)
|
for (auto & i : attrs)
|
||||||
if (i->second.inherited)
|
if (i.second.inherited)
|
||||||
str << "inherit " << i->first << " " << "; ";
|
str << "inherit " << i.first << " " << "; ";
|
||||||
else
|
else
|
||||||
str << i->first << " = " << *i->second.e << "; ";
|
str << i.first << " = " << *i.second.e << "; ";
|
||||||
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs)
|
for (auto & i : dynamicAttrs)
|
||||||
str << "\"${" << *i->nameExpr << "}\" = " << *i->valueExpr << "; ";
|
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
|
||||||
str << "}";
|
str << "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprList::show(std::ostream & str)
|
void ExprList::show(std::ostream & str)
|
||||||
{
|
{
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
foreach (vector<Expr *>::iterator, i, elems)
|
for (auto & i : elems)
|
||||||
str << "(" << **i << ") ";
|
str << "(" << *i << ") ";
|
||||||
str << "]";
|
str << "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,10 +121,10 @@ void ExprLambda::show(std::ostream & str)
|
|||||||
if (matchAttrs) {
|
if (matchAttrs) {
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
foreach (Formals::Formals_::iterator, i, formals->formals) {
|
for (auto & i : formals->formals) {
|
||||||
if (first) first = false; else str << ", ";
|
if (first) first = false; else str << ", ";
|
||||||
str << i->name;
|
str << i.name;
|
||||||
if (i->def) str << " ? " << *i->def;
|
if (i.def) str << " ? " << *i.def;
|
||||||
}
|
}
|
||||||
if (formals->ellipsis) {
|
if (formals->ellipsis) {
|
||||||
if (!first) str << ", ";
|
if (!first) str << ", ";
|
||||||
@@ -140,12 +140,12 @@ void ExprLambda::show(std::ostream & str)
|
|||||||
void ExprLet::show(std::ostream & str)
|
void ExprLet::show(std::ostream & str)
|
||||||
{
|
{
|
||||||
str << "(let ";
|
str << "(let ";
|
||||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
if (i->second.inherited) {
|
if (i.second.inherited) {
|
||||||
str << "inherit " << i->first << "; ";
|
str << "inherit " << i.first << "; ";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
str << i->first << " = " << *i->second.e << "; ";
|
str << i.first << " = " << *i.second.e << "; ";
|
||||||
str << "in " << *body << ")";
|
str << "in " << *body << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,9 +173,9 @@ void ExprConcatStrings::show(std::ostream & str)
|
|||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
str << "(";
|
str << "(";
|
||||||
foreach (vector<Expr *>::iterator, i, *es) {
|
for (auto & i : *es) {
|
||||||
if (first) first = false; else str << " + ";
|
if (first) first = false; else str << " + ";
|
||||||
str << **i;
|
str << *i;
|
||||||
}
|
}
|
||||||
str << ")";
|
str << ")";
|
||||||
}
|
}
|
||||||
@@ -191,7 +191,7 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
|
|||||||
if (!pos)
|
if (!pos)
|
||||||
str << "undefined position";
|
str << "undefined position";
|
||||||
else
|
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;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,15 +200,12 @@ string showAttrPath(const AttrPath & attrPath)
|
|||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
for (auto & i : attrPath) {
|
||||||
if (!first)
|
if (!first) out << '.'; else first = false;
|
||||||
out << '.';
|
if (i.symbol.set())
|
||||||
|
out << i.symbol;
|
||||||
else
|
else
|
||||||
first = false;
|
out << "\"${" << *i.expr << "}\"";
|
||||||
if (i->symbol.set())
|
|
||||||
out << i->symbol;
|
|
||||||
else
|
|
||||||
out << "\"${" << *i->expr << "}\"";
|
|
||||||
}
|
}
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
@@ -270,17 +267,17 @@ void ExprSelect::bindVars(const StaticEnv & env)
|
|||||||
{
|
{
|
||||||
e->bindVars(env);
|
e->bindVars(env);
|
||||||
if (def) def->bindVars(env);
|
if (def) def->bindVars(env);
|
||||||
foreach (AttrPath::iterator, i, attrPath)
|
for (auto & i : attrPath)
|
||||||
if (!i->symbol.set())
|
if (!i.symbol.set())
|
||||||
i->expr->bindVars(env);
|
i.expr->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
||||||
{
|
{
|
||||||
e->bindVars(env);
|
e->bindVars(env);
|
||||||
foreach (AttrPath::iterator, i, attrPath)
|
for (auto & i : attrPath)
|
||||||
if (!i->symbol.set())
|
if (!i.symbol.set())
|
||||||
i->expr->bindVars(env);
|
i.expr->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAttrs::bindVars(const StaticEnv & env)
|
void ExprAttrs::bindVars(const StaticEnv & env)
|
||||||
@@ -292,27 +289,27 @@ void ExprAttrs::bindVars(const StaticEnv & env)
|
|||||||
dynamicEnv = &newEnv;
|
dynamicEnv = &newEnv;
|
||||||
|
|
||||||
unsigned int displ = 0;
|
unsigned int displ = 0;
|
||||||
foreach (AttrDefs::iterator, i, attrs)
|
for (auto & i : attrs)
|
||||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
newEnv.vars[i.first] = i.second.displ = displ++;
|
||||||
|
|
||||||
foreach (AttrDefs::iterator, i, attrs)
|
for (auto & i : attrs)
|
||||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
foreach (AttrDefs::iterator, i, attrs)
|
for (auto & i : attrs)
|
||||||
i->second.e->bindVars(env);
|
i.second.e->bindVars(env);
|
||||||
|
|
||||||
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
i->nameExpr->bindVars(*dynamicEnv);
|
i.nameExpr->bindVars(*dynamicEnv);
|
||||||
i->valueExpr->bindVars(*dynamicEnv);
|
i.valueExpr->bindVars(*dynamicEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprList::bindVars(const StaticEnv & env)
|
void ExprList::bindVars(const StaticEnv & env)
|
||||||
{
|
{
|
||||||
foreach (vector<Expr *>::iterator, i, elems)
|
for (auto & i : elems)
|
||||||
(*i)->bindVars(env);
|
i->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLambda::bindVars(const StaticEnv & 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 (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||||
|
|
||||||
if (matchAttrs) {
|
if (matchAttrs) {
|
||||||
foreach (Formals::Formals_::iterator, i, formals->formals)
|
for (auto & i : formals->formals)
|
||||||
newEnv.vars[i->name] = displ++;
|
newEnv.vars[i.name] = displ++;
|
||||||
|
|
||||||
foreach (Formals::Formals_::iterator, i, formals->formals)
|
for (auto & i : formals->formals)
|
||||||
if (i->def) i->def->bindVars(newEnv);
|
if (i.def) i.def->bindVars(newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
body->bindVars(newEnv);
|
body->bindVars(newEnv);
|
||||||
@@ -339,11 +336,11 @@ void ExprLet::bindVars(const StaticEnv & env)
|
|||||||
StaticEnv newEnv(false, &env);
|
StaticEnv newEnv(false, &env);
|
||||||
|
|
||||||
unsigned int displ = 0;
|
unsigned int displ = 0;
|
||||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
newEnv.vars[i.first] = i.second.displ = displ++;
|
||||||
|
|
||||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
||||||
|
|
||||||
body->bindVars(newEnv);
|
body->bindVars(newEnv);
|
||||||
}
|
}
|
||||||
@@ -387,8 +384,8 @@ void ExprOpNot::bindVars(const StaticEnv & env)
|
|||||||
|
|
||||||
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||||
{
|
{
|
||||||
foreach (vector<Expr *>::iterator, i, *es)
|
for (auto & i : *es)
|
||||||
(*i)->bindVars(env);
|
i->bindVars(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPos::bindVars(const StaticEnv & env)
|
void ExprPos::bindVars(const StaticEnv & env)
|
||||||
@@ -422,8 +419,8 @@ string ExprLambda::showNamePos() const
|
|||||||
size_t SymbolTable::totalSize() const
|
size_t SymbolTable::totalSize() const
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
foreach (Symbols::const_iterator, i, symbols)
|
for (auto & i : symbols)
|
||||||
n += i->size();
|
n += i.size();
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ MakeError(ThrownError, AssertionError)
|
|||||||
MakeError(Abort, EvalError)
|
MakeError(Abort, EvalError)
|
||||||
MakeError(TypeError, EvalError)
|
MakeError(TypeError, EvalError)
|
||||||
MakeError(UndefinedVarError, Error)
|
MakeError(UndefinedVarError, Error)
|
||||||
|
MakeError(RestrictedPathError, Error)
|
||||||
|
|
||||||
|
|
||||||
/* Position objects. */
|
/* 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 */
|
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||||
unsigned int minIndent = 1000000;
|
unsigned int minIndent = 1000000;
|
||||||
unsigned int curIndent = 0;
|
unsigned int curIndent = 0;
|
||||||
foreach (vector<Expr *>::iterator, i, es) {
|
for (auto & i : es) {
|
||||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
/* Anti-quotations end the current start-of-line whitespace. */
|
/* Anti-quotations end the current start-of-line whitespace. */
|
||||||
if (atStartOfLine) {
|
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)
|
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||||
{
|
{
|
||||||
return Pos(data->path, loc.first_line, loc.first_column);
|
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 <id> ID ATTRPATH
|
||||||
%token <e> STR IND_STR
|
%token <e> STR IND_STR
|
||||||
%token <n> INT
|
%token <n> INT
|
||||||
%token <path> PATH SPATH
|
%token <path> PATH HPATH SPATH
|
||||||
%token <uri> URI
|
%token <uri> URI
|
||||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||||
%token DOLLAR_CURLY /* == ${ */
|
%token DOLLAR_CURLY /* == ${ */
|
||||||
@@ -286,7 +282,6 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
|||||||
%left '*' '/'
|
%left '*' '/'
|
||||||
%right CONCAT
|
%right CONCAT
|
||||||
%nonassoc '?'
|
%nonassoc '?'
|
||||||
%nonassoc '~'
|
|
||||||
%nonassoc NEGATE
|
%nonassoc NEGATE
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@@ -376,6 +371,7 @@ expr_simple
|
|||||||
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
|
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
|
||||||
}
|
}
|
||||||
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
|
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
|
||||||
|
| HPATH { $$ = new ExprPath(getEnv("HOME", "") + string{$1 + 1}); }
|
||||||
| SPATH {
|
| SPATH {
|
||||||
string path($1 + 1, strlen($1) - 2);
|
string path($1 + 1, strlen($1) - 2);
|
||||||
$$ = new ExprApp(CUR_POS,
|
$$ = new ExprApp(CUR_POS,
|
||||||
@@ -404,25 +400,18 @@ string_parts
|
|||||||
|
|
||||||
string_parts_interpolated
|
string_parts_interpolated
|
||||||
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
|
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
|
||||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
|
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
||||||
| STR DOLLAR_CURLY expr '}'
|
| DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
|
||||||
{
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
backToString(scanner);
|
|
||||||
$$ = new vector<Expr *>;
|
$$ = new vector<Expr *>;
|
||||||
$$->push_back($1);
|
$$->push_back($1);
|
||||||
$$->push_back($3);
|
$$->push_back($3);
|
||||||
}
|
}
|
||||||
| DOLLAR_CURLY expr '}'
|
|
||||||
{
|
|
||||||
backToString(scanner);
|
|
||||||
$$ = new vector<Expr *>;
|
|
||||||
$$->push_back($2);
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
: 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 *>; }
|
| { $$ = new vector<Expr *>; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -430,20 +419,20 @@ binds
|
|||||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
foreach (AttrPath::iterator, i, *$3) {
|
for (auto & i : *$3) {
|
||||||
if ($$->attrs.find(i->symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
dupAttr(i->symbol, makeCurPos(@3, data), $$->attrs[i->symbol].pos);
|
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
||||||
Pos pos = makeCurPos(@3, data);
|
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 ';'
|
| binds INHERIT '(' expr ')' attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
/* !!! Should ensure sharing of the expression in $4. */
|
/* !!! Should ensure sharing of the expression in $4. */
|
||||||
foreach (AttrPath::iterator, i, *$6) {
|
for (auto & i : *$6) {
|
||||||
if ($$->attrs.find(i->symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
dupAttr(i->symbol, makeCurPos(@6, data), $$->attrs[i->symbol].pos);
|
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));
|
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| { $$ = new ExprAttrs; }
|
| { $$ = new ExprAttrs; }
|
||||||
@@ -527,6 +516,8 @@ formal
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <eval.hh>
|
#include <eval.hh>
|
||||||
|
#include <download.hh>
|
||||||
|
#include <store-api.hh>
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
@@ -611,10 +602,14 @@ void EvalState::addToSearchPath(const string & s, bool warn)
|
|||||||
path = string(s, pos + 1);
|
path = string(s, pos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isUri(path))
|
||||||
|
path = downloadFileCached(path, true);
|
||||||
|
|
||||||
path = absPath(path);
|
path = absPath(path);
|
||||||
if (pathExists(path)) {
|
if (pathExists(path)) {
|
||||||
debug(format("adding path ‘%1%’ to the search path") % 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)
|
} else if (warn)
|
||||||
printMsg(lvlError, format("warning: Nix search path entry ‘%1%’ does not exist, ignoring") % path);
|
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;
|
Path res;
|
||||||
if (i->first.empty())
|
if (i.first.empty())
|
||||||
res = i->second + "/" + path;
|
res = i.second + "/" + path;
|
||||||
else {
|
else {
|
||||||
if (path.compare(0, i->first.size(), i->first) != 0 ||
|
if (path.compare(0, i.first.size(), i.first) != 0 ||
|
||||||
(path.size() > i->first.size() && path[i->first.size()] != '/'))
|
(path.size() > i.first.size() && path[i.first.size()] != '/'))
|
||||||
continue;
|
continue;
|
||||||
res = i->second +
|
res = i.second +
|
||||||
(path.size() == i->first.size() ? "" : "/" + string(path, i->first.size()));
|
(path.size() == i.first.size() ? "" : "/" + string(path, i.first.size()));
|
||||||
}
|
}
|
||||||
if (pathExists(res)) return canonPath(res);
|
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 "json-to-value.hh"
|
||||||
#include "names.hh"
|
#include "names.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
|
#include "download.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.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 % e.path % pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = state.checkSourcePath(path);
|
||||||
|
|
||||||
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
|
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
|
||||||
Derivation drv = readDerivation(path);
|
Derivation drv = readDerivation(path);
|
||||||
Value & w = *state.allocValue();
|
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);
|
Value * v2 = state.allocAttr(w, state.sDrvPath);
|
||||||
mkString(*v2, path, singleton<PathSet>("=" + path));
|
mkString(*v2, path, singleton<PathSet>("=" + path));
|
||||||
|
v2 = state.allocAttr(w, state.sName);
|
||||||
|
mkString(*v2, drv.env["name"]);
|
||||||
Value * outputsVal =
|
Value * outputsVal =
|
||||||
state.allocAttr(w, state.symbols.create("outputs"));
|
state.allocAttr(w, state.symbols.create("outputs"));
|
||||||
state.mkList(*outputsVal, drv.outputs.size());
|
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));
|
v2 = state.allocAttr(w, state.symbols.create(o.first));
|
||||||
mkString(*v2, o.second.path,
|
mkString(*v2, o.second.path,
|
||||||
singleton<PathSet>("!" + o.first + "!" + path));
|
singleton<PathSet>("!" + o.first + "!" + path));
|
||||||
outputsVal->list.elems[outputs_index] = state.allocValue();
|
outputsVal->listElems()[outputs_index] = state.allocValue();
|
||||||
mkString(*(outputsVal->list.elems[outputs_index++]), o.first);
|
mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
|
||||||
}
|
}
|
||||||
w.attrs->sort();
|
w.attrs->sort();
|
||||||
Value fun;
|
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);
|
state.forceFunction(fun, pos);
|
||||||
mkApp(v, fun, w);
|
mkApp(v, fun, w);
|
||||||
state.forceAttrs(v, pos);
|
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? */
|
/* !!! Should we pass the Pos or the file name too? */
|
||||||
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
|
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)
|
static void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
@@ -146,6 +151,8 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
|
|||||||
% path % e.path % pos);
|
% path % e.path % pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = state.checkSourcePath(path);
|
||||||
|
|
||||||
string sym = state.forceStringNoCtx(*args[1], pos);
|
string sym = state.forceStringNoCtx(*args[1], pos);
|
||||||
|
|
||||||
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
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 tPath: t = "path"; break;
|
||||||
case tNull: t = "null"; break;
|
case tNull: t = "null"; break;
|
||||||
case tAttrs: t = "set"; break;
|
case tAttrs: t = "set"; break;
|
||||||
case tList: t = "list"; break;
|
case tList1: case tList2: case tListN: t = "list"; break;
|
||||||
case tLambda:
|
case tLambda:
|
||||||
case tPrimOp:
|
case tPrimOp:
|
||||||
case tPrimOpApp:
|
case tPrimOpApp:
|
||||||
t = "lambda";
|
t = "lambda";
|
||||||
break;
|
break;
|
||||||
case tExternal:
|
case tExternal:
|
||||||
t = args[0]->external->typeOf();
|
t = args[0]->external->typeOf();
|
||||||
break;
|
break;
|
||||||
default: abort();
|
default: abort();
|
||||||
@@ -277,8 +284,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
|||||||
state.forceList(*startSet->value, pos);
|
state.forceList(*startSet->value, pos);
|
||||||
|
|
||||||
ValueList workSet;
|
ValueList workSet;
|
||||||
for (unsigned int n = 0; n < startSet->value->list.length; ++n)
|
for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
|
||||||
workSet.push_back(startSet->value->list.elems[n]);
|
workSet.push_back(startSet->value->listElems()[n]);
|
||||||
|
|
||||||
/* Get the operator. */
|
/* Get the operator. */
|
||||||
Bindings::iterator op =
|
Bindings::iterator op =
|
||||||
@@ -316,17 +323,17 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
|||||||
state.forceList(call, pos);
|
state.forceList(call, pos);
|
||||||
|
|
||||||
/* Add the values returned by the operator to the work set. */
|
/* Add the values returned by the operator to the work set. */
|
||||||
for (unsigned int n = 0; n < call.list.length; ++n) {
|
for (unsigned int n = 0; n < call.listSize(); ++n) {
|
||||||
state.forceValue(*call.list.elems[n]);
|
state.forceValue(*call.listElems()[n]);
|
||||||
workSet.push_back(call.list.elems[n]);
|
workSet.push_back(call.listElems()[n]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the result list. */
|
/* Create the result list. */
|
||||||
state.mkList(v, res.size());
|
state.mkList(v, res.size());
|
||||||
unsigned int n = 0;
|
unsigned int n = 0;
|
||||||
foreach (ValueList::iterator, i, res)
|
for (auto & i : res)
|
||||||
v.list.elems[n++] = *i;
|
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)
|
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string name = state.forceStringNoCtx(*args[0], pos);
|
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;
|
StringSet outputs;
|
||||||
outputs.insert("out");
|
outputs.insert("out");
|
||||||
|
|
||||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
for (auto & i : *args[0]->attrs) {
|
||||||
if (i->name == state.sIgnoreNulls) continue;
|
if (i.name == state.sIgnoreNulls) continue;
|
||||||
string key = i->name;
|
string key = i.name;
|
||||||
startNest(nest, lvlVomit, format("processing attribute ‘%1%’") % key);
|
startNest(nest, lvlVomit, format("processing attribute ‘%1%’") % key);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (ignoreNulls) {
|
if (ignoreNulls) {
|
||||||
state.forceValue(*i->value);
|
state.forceValue(*i.value);
|
||||||
if (i->value->type == tNull) continue;
|
if (i.value->type == tNull) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The `args' attribute is special: it supplies the
|
/* The `args' attribute is special: it supplies the
|
||||||
command-line arguments to the builder. */
|
command-line arguments to the builder. */
|
||||||
if (key == "args") {
|
if (key == "args") {
|
||||||
state.forceList(*i->value, pos);
|
state.forceList(*i.value, pos);
|
||||||
for (unsigned int n = 0; n < i->value->list.length; ++n) {
|
for (unsigned int n = 0; n < i.value->listSize(); ++n) {
|
||||||
string s = state.coerceToString(posDrvName, *i->value->list.elems[n], context, true);
|
string s = state.coerceToString(posDrvName, *i.value->listElems()[n], context, true);
|
||||||
drv.args.push_back(s);
|
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
|
/* All other attributes are passed to the builder through
|
||||||
the environment. */
|
the environment. */
|
||||||
else {
|
else {
|
||||||
string s = state.coerceToString(posDrvName, *i->value, context, true);
|
string s = state.coerceToString(posDrvName, *i.value, context, true);
|
||||||
drv.env[key] = s;
|
drv.env[key] = s;
|
||||||
if (key == "builder") drv.builder = s;
|
if (key == "builder") drv.builder = s;
|
||||||
else if (i->name == state.sSystem) drv.platform = s;
|
else if (i.name == state.sSystem) drv.platform = s;
|
||||||
else if (i->name == state.sName) {
|
else if (i.name == state.sName) {
|
||||||
drvName = s;
|
drvName = s;
|
||||||
printMsg(lvlVomit, format("derivation name is ‘%1%’") % drvName);
|
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") {
|
else if (key == "outputs") {
|
||||||
Strings tmp = tokenizeString<Strings>(s);
|
Strings tmp = tokenizeString<Strings>(s);
|
||||||
outputs.clear();
|
outputs.clear();
|
||||||
foreach (Strings::iterator, j, tmp) {
|
for (auto & j : tmp) {
|
||||||
if (outputs.find(*j) != outputs.end())
|
if (outputs.find(j) != outputs.end())
|
||||||
throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % *j % posDrvName);
|
throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % j % posDrvName);
|
||||||
/* !!! Check whether *j is a valid attribute
|
/* !!! Check whether j is a valid attribute
|
||||||
name. */
|
name. */
|
||||||
/* Derivations cannot be named ‘drv’, because
|
/* Derivations cannot be named ‘drv’, because
|
||||||
then we'd have an attribute ‘drvPath’ in
|
then we'd have an attribute ‘drvPath’ in
|
||||||
the resulting set. */
|
the resulting set. */
|
||||||
if (*j == "drv")
|
if (j == "drv")
|
||||||
throw EvalError(format("invalid derivation output name ‘drv’, at %1%") % posDrvName);
|
throw EvalError(format("invalid derivation output name ‘drv’, at %1%") % posDrvName);
|
||||||
outputs.insert(*j);
|
outputs.insert(j);
|
||||||
}
|
}
|
||||||
if (outputs.empty())
|
if (outputs.empty())
|
||||||
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
|
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
|
/* Everything in the context of the strings in the derivation
|
||||||
attributes should be added as dependencies of the resulting
|
attributes should be added as dependencies of the resulting
|
||||||
derivation. */
|
derivation. */
|
||||||
foreach (PathSet::iterator, i, context) {
|
for (auto & path : context) {
|
||||||
Path path = *i;
|
|
||||||
|
|
||||||
/* Paths marked with `=' denote that the path of a derivation
|
/* Paths marked with `=' denote that the path of a derivation
|
||||||
is explicitly passed to the builder. Since that allows the
|
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) == '=') {
|
if (path.at(0) == '=') {
|
||||||
/* !!! This doesn't work if readOnlyMode is set. */
|
/* !!! This doesn't work if readOnlyMode is set. */
|
||||||
PathSet refs; computeFSClosure(*store, string(path, 1), refs);
|
PathSet refs; computeFSClosure(*store, string(path, 1), refs);
|
||||||
foreach (PathSet::iterator, j, refs) {
|
for (auto & j : refs) {
|
||||||
drv.inputSrcs.insert(*j);
|
drv.inputSrcs.insert(j);
|
||||||
if (isDerivation(*j))
|
if (isDerivation(j))
|
||||||
drv.inputDrvs[*j] = store->queryDerivationOutputNames(*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
|
are empty, and the corresponding environment variables have
|
||||||
an empty value. This ensures that changes in the set of
|
an empty value. This ensures that changes in the set of
|
||||||
output names do get reflected in the hash. */
|
output names do get reflected in the hash. */
|
||||||
foreach (StringSet::iterator, i, outputs) {
|
for (auto & i : outputs) {
|
||||||
drv.env[*i] = "";
|
drv.env[i] = "";
|
||||||
drv.outputs[*i] = DerivationOutput("", "", "");
|
drv.outputs[i] = DerivationOutput("", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use the masked derivation expression to compute the output
|
/* Use the masked derivation expression to compute the output
|
||||||
path. */
|
path. */
|
||||||
Hash h = hashDerivationModulo(*store, drv);
|
Hash h = hashDerivationModulo(*store, drv);
|
||||||
|
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
for (auto & i : drv.outputs)
|
||||||
if (i->second.path == "") {
|
if (i.second.path == "") {
|
||||||
Path outPath = makeOutputPath(i->first, h, drvName);
|
Path outPath = makeOutputPath(i.first, h, drvName);
|
||||||
drv.env[i->first] = outPath;
|
drv.env[i.first] = outPath;
|
||||||
i->second.path = 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());
|
state.mkAttrs(v, 1 + drv.outputs.size());
|
||||||
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
|
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
for (auto & i : drv.outputs) {
|
||||||
mkString(*state.allocAttr(v, state.symbols.create(i->first)),
|
mkString(*state.allocAttr(v, state.symbols.create(i.first)),
|
||||||
i->second.path, singleton<PathSet>("!" + i->first + "!" + drvPath));
|
i.second.path, singleton<PathSet>("!" + i.first + "!" + drvPath));
|
||||||
}
|
}
|
||||||
v.attrs->sort();
|
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)
|
static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
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
|
/* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink
|
||||||
directly in the store. The latter condition is necessary so
|
directly in the store. The latter condition is necessary so
|
||||||
e.g. nix-push does the right thing. */
|
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);
|
Path path = state.coerceToPath(pos, *args[0], context);
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throw EvalError(format("string ‘%1%’ cannot refer to other paths, at %2%") % path % pos);
|
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)
|
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
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%")
|
throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||||
% path % e.path % pos);
|
% 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;
|
SearchPath searchPath;
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
for (unsigned int n = 0; n < args[0]->list.length; ++n) {
|
for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
|
||||||
Value & v2(*args[0]->list.elems[n]);
|
Value & v2(*args[0]->listElems()[n]);
|
||||||
state.forceAttrs(v2, pos);
|
state.forceAttrs(v2, pos);
|
||||||
|
|
||||||
string prefix;
|
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);
|
throw EvalError(format("attribute ‘path’ missing, at %1%") % pos);
|
||||||
string path = state.coerceToPath(pos, *i->value, context);
|
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);
|
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);
|
% 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 ..) */
|
/* 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);
|
% path % e.path % pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries entries = readDirectory(path);
|
DirEntries entries = readDirectory(state.checkSourcePath(path));
|
||||||
state.mkAttrs(v, entries.size());
|
state.mkAttrs(v, entries.size());
|
||||||
|
|
||||||
for (auto & ent : entries) {
|
for (auto & ent : entries) {
|
||||||
Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name));
|
Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name));
|
||||||
if (ent.type == DT_UNKNOWN)
|
if (ent.type == DT_UNKNOWN)
|
||||||
ent.type = getFileType(path);
|
ent.type = getFileType(path + "/" + ent.name);
|
||||||
mkStringNoCopy(*ent_val,
|
mkStringNoCopy(*ent_val,
|
||||||
ent.type == DT_REG ? "regular" :
|
ent.type == DT_REG ? "regular" :
|
||||||
ent.type == DT_DIR ? "directory" :
|
ent.type == DT_DIR ? "directory" :
|
||||||
@@ -855,11 +872,14 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
|||||||
|
|
||||||
PathSet refs;
|
PathSet refs;
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, context) {
|
for (auto path : context) {
|
||||||
Path path = *i;
|
|
||||||
if (path.at(0) == '=') path = string(path, 1);
|
if (path.at(0) == '=') path = string(path, 1);
|
||||||
if (isDerivation(path))
|
if (isDerivation(path)) {
|
||||||
throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
|
/* 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);
|
refs.insert(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -927,9 +947,11 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
|||||||
|
|
||||||
FilterFromExpr filter(state, *args[0]);
|
FilterFromExpr filter(state, *args[0]);
|
||||||
|
|
||||||
|
path = state.checkSourcePath(path);
|
||||||
|
|
||||||
Path dstPath = settings.readOnlyMode
|
Path dstPath = settings.readOnlyMode
|
||||||
? computeStorePathForPath(path, true, htSHA256, filter).first
|
? 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));
|
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;
|
unsigned int n = 0;
|
||||||
for (auto & i : *args[0]->attrs)
|
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; });
|
[](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;
|
unsigned int n = 0;
|
||||||
for (auto & i : *args[0]->attrs)
|
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; });
|
[](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
|
||||||
|
|
||||||
for (unsigned int i = 0; i < n; ++i)
|
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. */
|
/* Get the attribute names to be removed. */
|
||||||
std::set<Symbol> names;
|
std::set<Symbol> names;
|
||||||
for (unsigned int i = 0; i < args[1]->list.length; ++i) {
|
for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
|
||||||
state.forceStringNoCtx(*args[1]->list.elems[i], pos);
|
state.forceStringNoCtx(*args[1]->listElems()[i], pos);
|
||||||
names.insert(state.symbols.create(args[1]->list.elems[i]->string.s));
|
names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy all attributes not in that set. Note that we don't need
|
/* 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
|
to sort v.attrs because it's a subset of an already sorted
|
||||||
vector. */
|
vector. */
|
||||||
state.mkAttrs(v, args[0]->attrs->size());
|
state.mkAttrs(v, args[0]->attrs->size());
|
||||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
for (auto & i : *args[0]->attrs) {
|
||||||
if (names.find(i->name) == names.end())
|
if (names.find(i.name) == names.end())
|
||||||
v.attrs->push_back(*i);
|
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.forceList(*args[0], pos);
|
||||||
|
|
||||||
state.mkAttrs(v, args[0]->list.length);
|
state.mkAttrs(v, args[0]->listSize());
|
||||||
|
|
||||||
std::set<Symbol> seen;
|
std::set<Symbol> seen;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < args[0]->list.length; ++i) {
|
for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
|
||||||
Value & v2(*args[0]->list.elems[i]);
|
Value & v2(*args[0]->listElems()[i]);
|
||||||
state.forceAttrs(v2, pos);
|
state.forceAttrs(v2, pos);
|
||||||
|
|
||||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
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()));
|
state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
|
||||||
|
|
||||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
for (auto & i : *args[0]->attrs) {
|
||||||
Bindings::iterator j = args[1]->attrs->find(i->name);
|
Bindings::iterator j = args[1]->attrs->find(i.name);
|
||||||
if (j != args[1]->attrs->end())
|
if (j != args[1]->attrs->end())
|
||||||
v.attrs->push_back(*j);
|
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));
|
Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
|
||||||
state.forceList(*args[1], pos);
|
state.forceList(*args[1], pos);
|
||||||
|
|
||||||
Value * res[args[1]->list.length];
|
Value * res[args[1]->listSize()];
|
||||||
unsigned int found = 0;
|
unsigned int found = 0;
|
||||||
|
|
||||||
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
|
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
||||||
Value & v2(*args[1]->list.elems[n]);
|
Value & v2(*args[1]->listElems()[n]);
|
||||||
state.forceAttrs(v2, pos);
|
state.forceAttrs(v2, pos);
|
||||||
Bindings::iterator i = v2.attrs->find(attrName);
|
Bindings::iterator i = v2.attrs->find(attrName);
|
||||||
if (i != v2.attrs->end())
|
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);
|
state.mkList(v, found);
|
||||||
for (unsigned int n = 0; n < found; ++n)
|
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());
|
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)
|
// !!! 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();
|
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)
|
static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceValue(*args[0]);
|
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)
|
static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
|
||||||
{
|
{
|
||||||
state.forceList(list, pos);
|
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);
|
throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
|
||||||
state.forceValue(*list.list.elems[n]);
|
state.forceValue(*list.listElems()[n]);
|
||||||
v = *list.list.elems[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
|
a list. Warning: this function takes O(n) time, so you probably
|
||||||
don't want to use it! */
|
don't want to use it! */
|
||||||
static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceList(*args[0], pos);
|
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);
|
throw Error(format("‘tail’ called on an empty list, at %1%") % pos);
|
||||||
state.mkList(v, args[0]->list.length - 1);
|
state.mkList(v, args[0]->listSize() - 1);
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
v.list.elems[n] = args[0]->list.elems[n + 1];
|
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.forceFunction(*args[0], pos);
|
||||||
state.forceList(*args[1], 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)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
mkApp(*(v.list.elems[n] = state.allocValue()),
|
mkApp(*(v.listElems()[n] = state.allocValue()),
|
||||||
*args[0], *args[1]->list.elems[n]);
|
*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);
|
state.forceList(*args[1], pos);
|
||||||
|
|
||||||
// FIXME: putting this on the stack is risky.
|
// FIXME: putting this on the stack is risky.
|
||||||
Value * vs[args[1]->list.length];
|
Value * vs[args[1]->listSize()];
|
||||||
unsigned int k = 0;
|
unsigned int k = 0;
|
||||||
|
|
||||||
bool same = true;
|
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;
|
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))
|
if (state.forceBool(res))
|
||||||
vs[k++] = args[1]->list.elems[n];
|
vs[k++] = args[1]->listElems()[n];
|
||||||
else
|
else
|
||||||
same = false;
|
same = false;
|
||||||
}
|
}
|
||||||
@@ -1253,7 +1275,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
|
|||||||
v = *args[1];
|
v = *args[1];
|
||||||
else {
|
else {
|
||||||
state.mkList(v, k);
|
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;
|
bool res = false;
|
||||||
state.forceList(*args[1], pos);
|
state.forceList(*args[1], pos);
|
||||||
for (unsigned int n = 0; n < args[1]->list.length; ++n)
|
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
|
||||||
if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
|
if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
|
||||||
res = true;
|
res = true;
|
||||||
break;
|
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)
|
static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceList(*args[0], pos);
|
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)
|
static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceList(*args[0], pos);
|
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);
|
string s = state.coerceToString(pos, *args[0], context);
|
||||||
|
|
||||||
PathSet context2;
|
PathSet context2;
|
||||||
foreach (PathSet::iterator, i, context) {
|
for (auto & p : context)
|
||||||
Path p = *i;
|
context2.insert(p.at(0) == '=' ? "~" + string(p, 1) : p);
|
||||||
if (p.at(0) == '=') p = "~" + string(p, 1);
|
|
||||||
context2.insert(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
mkString(v, s, context2);
|
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) {
|
for (unsigned int n = 0; n < len; ++n) {
|
||||||
auto i = subs.find(n);
|
auto i = subs.find(n);
|
||||||
if (i == subs.end())
|
if (i == subs.end())
|
||||||
mkNull(*(v.list.elems[n] = state.allocValue()));
|
mkNull(*(v.listElems()[n] = state.allocValue()));
|
||||||
else
|
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
|
* 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
|
* Primop registration
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
@@ -1493,12 +1723,15 @@ void EvalState::createBaseEnv()
|
|||||||
mkInt(v, time(0));
|
mkInt(v, time(0));
|
||||||
addConstant("__currentTime", v);
|
addConstant("__currentTime", v);
|
||||||
|
|
||||||
mkString(v, settings.thisSystem.c_str());
|
mkString(v, settings.thisSystem);
|
||||||
addConstant("__currentSystem", v);
|
addConstant("__currentSystem", v);
|
||||||
|
|
||||||
mkString(v, nixVersion.c_str());
|
mkString(v, nixVersion);
|
||||||
addConstant("__nixVersion", v);
|
addConstant("__nixVersion", v);
|
||||||
|
|
||||||
|
mkString(v, settings.nixStore);
|
||||||
|
addConstant("__storeDir", v);
|
||||||
|
|
||||||
/* Language version. This should be increased every time a new
|
/* Language version. This should be increased every time a new
|
||||||
language feature gets added. It's not necessary to increase it
|
language feature gets added. It's not necessary to increase it
|
||||||
when primops get added, because you can just use `builtins ?
|
when primops get added, because you can just use `builtins ?
|
||||||
@@ -1576,6 +1809,11 @@ void EvalState::createBaseEnv()
|
|||||||
addPrimOp("__elem", 2, prim_elem);
|
addPrimOp("__elem", 2, prim_elem);
|
||||||
addPrimOp("__concatLists", 1, prim_concatLists);
|
addPrimOp("__concatLists", 1, prim_concatLists);
|
||||||
addPrimOp("__length", 1, prim_length);
|
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
|
// Integer arithmetic
|
||||||
addPrimOp("__add", 2, prim_add);
|
addPrimOp("__add", 2, prim_add);
|
||||||
@@ -1592,6 +1830,8 @@ void EvalState::createBaseEnv()
|
|||||||
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||||
addPrimOp("__hashString", 2, prim_hashString);
|
addPrimOp("__hashString", 2, prim_hashString);
|
||||||
addPrimOp("__match", 2, prim_match);
|
addPrimOp("__match", 2, prim_match);
|
||||||
|
addPrimOp("__concatStringsSep", 2, prim_concatStringSep);
|
||||||
|
addPrimOp("__replaceStrings", 3, prim_replaceStrings);
|
||||||
|
|
||||||
// Versions
|
// Versions
|
||||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||||
@@ -1600,10 +1840,13 @@ void EvalState::createBaseEnv()
|
|||||||
// Derivations
|
// Derivations
|
||||||
addPrimOp("derivationStrict", 1, prim_derivationStrict);
|
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
|
/* Add a wrapper around the derivation primop that computes the
|
||||||
`drvPath' and `outPath' attributes lazily. */
|
`drvPath' and `outPath' attributes lazily. */
|
||||||
string path = findFile("nix/derivation.nix");
|
string path = settings.nixDataDir + "/nix/corepkgs/derivation.nix";
|
||||||
assert(!path.empty());
|
|
||||||
sDerivationNix = symbols.create(path);
|
sDerivationNix = symbols.create(path);
|
||||||
evalFile(path, v);
|
evalFile(path, v);
|
||||||
addConstant("derivation", v);
|
addConstant("derivation", v);
|
||||||
@@ -1612,7 +1855,7 @@ void EvalState::createBaseEnv()
|
|||||||
mkList(v, searchPath.size());
|
mkList(v, searchPath.size());
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : searchPath) {
|
||||||
v2 = v.list.elems[n++] = allocValue();
|
v2 = v.listElems()[n++] = allocValue();
|
||||||
mkAttrs(*v2, 2);
|
mkAttrs(*v2, 2);
|
||||||
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
|
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
|
||||||
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
|
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ namespace nix {
|
|||||||
void escapeJSON(std::ostream & str, const string & s)
|
void escapeJSON(std::ostream & str, const string & s)
|
||||||
{
|
{
|
||||||
str << "\"";
|
str << "\"";
|
||||||
foreach (string::const_iterator, i, s)
|
for (auto & i : s)
|
||||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
if (i == '\"' || i == '\\') str << "\\" << i;
|
||||||
else if (*i == '\n') str << "\\n";
|
else if (i == '\n') str << "\\n";
|
||||||
else if (*i == '\r') str << "\\r";
|
else if (i == '\r') str << "\\r";
|
||||||
else if (*i == '\t') str << "\\t";
|
else if (i == '\t') str << "\\t";
|
||||||
else if (*i >= 0 && *i < 32)
|
else if (i >= 0 && i < 32)
|
||||||
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
|
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) i << std::dec;
|
||||||
else str << *i;
|
else str << i;
|
||||||
str << "\"";
|
str << "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,11 +59,11 @@ void printValueAsJSON(EvalState & state, bool strict,
|
|||||||
if (i == v.attrs->end()) {
|
if (i == v.attrs->end()) {
|
||||||
JSONObject json(str);
|
JSONObject json(str);
|
||||||
StringSet names;
|
StringSet names;
|
||||||
foreach (Bindings::iterator, i, *v.attrs)
|
for (auto & j : *v.attrs)
|
||||||
names.insert(i->name);
|
names.insert(j.name);
|
||||||
foreach (StringSet::iterator, i, names) {
|
for (auto & j : names) {
|
||||||
Attr & a(*v.attrs->find(state.symbols.create(*i)));
|
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||||
json.attr(*i);
|
json.attr(j);
|
||||||
printValueAsJSON(state, strict, *a.value, str, context);
|
printValueAsJSON(state, strict, *a.value, str, context);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@@ -71,16 +71,16 @@ void printValueAsJSON(EvalState & state, bool strict,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case tList: {
|
case tList1: case tList2: case tListN: {
|
||||||
JSONList json(str);
|
JSONList json(str);
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||||
json.elem();
|
json.elem();
|
||||||
printValueAsJSON(state, strict, *v.list.elems[n], str, context);
|
printValueAsJSON(state, strict, *v.listElems()[n], str, context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case tExternal:
|
case tExternal:
|
||||||
v.external->printValueAsJSON(state, strict, str, context);
|
v.external->printValueAsJSON(state, strict, str, context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -34,14 +34,14 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
|||||||
{
|
{
|
||||||
StringSet names;
|
StringSet names;
|
||||||
|
|
||||||
foreach (Bindings::iterator, i, attrs)
|
for (auto & i : attrs)
|
||||||
names.insert(i->name);
|
names.insert(i.name);
|
||||||
|
|
||||||
foreach (StringSet::iterator, i, names) {
|
for (auto & i : names) {
|
||||||
Attr & a(*attrs.find(state.symbols.create(*i)));
|
Attr & a(*attrs.find(state.symbols.create(i)));
|
||||||
|
|
||||||
XMLAttrs xmlAttrs;
|
XMLAttrs xmlAttrs;
|
||||||
xmlAttrs["name"] = *i;
|
xmlAttrs["name"] = i;
|
||||||
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
|
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
|
||||||
|
|
||||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||||
@@ -119,10 +119,10 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tList: {
|
case tList1: case tList2: case tListN: {
|
||||||
XMLOpenElement _(doc, "list");
|
XMLOpenElement _(doc, "list");
|
||||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||||
printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
|
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,8 +136,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
||||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||||
XMLOpenElement _(doc, "attrspat", attrs);
|
XMLOpenElement _(doc, "attrspat", attrs);
|
||||||
foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
|
for (auto & i : v.lambda.fun->formals->formals)
|
||||||
doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
|
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
||||||
} else
|
} else
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
||||||
|
|
||||||
|
|||||||
+26
-3
@@ -12,7 +12,9 @@ typedef enum {
|
|||||||
tPath,
|
tPath,
|
||||||
tNull,
|
tNull,
|
||||||
tAttrs,
|
tAttrs,
|
||||||
tList,
|
tList1,
|
||||||
|
tList2,
|
||||||
|
tListN,
|
||||||
tThunk,
|
tThunk,
|
||||||
tApp,
|
tApp,
|
||||||
tLambda,
|
tLambda,
|
||||||
@@ -119,9 +121,10 @@ struct Value
|
|||||||
const char * path;
|
const char * path;
|
||||||
Bindings * attrs;
|
Bindings * attrs;
|
||||||
struct {
|
struct {
|
||||||
unsigned int length;
|
unsigned int size;
|
||||||
Value * * elems;
|
Value * * elems;
|
||||||
} list;
|
} bigList;
|
||||||
|
Value * smallList[2];
|
||||||
struct {
|
struct {
|
||||||
Env * env;
|
Env * env;
|
||||||
Expr * expr;
|
Expr * expr;
|
||||||
@@ -139,6 +142,26 @@ struct Value
|
|||||||
} primOpApp;
|
} primOpApp;
|
||||||
ExternalValueBase * external;
|
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;
|
if (lt == "pretty") logType = ltPretty;
|
||||||
else if (lt == "escapes") logType = ltEscapes;
|
else if (lt == "escapes") logType = ltEscapes;
|
||||||
else if (lt == "flat") logType = ltFlat;
|
else if (lt == "flat") logType = ltFlat;
|
||||||
|
else if (lt == "systemd") logType = ltSystemd;
|
||||||
else throw UsageError("unknown log type");
|
else throw UsageError("unknown log type");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +117,9 @@ void initNix()
|
|||||||
|
|
||||||
std::ios::sync_with_stdio(false);
|
std::ios::sync_with_stdio(false);
|
||||||
|
|
||||||
|
if (getEnv("IN_SYSTEMD") == "1")
|
||||||
|
logType = ltSystemd;
|
||||||
|
|
||||||
settings.processEnvironment();
|
settings.processEnvironment();
|
||||||
settings.loadConfFile();
|
settings.loadConfFile();
|
||||||
|
|
||||||
@@ -239,6 +243,20 @@ void parseCmdLine(int argc, char * * argv,
|
|||||||
void printVersion(const string & programName)
|
void printVersion(const string & programName)
|
||||||
{
|
{
|
||||||
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
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();
|
throw Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,8 +311,9 @@ int handleExceptions(const string & programName, std::function<void()> fun)
|
|||||||
RunPager::RunPager()
|
RunPager::RunPager()
|
||||||
{
|
{
|
||||||
if (!isatty(STDOUT_FILENO)) return;
|
if (!isatty(STDOUT_FILENO)) return;
|
||||||
string pager = getEnv("PAGER", "default");
|
char * pager = getenv("NIX_PAGER");
|
||||||
if (pager == "" || pager == "cat") return;
|
if (!pager) pager = getenv("PAGER");
|
||||||
|
if (pager && ((string) pager == "" || (string) pager == "cat")) return;
|
||||||
|
|
||||||
/* Ignore SIGINT. The pager will handle it (and we'll get
|
/* Ignore SIGINT. The pager will handle it (and we'll get
|
||||||
SIGPIPE). */
|
SIGPIPE). */
|
||||||
@@ -314,8 +333,8 @@ RunPager::RunPager()
|
|||||||
throw SysError("dupping stdin");
|
throw SysError("dupping stdin");
|
||||||
if (!getenv("LESS"))
|
if (!getenv("LESS"))
|
||||||
setenv("LESS", "FRSXMK", 1);
|
setenv("LESS", "FRSXMK", 1);
|
||||||
if (pager != "default")
|
if (pager)
|
||||||
execl("/bin/sh", "sh", "-c", pager.c_str(), NULL);
|
execl("/bin/sh", "sh", "-c", pager, NULL);
|
||||||
execlp("pager", "pager", NULL);
|
execlp("pager", "pager", NULL);
|
||||||
execlp("less", "less", NULL);
|
execlp("less", "less", NULL);
|
||||||
execlp("more", "more", 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;
|
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 "globals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "misc.hh"
|
#include "misc.hh"
|
||||||
|
#include "worker-protocol.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
@@ -31,8 +32,8 @@ Path writeDerivation(StoreAPI & store,
|
|||||||
{
|
{
|
||||||
PathSet references;
|
PathSet references;
|
||||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
|
for (auto & i : drv.inputDrvs)
|
||||||
references.insert(i->first);
|
references.insert(i.first);
|
||||||
/* Note that the outputs of a derivation are *not* references
|
/* Note that the outputs of a derivation are *not* references
|
||||||
(that can be missing (of course) and should not necessarily be
|
(that can be missing (of course) and should not necessarily be
|
||||||
held during a garbage collection). */
|
held during a garbage collection). */
|
||||||
@@ -155,21 +156,21 @@ string unparseDerivation(const Derivation & drv)
|
|||||||
s += "Derive([";
|
s += "Derive([";
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
|
for (auto & i : drv.outputs) {
|
||||||
if (first) first = false; else s += ',';
|
if (first) first = false; else s += ',';
|
||||||
s += '('; printString(s, i->first);
|
s += '('; printString(s, i.first);
|
||||||
s += ','; printString(s, i->second.path);
|
s += ','; printString(s, i.second.path);
|
||||||
s += ','; printString(s, i->second.hashAlgo);
|
s += ','; printString(s, i.second.hashAlgo);
|
||||||
s += ','; printString(s, i->second.hash);
|
s += ','; printString(s, i.second.hash);
|
||||||
s += ')';
|
s += ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
s += "],[";
|
s += "],[";
|
||||||
first = true;
|
first = true;
|
||||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
|
for (auto & i : drv.inputDrvs) {
|
||||||
if (first) first = false; else s += ',';
|
if (first) first = false; else s += ',';
|
||||||
s += '('; printString(s, i->first);
|
s += '('; printString(s, i.first);
|
||||||
s += ','; printStrings(s, i->second.begin(), i->second.end());
|
s += ','; printStrings(s, i.second.begin(), i.second.end());
|
||||||
s += ')';
|
s += ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,10 +183,10 @@ string unparseDerivation(const Derivation & drv)
|
|||||||
|
|
||||||
s += ",[";
|
s += ",[";
|
||||||
first = true;
|
first = true;
|
||||||
foreach (StringPairs::const_iterator, i, drv.env) {
|
for (auto & i : drv.env) {
|
||||||
if (first) first = false; else s += ',';
|
if (first) first = false; else s += ',';
|
||||||
s += '('; printString(s, i->first);
|
s += '('; printString(s, i.first);
|
||||||
s += ','; printString(s, i->second);
|
s += ','; printString(s, i.second);
|
||||||
s += ')';
|
s += ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,15 +247,15 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
|
|||||||
/* For other derivations, replace the inputs paths with recursive
|
/* For other derivations, replace the inputs paths with recursive
|
||||||
calls to this function.*/
|
calls to this function.*/
|
||||||
DerivationInputs inputs2;
|
DerivationInputs inputs2;
|
||||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
|
for (auto & i : drv.inputDrvs) {
|
||||||
Hash h = drvHashes[i->first];
|
Hash h = drvHashes[i.first];
|
||||||
if (h.type == htUnknown) {
|
if (h.type == htUnknown) {
|
||||||
assert(store.isValidPath(i->first));
|
assert(store.isValidPath(i.first));
|
||||||
Derivation drv2 = readDerivation(i->first);
|
Derivation drv2 = readDerivation(i.first);
|
||||||
h = hashDerivationModulo(store, drv2);
|
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;
|
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;
|
typedef std::map<string, string> StringPairs;
|
||||||
|
|
||||||
struct Derivation
|
struct BasicDerivation
|
||||||
{
|
{
|
||||||
DerivationOutputs outputs; /* keyed on symbolic IDs */
|
DerivationOutputs outputs; /* keyed on symbolic IDs */
|
||||||
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
|
|
||||||
PathSet inputSrcs; /* inputs that are sources */
|
PathSet inputSrcs; /* inputs that are sources */
|
||||||
string platform;
|
string platform;
|
||||||
Path builder;
|
Path builder;
|
||||||
Strings args;
|
Strings args;
|
||||||
StringPairs env;
|
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);
|
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);
|
"(are you running nix-build inside the store?)") % gcRoot);
|
||||||
|
|
||||||
if (indirect) {
|
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. */
|
point to the Nix store. */
|
||||||
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
|
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
|
||||||
throw Error(format("cannot create symlink ‘%1%’; already exists") % 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");
|
StringSet paths = tokenizeString<StringSet>(result, "\n");
|
||||||
|
|
||||||
foreach (StringSet::iterator, i, paths) {
|
for (auto & i : paths)
|
||||||
if (isInStore(*i)) {
|
if (isInStore(i)) {
|
||||||
Path path = toStorePath(*i);
|
Path path = toStorePath(i);
|
||||||
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
|
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
|
||||||
debug(format("got additional root ‘%1%’") % path);
|
debug(format("got additional root ‘%1%’") % path);
|
||||||
roots.insert(path);
|
roots.insert(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -376,6 +375,7 @@ struct LocalStore::GCState
|
|||||||
bool gcKeepOutputs;
|
bool gcKeepOutputs;
|
||||||
bool gcKeepDerivations;
|
bool gcKeepDerivations;
|
||||||
unsigned long long bytesInvalidated;
|
unsigned long long bytesInvalidated;
|
||||||
|
bool moveToTrash = true;
|
||||||
Path trashDir;
|
Path trashDir;
|
||||||
bool shouldDelete;
|
bool shouldDelete;
|
||||||
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
|
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
|
||||||
@@ -407,8 +407,8 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
|||||||
if (isValidPath(path)) {
|
if (isValidPath(path)) {
|
||||||
PathSet referrers;
|
PathSet referrers;
|
||||||
queryReferrers(path, referrers);
|
queryReferrers(path, referrers);
|
||||||
foreach (PathSet::iterator, i, referrers)
|
for (auto & i : referrers)
|
||||||
if (*i != path) deletePathRecursive(state, *i);
|
if (i != path) deletePathRecursive(state, i);
|
||||||
size = queryPathInfo(path).narSize;
|
size = queryPathInfo(path).narSize;
|
||||||
invalidatePathChecked(path);
|
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
|
not holding the global GC lock) we can delete the path without
|
||||||
being afraid that the path has become alive again. Otherwise
|
being afraid that the path has become alive again. Otherwise
|
||||||
delete it right away. */
|
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:
|
// Estimate the amount freed using the narSize field. FIXME:
|
||||||
// if the path was not valid, need to determine the actual
|
// if the path was not valid, need to determine the actual
|
||||||
// size.
|
// size.
|
||||||
state.bytesInvalidated += size;
|
try {
|
||||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||||
throw SysError(format("making ‘%1%’ writable") % path);
|
throw SysError(format("making ‘%1%’ writable") % path);
|
||||||
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
||||||
if (rename(path.c_str(), tmp.c_str()))
|
if (rename(path.c_str(), tmp.c_str()))
|
||||||
throw SysError(format("unable to rename ‘%1%’ to ‘%2%’") % path % tmp);
|
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
|
} else
|
||||||
deleteGarbage(state, path);
|
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. */
|
don't delete the derivation if any of the outputs are alive. */
|
||||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||||
PathSet outputs = queryDerivationOutputs(path);
|
PathSet outputs = queryDerivationOutputs(path);
|
||||||
foreach (PathSet::iterator, i, outputs)
|
for (auto & i : outputs)
|
||||||
if (isValidPath(*i) && queryDeriver(*i) == path)
|
if (isValidPath(i) && queryDeriver(i) == path)
|
||||||
incoming.insert(*i);
|
incoming.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If gc-keep-outputs is set, then don't delete this path if there
|
/* If gc-keep-outputs is set, then don't delete this path if there
|
||||||
are derivers of this path that are not garbage. */
|
are derivers of this path that are not garbage. */
|
||||||
if (state.gcKeepOutputs) {
|
if (state.gcKeepOutputs) {
|
||||||
PathSet derivers = queryValidDerivers(path);
|
PathSet derivers = queryValidDerivers(path);
|
||||||
foreach (PathSet::iterator, i, derivers)
|
for (auto & i : derivers)
|
||||||
incoming.insert(*i);
|
incoming.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, incoming)
|
for (auto & i : incoming)
|
||||||
if (*i != path)
|
if (i != path)
|
||||||
if (canReachRoot(state, visited, *i)) {
|
if (canReachRoot(state, visited, i)) {
|
||||||
state.alive.insert(path);
|
state.alive.insert(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -614,7 +621,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||||||
printMsg(lvlError, format("finding garbage collector roots..."));
|
printMsg(lvlError, format("finding garbage collector roots..."));
|
||||||
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
|
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
|
/* Add additional roots returned by the program specified by the
|
||||||
NIX_ROOT_FINDER environment variable. This is typically used
|
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 (state.shouldDelete) {
|
||||||
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
|
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
|
/* 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) {
|
if (options.action == GCOptions::gcDeleteSpecific) {
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, options.pathsToDelete) {
|
for (auto & i : options.pathsToDelete) {
|
||||||
assertStorePath(*i);
|
assertStorePath(i);
|
||||||
tryToDelete(state, *i);
|
tryToDelete(state, i);
|
||||||
if (state.dead.find(*i) == state.dead.end())
|
if (state.dead.find(i) == state.dead.end())
|
||||||
throw Error(format("cannot delete path ‘%1%’ since it is still alive") % *i);
|
throw Error(format("cannot delete path ‘%1%’ since it is still alive") % i);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (options.maxFreed > 0) {
|
} else if (options.maxFreed > 0) {
|
||||||
@@ -692,8 +706,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||||||
vector<Path> entries_(entries.begin(), entries.end());
|
vector<Path> entries_(entries.begin(), entries.end());
|
||||||
random_shuffle(entries_.begin(), entries_.end());
|
random_shuffle(entries_.begin(), entries_.end());
|
||||||
|
|
||||||
foreach (vector<Path>::iterator, i, entries_)
|
for (auto & i : entries_)
|
||||||
tryToDelete(state, *i);
|
tryToDelete(state, i);
|
||||||
|
|
||||||
} catch (GCLimitReached & e) {
|
} catch (GCLimitReached & e) {
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-11
@@ -41,13 +41,12 @@ Settings::Settings()
|
|||||||
buildTimeout = 0;
|
buildTimeout = 0;
|
||||||
useBuildHook = true;
|
useBuildHook = true;
|
||||||
printBuildTrace = false;
|
printBuildTrace = false;
|
||||||
reservedSize = 1024 * 1024;
|
reservedSize = 8 * 1024 * 1024;
|
||||||
fsyncMetadata = true;
|
fsyncMetadata = true;
|
||||||
useSQLiteWAL = true;
|
useSQLiteWAL = true;
|
||||||
syncBeforeRegistering = false;
|
syncBeforeRegistering = false;
|
||||||
useSubstitutes = true;
|
useSubstitutes = true;
|
||||||
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
||||||
useChroot = false;
|
|
||||||
useSshSubstituter = true;
|
useSshSubstituter = true;
|
||||||
impersonateLinux26 = false;
|
impersonateLinux26 = false;
|
||||||
keepLog = true;
|
keepLog = true;
|
||||||
@@ -68,6 +67,7 @@ Settings::Settings()
|
|||||||
|
|
||||||
void Settings::processEnvironment()
|
void Settings::processEnvironment()
|
||||||
{
|
{
|
||||||
|
nixPrefix = NIX_PREFIX;
|
||||||
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
||||||
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
|
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
|
||||||
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_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()
|
void Settings::update()
|
||||||
{
|
{
|
||||||
_get(tryFallback, "build-fallback");
|
_get(tryFallback, "build-fallback");
|
||||||
@@ -158,7 +166,6 @@ void Settings::update()
|
|||||||
_get(syncBeforeRegistering, "sync-before-registering");
|
_get(syncBeforeRegistering, "sync-before-registering");
|
||||||
_get(useSubstitutes, "build-use-substitutes");
|
_get(useSubstitutes, "build-use-substitutes");
|
||||||
_get(buildUsersGroup, "build-users-group");
|
_get(buildUsersGroup, "build-users-group");
|
||||||
_get(useChroot, "build-use-chroot");
|
|
||||||
_get(impersonateLinux26, "build-impersonate-linux-26");
|
_get(impersonateLinux26, "build-impersonate-linux-26");
|
||||||
_get(keepLog, "build-keep-log");
|
_get(keepLog, "build-keep-log");
|
||||||
_get(compressLog, "build-compress-log");
|
_get(compressLog, "build-compress-log");
|
||||||
@@ -175,6 +182,7 @@ void Settings::update()
|
|||||||
_get(logServers, "log-servers");
|
_get(logServers, "log-servers");
|
||||||
_get(enableImportNative, "allow-unsafe-native-code-during-evaluation");
|
_get(enableImportNative, "allow-unsafe-native-code-during-evaluation");
|
||||||
_get(useCaseHack, "use-case-hack");
|
_get(useCaseHack, "use-case-hack");
|
||||||
|
_get(preBuildHook, "pre-build-hook");
|
||||||
|
|
||||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||||
if (subs == "default") {
|
if (subs == "default") {
|
||||||
@@ -240,12 +248,12 @@ template<class N> void Settings::_get(N & res, const string & name)
|
|||||||
string Settings::pack()
|
string Settings::pack()
|
||||||
{
|
{
|
||||||
string s;
|
string s;
|
||||||
foreach (SettingsMap::iterator, i, settings) {
|
for (auto & i : settings) {
|
||||||
if (i->first.find('\n') != string::npos ||
|
if (i.first.find('\n') != string::npos ||
|
||||||
i->first.find('=') != string::npos ||
|
i.first.find('=') != string::npos ||
|
||||||
i->second.find('\n') != string::npos)
|
i.second.find('\n') != string::npos)
|
||||||
throw Error("illegal option name/value");
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -253,11 +261,11 @@ string Settings::pack()
|
|||||||
|
|
||||||
void Settings::unpack(const string & pack) {
|
void Settings::unpack(const string & pack) {
|
||||||
Strings lines = tokenizeString<Strings>(pack, "\n");
|
Strings lines = tokenizeString<Strings>(pack, "\n");
|
||||||
foreach (Strings::iterator, i, lines) {
|
for (auto & i : lines) {
|
||||||
string::size_type eq = i->find('=');
|
string::size_type eq = i.find('=');
|
||||||
if (eq == string::npos)
|
if (eq == string::npos)
|
||||||
throw Error("illegal option name/value");
|
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);
|
bool get(const string & name, bool def);
|
||||||
|
|
||||||
|
int get(const string & name, int def);
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
string pack();
|
string pack();
|
||||||
@@ -40,6 +42,8 @@ struct Settings {
|
|||||||
|
|
||||||
Path nixDataDir; /* !!! fix */
|
Path nixDataDir; /* !!! fix */
|
||||||
|
|
||||||
|
Path nixPrefix;
|
||||||
|
|
||||||
/* The directory where we log various operations. */
|
/* The directory where we log various operations. */
|
||||||
Path nixLogDir;
|
Path nixLogDir;
|
||||||
|
|
||||||
@@ -145,9 +149,6 @@ struct Settings {
|
|||||||
/* The Unix group that contains the build users. */
|
/* The Unix group that contains the build users. */
|
||||||
string buildUsersGroup;
|
string buildUsersGroup;
|
||||||
|
|
||||||
/* Whether to build in chroot. */
|
|
||||||
bool useChroot;
|
|
||||||
|
|
||||||
/* Set of ssh connection strings for the ssh substituter */
|
/* Set of ssh connection strings for the ssh substituter */
|
||||||
Strings sshSubstituterHosts;
|
Strings sshSubstituterHosts;
|
||||||
|
|
||||||
@@ -205,6 +206,10 @@ struct Settings {
|
|||||||
/* Whether the importNative primop should be enabled */
|
/* Whether the importNative primop should be enabled */
|
||||||
bool enableImportNative;
|
bool enableImportNative;
|
||||||
|
|
||||||
|
/* The hook to run just before a build to set derivation-specific
|
||||||
|
build settings */
|
||||||
|
Path preBuildHook;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SettingsMap settings, overrides;
|
SettingsMap settings, overrides;
|
||||||
|
|
||||||
|
|||||||
+106
-98
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
@@ -256,20 +257,23 @@ LocalStore::LocalStore(bool reserveSpace)
|
|||||||
if (chmod(perUserDir.c_str(), 01777) == -1)
|
if (chmod(perUserDir.c_str(), 01777) == -1)
|
||||||
throw SysError(format("could not set permissions on ‘%1%’ to 1777") % perUserDir);
|
throw SysError(format("could not set permissions on ‘%1%’ to 1777") % perUserDir);
|
||||||
|
|
||||||
|
mode_t perm = 01775;
|
||||||
|
|
||||||
struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
|
struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
|
||||||
if (!gr)
|
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);
|
% 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 (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
||||||
if (stat(settings.nixStore.c_str(), &st))
|
if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
|
||||||
throw SysError(format("getting attributes of path ‘%1%’") % settings.nixStore);
|
throw SysError(format("changing ownership of path ‘%1%’") % settings.nixStore);
|
||||||
|
if (chmod(settings.nixStore.c_str(), perm) == -1)
|
||||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != 01775) {
|
throw SysError(format("changing permissions on path ‘%1%’") % settings.nixStore);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +289,17 @@ LocalStore::LocalStore(bool reserveSpace)
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(reservedPath.c_str(), &st) == -1 ||
|
if (stat(reservedPath.c_str(), &st) == -1 ||
|
||||||
st.st_size != settings.reservedSize)
|
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
|
else
|
||||||
deletePath(reservedPath);
|
deletePath(reservedPath);
|
||||||
@@ -353,13 +367,13 @@ LocalStore::LocalStore(bool reserveSpace)
|
|||||||
LocalStore::~LocalStore()
|
LocalStore::~LocalStore()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
|
for (auto & i : runningSubstituters) {
|
||||||
if (i->second.disabled) continue;
|
if (i.second.disabled) continue;
|
||||||
i->second.to.close();
|
i.second.to.close();
|
||||||
i->second.from.close();
|
i.second.from.close();
|
||||||
i->second.error.close();
|
i.second.error.close();
|
||||||
if (i->second.pid != -1)
|
if (i.second.pid != -1)
|
||||||
i->second.pid.wait(true);
|
i.second.pid.wait(true);
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
@@ -657,19 +671,19 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
|||||||
|
|
||||||
else {
|
else {
|
||||||
Derivation drvCopy(drv);
|
Derivation drvCopy(drv);
|
||||||
foreach (DerivationOutputs::iterator, i, drvCopy.outputs) {
|
for (auto & i : drvCopy.outputs) {
|
||||||
i->second.path = "";
|
i.second.path = "";
|
||||||
drvCopy.env[i->first] = "";
|
drvCopy.env[i.first] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash h = hashDerivationModulo(*this, drvCopy);
|
Hash h = hashDerivationModulo(*this, drvCopy);
|
||||||
|
|
||||||
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
|
for (auto & i : drv.outputs) {
|
||||||
Path outPath = makeOutputPath(i->first, h, drvName);
|
Path outPath = makeOutputPath(i.first, h, drvName);
|
||||||
StringPairs::const_iterator j = drv.env.find(i->first);
|
StringPairs::const_iterator j = drv.env.find(i.first);
|
||||||
if (i->second.path != outPath || j == drv.env.end() || j->second != outPath)
|
if (i.second.path != outPath || j == drv.env.end() || j->second != outPath)
|
||||||
throw Error(format("derivation ‘%1%’ has incorrect output ‘%2%’, should be ‘%3%’")
|
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. */
|
registration above is undone. */
|
||||||
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
||||||
|
|
||||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
for (auto & i : drv.outputs) {
|
||||||
SQLiteStmtUse use(stmtAddDerivationOutput);
|
SQLiteStmtUse use(stmtAddDerivationOutput);
|
||||||
stmtAddDerivationOutput.bind(id);
|
stmtAddDerivationOutput.bind(id);
|
||||||
stmtAddDerivationOutput.bind(i->first);
|
stmtAddDerivationOutput.bind(i.first);
|
||||||
stmtAddDerivationOutput.bind(i->second.path);
|
stmtAddDerivationOutput.bind(i.second.path);
|
||||||
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
|
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
|
||||||
throwSQLiteError(db, format("adding derivation output for ‘%1%’ in database") % info.path);
|
throwSQLiteError(db, format("adding derivation output for ‘%1%’ in database") % info.path);
|
||||||
}
|
}
|
||||||
@@ -782,11 +796,11 @@ void LocalStore::clearFailedPaths(const PathSet & paths)
|
|||||||
retry_sqlite {
|
retry_sqlite {
|
||||||
SQLiteTxn txn(db);
|
SQLiteTxn txn(db);
|
||||||
|
|
||||||
foreach (PathSet::const_iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
SQLiteStmtUse use(stmtClearFailedPath);
|
SQLiteStmtUse use(stmtClearFailedPath);
|
||||||
stmtClearFailedPath.bind(*i);
|
stmtClearFailedPath.bind(i);
|
||||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
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();
|
txn.commit();
|
||||||
@@ -909,8 +923,8 @@ PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
|||||||
{
|
{
|
||||||
retry_sqlite {
|
retry_sqlite {
|
||||||
PathSet res;
|
PathSet res;
|
||||||
foreach (PathSet::const_iterator, i, paths)
|
for (auto & i : paths)
|
||||||
if (isValidPath_(*i)) res.insert(*i);
|
if (isValidPath_(i)) res.insert(i);
|
||||||
return res;
|
return res;
|
||||||
} end_retry_sqlite;
|
} end_retry_sqlite;
|
||||||
}
|
}
|
||||||
@@ -1198,14 +1212,14 @@ template<class T> T LocalStore::getIntLineFromSubstituter(RunningSubstituter & r
|
|||||||
PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
PathSet res;
|
PathSet res;
|
||||||
foreach (Paths::iterator, i, settings.substituters) {
|
for (auto & i : settings.substituters) {
|
||||||
if (res.size() == paths.size()) break;
|
if (res.size() == paths.size()) break;
|
||||||
RunningSubstituter & run(runningSubstituters[*i]);
|
RunningSubstituter & run(runningSubstituters[i]);
|
||||||
startSubstituter(*i, run);
|
startSubstituter(i, run);
|
||||||
if (run.disabled) continue;
|
if (run.disabled) continue;
|
||||||
string s = "have ";
|
string s = "have ";
|
||||||
foreach (PathSet::const_iterator, j, paths)
|
for (auto & j : paths)
|
||||||
if (res.find(*j) == res.end()) { s += *j; s += " "; }
|
if (res.find(j) == res.end()) { s += j; s += " "; }
|
||||||
writeLine(run.to, s);
|
writeLine(run.to, s);
|
||||||
while (true) {
|
while (true) {
|
||||||
/* FIXME: we only read stderr when an error occurs, so
|
/* FIXME: we only read stderr when an error occurs, so
|
||||||
@@ -1229,8 +1243,8 @@ void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
|||||||
if (run.disabled) return;
|
if (run.disabled) return;
|
||||||
|
|
||||||
string s = "info ";
|
string s = "info ";
|
||||||
foreach (PathSet::const_iterator, i, paths)
|
for (auto & i : paths)
|
||||||
if (infos.find(*i) == infos.end()) { s += *i; s += " "; }
|
if (infos.find(i) == infos.end()) { s += i; s += " "; }
|
||||||
writeLine(run.to, s);
|
writeLine(run.to, s);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -1258,9 +1272,9 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
|
|||||||
SubstitutablePathInfos & infos)
|
SubstitutablePathInfos & infos)
|
||||||
{
|
{
|
||||||
PathSet todo = paths;
|
PathSet todo = paths;
|
||||||
foreach (Paths::iterator, i, settings.substituters) {
|
for (auto & i : settings.substituters) {
|
||||||
if (todo.empty()) break;
|
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);
|
SQLiteTxn txn(db);
|
||||||
PathSet paths;
|
PathSet paths;
|
||||||
|
|
||||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
for (auto & i : infos) {
|
||||||
assert(i->hash.type == htSHA256);
|
assert(i.hash.type == htSHA256);
|
||||||
if (isValidPath_(i->path))
|
if (isValidPath_(i.path))
|
||||||
updatePathInfo(*i);
|
updatePathInfo(i);
|
||||||
else
|
else
|
||||||
addValidPath(*i, false);
|
addValidPath(i, false);
|
||||||
paths.insert(i->path);
|
paths.insert(i.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
for (auto & i : infos) {
|
||||||
unsigned long long referrer = queryValidPathId(i->path);
|
unsigned long long referrer = queryValidPathId(i.path);
|
||||||
foreach (PathSet::iterator, j, i->references)
|
for (auto & j : i.references)
|
||||||
addReference(referrer, queryValidPathId(*j));
|
addReference(referrer, queryValidPathId(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that the derivation outputs are correct. We can't do
|
/* Check that the derivation outputs are correct. We can't do
|
||||||
this in addValidPath() above, because the references might
|
this in addValidPath() above, because the references might
|
||||||
not be valid yet. */
|
not be valid yet. */
|
||||||
foreach (ValidPathInfos::const_iterator, i, infos)
|
for (auto & i : infos)
|
||||||
if (isDerivation(i->path)) {
|
if (isDerivation(i.path)) {
|
||||||
// FIXME: inefficient; we already loaded the
|
// FIXME: inefficient; we already loaded the
|
||||||
// derivation in addValidPath().
|
// derivation in addValidPath().
|
||||||
Derivation drv = readDerivation(i->path);
|
Derivation drv = readDerivation(i.path);
|
||||||
checkDerivationOutputs(i->path, drv);
|
checkDerivationOutputs(i.path, drv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do a topological sort of the paths. This will throw an
|
/* 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)
|
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
|
||||||
{
|
{
|
||||||
Path srcPath(absPath(_srcPath));
|
Path srcPath(absPath(_srcPath));
|
||||||
@@ -1417,7 +1431,7 @@ Path LocalStore::addToStore(const Path & _srcPath,
|
|||||||
else
|
else
|
||||||
sink.s = readFile(srcPath);
|
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);
|
assertStorePath(path);
|
||||||
|
|
||||||
printMsg(lvlInfo, format("exporting path ‘%1%’") % path);
|
printMsg(lvlTalkative, format("exporting path ‘%1%’") % path);
|
||||||
|
|
||||||
if (!isValidPath(path))
|
if (!isValidPath(path))
|
||||||
throw Error(format("path ‘%1%’ is not valid") % 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
|
throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path
|
||||||
% printHash(storedHash) % printHash(hash));
|
% printHash(storedHash) % printHash(hash));
|
||||||
|
|
||||||
writeInt(EXPORT_MAGIC, hashAndWriteSink);
|
|
||||||
|
|
||||||
writeString(path, hashAndWriteSink);
|
|
||||||
|
|
||||||
PathSet references;
|
PathSet references;
|
||||||
queryReferences(path, references);
|
queryReferences(path, references);
|
||||||
writeStrings(references, hashAndWriteSink);
|
|
||||||
|
|
||||||
Path deriver = queryDeriver(path);
|
hashAndWriteSink << EXPORT_MAGIC << path << references << queryDeriver(path);
|
||||||
writeString(deriver, hashAndWriteSink);
|
|
||||||
|
|
||||||
if (sign) {
|
if (sign) {
|
||||||
Hash hash = hashAndWriteSink.currentHash();
|
Hash hash = hashAndWriteSink.currentHash();
|
||||||
|
|
||||||
writeInt(1, hashAndWriteSink);
|
|
||||||
|
|
||||||
Path tmpDir = createTempDir();
|
Path tmpDir = createTempDir();
|
||||||
AutoDelete delTmp(tmpDir);
|
AutoDelete delTmp(tmpDir);
|
||||||
Path hashFile = tmpDir + "/hash";
|
Path hashFile = tmpDir + "/hash";
|
||||||
@@ -1547,10 +1553,10 @@ void LocalStore::exportPath(const Path & path, bool sign,
|
|||||||
args.push_back(hashFile);
|
args.push_back(hashFile);
|
||||||
string signature = runProgram(OPENSSL_PATH, true, args);
|
string signature = runProgram(OPENSSL_PATH, true, args);
|
||||||
|
|
||||||
writeString(signature, hashAndWriteSink);
|
hashAndWriteSink << 1 << signature;
|
||||||
|
|
||||||
} else
|
} else
|
||||||
writeInt(0, hashAndWriteSink);
|
hashAndWriteSink << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1607,6 +1613,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
|
|||||||
|
|
||||||
Path dstPath = readStorePath(hashAndReadSource);
|
Path dstPath = readStorePath(hashAndReadSource);
|
||||||
|
|
||||||
|
printMsg(lvlTalkative, format("importing path ‘%1%’") % dstPath);
|
||||||
|
|
||||||
PathSet references = readStorePaths<PathSet>(hashAndReadSource);
|
PathSet references = readStorePaths<PathSet>(hashAndReadSource);
|
||||||
|
|
||||||
Path deriver = readString(hashAndReadSource);
|
Path deriver = readString(hashAndReadSource);
|
||||||
@@ -1747,8 +1755,8 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
|||||||
|
|
||||||
PathSet validPaths2 = queryAllValidPaths(), validPaths, done;
|
PathSet validPaths2 = queryAllValidPaths(), validPaths, done;
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, validPaths2)
|
for (auto & i : validPaths2)
|
||||||
verifyPath(*i, store, done, validPaths, repair, errors);
|
verifyPath(i, store, done, validPaths, repair, errors);
|
||||||
|
|
||||||
/* Release the GC lock so that checking content hashes (which can
|
/* Release the GC lock so that checking content hashes (which can
|
||||||
take ages) doesn't block the GC or builds. */
|
take ages) doesn't block the GC or builds. */
|
||||||
@@ -1760,33 +1768,33 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
|||||||
|
|
||||||
Hash nullHash(htSHA256);
|
Hash nullHash(htSHA256);
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, validPaths) {
|
for (auto & i : validPaths) {
|
||||||
try {
|
try {
|
||||||
ValidPathInfo info = queryPathInfo(*i);
|
ValidPathInfo info = queryPathInfo(i);
|
||||||
|
|
||||||
/* Check the content hash (optionally - slow). */
|
/* Check the content hash (optionally - slow). */
|
||||||
printMsg(lvlTalkative, format("checking contents of ‘%1%’") % *i);
|
printMsg(lvlTalkative, format("checking contents of ‘%1%’") % i);
|
||||||
HashResult current = hashPath(info.hash.type, *i);
|
HashResult current = hashPath(info.hash.type, i);
|
||||||
|
|
||||||
if (info.hash != nullHash && info.hash != current.first) {
|
if (info.hash != nullHash && info.hash != current.first) {
|
||||||
printMsg(lvlError, format("path ‘%1%’ was modified! "
|
printMsg(lvlError, format("path ‘%1%’ was modified! "
|
||||||
"expected hash ‘%2%’, got ‘%3%’")
|
"expected hash ‘%2%’, got ‘%3%’")
|
||||||
% *i % printHash(info.hash) % printHash(current.first));
|
% i % printHash(info.hash) % printHash(current.first));
|
||||||
if (repair) repairPath(*i); else errors = true;
|
if (repair) repairPath(i); else errors = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
bool update = false;
|
bool update = false;
|
||||||
|
|
||||||
/* Fill in missing hashes. */
|
/* Fill in missing hashes. */
|
||||||
if (info.hash == nullHash) {
|
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;
|
info.hash = current.first;
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in missing narSize fields (from old stores). */
|
/* Fill in missing narSize fields (from old stores). */
|
||||||
if (info.narSize == 0) {
|
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;
|
info.narSize = current.second;
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
@@ -1798,7 +1806,7 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
|||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
/* It's possible that the path got GC'ed, so ignore
|
/* It's possible that the path got GC'ed, so ignore
|
||||||
errors on invalid paths. */
|
errors on invalid paths. */
|
||||||
if (isValidPath(*i))
|
if (isValidPath(i))
|
||||||
printMsg(lvlError, format("error: %1%") % e.msg());
|
printMsg(lvlError, format("error: %1%") % e.msg());
|
||||||
else
|
else
|
||||||
printMsg(lvlError, format("warning: %1%") % e.msg());
|
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. */
|
first, then we can invalidate this path as well. */
|
||||||
bool canInvalidate = true;
|
bool canInvalidate = true;
|
||||||
PathSet referrers; queryReferrers(path, referrers);
|
PathSet referrers; queryReferrers(path, referrers);
|
||||||
foreach (PathSet::iterator, i, referrers)
|
for (auto & i : referrers)
|
||||||
if (*i != path) {
|
if (i != path) {
|
||||||
verifyPath(*i, store, done, validPaths, repair, errors);
|
verifyPath(i, store, done, validPaths, repair, errors);
|
||||||
if (validPaths.find(*i) != validPaths.end())
|
if (validPaths.find(i) != validPaths.end())
|
||||||
canInvalidate = false;
|
canInvalidate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1911,12 +1919,12 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
|
|||||||
/* Parse it. */
|
/* Parse it. */
|
||||||
Strings lines = tokenizeString<Strings>(info, "\n");
|
Strings lines = tokenizeString<Strings>(info, "\n");
|
||||||
|
|
||||||
foreach (Strings::iterator, i, lines) {
|
for (auto & i : lines) {
|
||||||
string::size_type p = i->find(':');
|
string::size_type p = i.find(':');
|
||||||
if (p == string::npos)
|
if (p == string::npos)
|
||||||
throw Error(format("corrupt line in ‘%1%’: %2%") % infoFile % *i);
|
throw Error(format("corrupt line in ‘%1%’: %2%") % infoFile % i);
|
||||||
string name(*i, 0, p);
|
string name(i, 0, p);
|
||||||
string value(*i, p + 2);
|
string value(i, p + 2);
|
||||||
if (name == "References") {
|
if (name == "References") {
|
||||||
Strings refs = tokenizeString<Strings>(value, " ");
|
Strings refs = tokenizeString<Strings>(value, " ");
|
||||||
res.references = PathSet(refs.begin(), refs.end());
|
res.references = PathSet(refs.begin(), refs.end());
|
||||||
@@ -1946,18 +1954,18 @@ void LocalStore::upgradeStore6()
|
|||||||
|
|
||||||
SQLiteTxn txn(db);
|
SQLiteTxn txn(db);
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, validPaths) {
|
for (auto & i : validPaths) {
|
||||||
addValidPath(queryPathInfoOld(*i), false);
|
addValidPath(queryPathInfoOld(i), false);
|
||||||
std::cerr << ".";
|
std::cerr << ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "|";
|
std::cerr << "|";
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, validPaths) {
|
for (auto & i : validPaths) {
|
||||||
ValidPathInfo info = queryPathInfoOld(*i);
|
ValidPathInfo info = queryPathInfoOld(i);
|
||||||
unsigned long long referrer = queryValidPathId(*i);
|
unsigned long long referrer = queryValidPathId(i);
|
||||||
foreach (PathSet::iterator, j, info.references)
|
for (auto & j : info.references)
|
||||||
addReference(referrer, queryValidPathId(*j));
|
addReference(referrer, queryValidPathId(j));
|
||||||
std::cerr << ".";
|
std::cerr << ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public:
|
|||||||
void querySubstitutablePathInfos(const PathSet & paths,
|
void querySubstitutablePathInfos(const PathSet & paths,
|
||||||
SubstitutablePathInfos & infos);
|
SubstitutablePathInfos & infos);
|
||||||
|
|
||||||
Path addToStore(const Path & srcPath,
|
Path addToStore(const string & name, const Path & srcPath,
|
||||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||||
PathFilter & filter = defaultPathFilter, bool repair = false);
|
PathFilter & filter = defaultPathFilter, bool repair = false);
|
||||||
|
|
||||||
@@ -151,6 +151,9 @@ public:
|
|||||||
|
|
||||||
void buildPaths(const PathSet & paths, BuildMode buildMode);
|
void buildPaths(const PathSet & paths, BuildMode buildMode);
|
||||||
|
|
||||||
|
BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
|
||||||
|
BuildMode buildMode) override;
|
||||||
|
|
||||||
void ensurePath(const Path & path);
|
void ensurePath(const Path & path);
|
||||||
|
|
||||||
void addTempRoot(const Path & path);
|
void addTempRoot(const Path & path);
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
|
|||||||
|
|
||||||
libstore_LIBS = libutil libformat
|
libstore_LIBS = libutil libformat
|
||||||
|
|
||||||
libstore_LDFLAGS = -lsqlite3 -lbz2
|
libstore_LDFLAGS = -lsqlite3 -lbz2 -lcurl
|
||||||
|
|
||||||
ifeq ($(OS), SunOS)
|
ifeq ($(OS), SunOS)
|
||||||
libstore_LDFLAGS += -lsocket
|
libstore_LDFLAGS += -lsocket
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libstore_CXXFLAGS = \
|
libstore_CXXFLAGS = \
|
||||||
|
-DNIX_PREFIX=\"$(prefix)\" \
|
||||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
-DNIX_DATA_DIR=\"$(datadir)\" \
|
||||||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
||||||
@@ -22,7 +23,7 @@ libstore_CXXFLAGS = \
|
|||||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||||
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
||||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||||
-DDEFAULT_CHROOT_DIRS="\"$(DEFAULT_CHROOT_DIRS)\""
|
-DBASH_PATH="\"$(bash)\""
|
||||||
|
|
||||||
$(d)/local-store.cc: $(d)/schema.sql.hh
|
$(d)/local-store.cc: $(d)/schema.sql.hh
|
||||||
|
|
||||||
|
|||||||
+45
-45
@@ -28,15 +28,15 @@ void computeFSClosure(StoreAPI & store, const Path & path,
|
|||||||
|
|
||||||
if (includeOutputs) {
|
if (includeOutputs) {
|
||||||
PathSet derivers = store.queryValidDerivers(path);
|
PathSet derivers = store.queryValidDerivers(path);
|
||||||
foreach (PathSet::iterator, i, derivers)
|
for (auto & i : derivers)
|
||||||
edges.insert(*i);
|
edges.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeDerivers && isDerivation(path)) {
|
if (includeDerivers && isDerivation(path)) {
|
||||||
PathSet outputs = store.queryDerivationOutputs(path);
|
PathSet outputs = store.queryDerivationOutputs(path);
|
||||||
foreach (PathSet::iterator, i, outputs)
|
for (auto & i : outputs)
|
||||||
if (store.isValidPath(*i) && store.queryDeriver(*i) == path)
|
if (store.isValidPath(i) && store.queryDeriver(i) == path)
|
||||||
edges.insert(*i);
|
edges.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -44,8 +44,8 @@ void computeFSClosure(StoreAPI & store, const Path & path,
|
|||||||
|
|
||||||
if (includeOutputs && isDerivation(path)) {
|
if (includeOutputs && isDerivation(path)) {
|
||||||
PathSet outputs = store.queryDerivationOutputs(path);
|
PathSet outputs = store.queryDerivationOutputs(path);
|
||||||
foreach (PathSet::iterator, i, outputs)
|
for (auto & i : outputs)
|
||||||
if (store.isValidPath(*i)) edges.insert(*i);
|
if (store.isValidPath(i)) edges.insert(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeDerivers) {
|
if (includeDerivers) {
|
||||||
@@ -54,15 +54,15 @@ void computeFSClosure(StoreAPI & store, const Path & path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, edges)
|
for (auto & i : edges)
|
||||||
computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
|
computeFSClosure(store, i, paths, flipDirection, includeOutputs, includeDerivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path findOutput(const Derivation & drv, string id)
|
Path findOutput(const Derivation & drv, string id)
|
||||||
{
|
{
|
||||||
foreach (DerivationOutputs::const_iterator, i, drv.outputs)
|
for (auto & i : drv.outputs)
|
||||||
if (i->first == id) return i->second.path;
|
if (i.first == id) return i.second.path;
|
||||||
throw Error(format("derivation has no output ‘%1%’") % id);
|
throw Error(format("derivation has no output ‘%1%’") % id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,36 +98,36 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
|||||||
|
|
||||||
PathSet query, todoDrv, todoNonDrv;
|
PathSet query, todoDrv, todoNonDrv;
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, todo) {
|
for (auto & i : todo) {
|
||||||
if (done.find(*i) != done.end()) continue;
|
if (done.find(i) != done.end()) continue;
|
||||||
done.insert(*i);
|
done.insert(i);
|
||||||
|
|
||||||
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
|
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
|
||||||
|
|
||||||
if (isDerivation(i2.first)) {
|
if (isDerivation(i2.first)) {
|
||||||
if (!store.isValidPath(i2.first)) {
|
if (!store.isValidPath(i2.first)) {
|
||||||
// FIXME: we could try to substitute p.
|
// FIXME: we could try to substitute p.
|
||||||
unknown.insert(*i);
|
unknown.insert(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Derivation drv = derivationFromPath(store, i2.first);
|
Derivation drv = derivationFromPath(store, i2.first);
|
||||||
|
|
||||||
PathSet invalid;
|
PathSet invalid;
|
||||||
foreach (DerivationOutputs::iterator, j, drv.outputs)
|
for (auto & j : drv.outputs)
|
||||||
if (wantOutput(j->first, i2.second)
|
if (wantOutput(j.first, i2.second)
|
||||||
&& !store.isValidPath(j->second.path))
|
&& !store.isValidPath(j.second.path))
|
||||||
invalid.insert(j->second.path);
|
invalid.insert(j.second.path);
|
||||||
if (invalid.empty()) continue;
|
if (invalid.empty()) continue;
|
||||||
|
|
||||||
todoDrv.insert(*i);
|
todoDrv.insert(i);
|
||||||
if (settings.useSubstitutes && !willBuildLocally(drv))
|
if (settings.useSubstitutes && substitutesAllowed(drv))
|
||||||
query.insert(invalid.begin(), invalid.end());
|
query.insert(invalid.begin(), invalid.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if (store.isValidPath(*i)) continue;
|
if (store.isValidPath(i)) continue;
|
||||||
query.insert(*i);
|
query.insert(i);
|
||||||
todoNonDrv.insert(*i);
|
todoNonDrv.insert(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,22 +136,22 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
|||||||
SubstitutablePathInfos infos;
|
SubstitutablePathInfos infos;
|
||||||
store.querySubstitutablePathInfos(query, infos);
|
store.querySubstitutablePathInfos(query, infos);
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, todoDrv) {
|
for (auto & i : todoDrv) {
|
||||||
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
|
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(i);
|
||||||
|
|
||||||
// FIXME: cache this
|
// FIXME: cache this
|
||||||
Derivation drv = derivationFromPath(store, i2.first);
|
Derivation drv = derivationFromPath(store, i2.first);
|
||||||
|
|
||||||
PathSet outputs;
|
PathSet outputs;
|
||||||
bool mustBuild = false;
|
bool mustBuild = false;
|
||||||
if (settings.useSubstitutes && !willBuildLocally(drv)) {
|
if (settings.useSubstitutes && substitutesAllowed(drv)) {
|
||||||
foreach (DerivationOutputs::iterator, j, drv.outputs) {
|
for (auto & j : drv.outputs) {
|
||||||
if (!wantOutput(j->first, i2.second)) continue;
|
if (!wantOutput(j.first, i2.second)) continue;
|
||||||
if (!store.isValidPath(j->second.path)) {
|
if (!store.isValidPath(j.second.path)) {
|
||||||
if (infos.find(j->second.path) == infos.end())
|
if (infos.find(j.second.path) == infos.end())
|
||||||
mustBuild = true;
|
mustBuild = true;
|
||||||
else
|
else
|
||||||
outputs.insert(j->second.path);
|
outputs.insert(j.second.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@@ -160,22 +160,22 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
|||||||
if (mustBuild) {
|
if (mustBuild) {
|
||||||
willBuild.insert(i2.first);
|
willBuild.insert(i2.first);
|
||||||
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||||
foreach (DerivationInputs::iterator, j, drv.inputDrvs)
|
for (auto & j : drv.inputDrvs)
|
||||||
todo.insert(makeDrvPathWithOutputs(j->first, j->second));
|
todo.insert(makeDrvPathWithOutputs(j.first, j.second));
|
||||||
} else
|
} else
|
||||||
todoNonDrv.insert(outputs.begin(), outputs.end());
|
todoNonDrv.insert(outputs.begin(), outputs.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, todoNonDrv) {
|
for (auto & i : todoNonDrv) {
|
||||||
done.insert(*i);
|
done.insert(i);
|
||||||
SubstitutablePathInfos::iterator info = infos.find(*i);
|
SubstitutablePathInfos::iterator info = infos.find(i);
|
||||||
if (info != infos.end()) {
|
if (info != infos.end()) {
|
||||||
willSubstitute.insert(*i);
|
willSubstitute.insert(i);
|
||||||
downloadSize += info->second.downloadSize;
|
downloadSize += info->second.downloadSize;
|
||||||
narSize += info->second.narSize;
|
narSize += info->second.narSize;
|
||||||
todo.insert(info->second.references.begin(), info->second.references.end());
|
todo.insert(info->second.references.begin(), info->second.references.end());
|
||||||
} else
|
} else
|
||||||
unknown.insert(*i);
|
unknown.insert(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,11 +196,11 @@ static void dfsVisit(StoreAPI & store, const PathSet & paths,
|
|||||||
if (store.isValidPath(path))
|
if (store.isValidPath(path))
|
||||||
store.queryReferences(path, references);
|
store.queryReferences(path, references);
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, references)
|
for (auto & i : references)
|
||||||
/* Don't traverse into paths that don't exist. That can
|
/* Don't traverse into paths that don't exist. That can
|
||||||
happen due to substitutes for non-existent paths. */
|
happen due to substitutes for non-existent paths. */
|
||||||
if (*i != path && paths.find(*i) != paths.end())
|
if (i != path && paths.find(i) != paths.end())
|
||||||
dfsVisit(store, paths, *i, visited, sorted, parents);
|
dfsVisit(store, paths, i, visited, sorted, parents);
|
||||||
|
|
||||||
sorted.push_front(path);
|
sorted.push_front(path);
|
||||||
parents.erase(path);
|
parents.erase(path);
|
||||||
@@ -211,8 +211,8 @@ Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
|
|||||||
{
|
{
|
||||||
Paths sorted;
|
Paths sorted;
|
||||||
PathSet visited, parents;
|
PathSet visited, parents;
|
||||||
foreach (PathSet::const_iterator, i, paths)
|
for (auto & i : paths)
|
||||||
dfsVisit(store, paths, *i, visited, sorted, parents);
|
dfsVisit(store, paths, i, visited, sorted, parents);
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
|||||||
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
|
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
|
||||||
unsigned long long & downloadSize, unsigned long long & narSize);
|
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)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
Strings names = readDirectoryIgnoringInodes(path, inodeHash);
|
Strings names = readDirectoryIgnoringInodes(path, inodeHash);
|
||||||
foreach (Strings::iterator, i, names)
|
for (auto & i : names)
|
||||||
optimisePath_(stats, path + "/" + *i, inodeHash);
|
optimisePath_(stats, path + "/" + i, inodeHash);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,11 +218,11 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
|
|||||||
PathSet paths = queryAllValidPaths();
|
PathSet paths = queryAllValidPaths();
|
||||||
InodeHash inodeHash = loadInodeHash();
|
InodeHash inodeHash = loadInodeHash();
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
addTempRoot(*i);
|
addTempRoot(i);
|
||||||
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
|
if (!isValidPath(i)) continue; /* path was GC'ed, probably */
|
||||||
startNest(nest, lvlChatty, format("hashing files in ‘%1%’") % *i);
|
startNest(nest, lvlChatty, format("hashing files in ‘%1%’") % i);
|
||||||
optimisePath_(stats, *i, inodeHash);
|
optimisePath_(stats, i, inodeHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,9 +104,8 @@ bool PathLocks::lockPaths(const PathSet & _paths,
|
|||||||
paths.sort();
|
paths.sort();
|
||||||
|
|
||||||
/* Acquire the lock for each path. */
|
/* Acquire the lock for each path. */
|
||||||
foreach (Paths::iterator, i, paths) {
|
for (auto & path : paths) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
Path path = *i;
|
|
||||||
Path lockPath = path + ".lock";
|
Path lockPath = path + ".lock";
|
||||||
|
|
||||||
debug(format("locking path ‘%1%’") % path);
|
debug(format("locking path ‘%1%’") % path);
|
||||||
@@ -119,7 +118,7 @@ bool PathLocks::lockPaths(const PathSet & _paths,
|
|||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
/* Open/create the lock file. */
|
/* Open/create the lock file. */
|
||||||
fd = openLockFile(lockPath, true);
|
fd = openLockFile(lockPath, true);
|
||||||
|
|
||||||
/* Acquire an exclusive lock. */
|
/* Acquire an exclusive lock. */
|
||||||
if (!lockFile(fd, ltWrite, false)) {
|
if (!lockFile(fd, ltWrite, false)) {
|
||||||
@@ -168,15 +167,15 @@ PathLocks::~PathLocks()
|
|||||||
|
|
||||||
void PathLocks::unlock()
|
void PathLocks::unlock()
|
||||||
{
|
{
|
||||||
foreach (list<FDPair>::iterator, i, fds) {
|
for (auto & i : fds) {
|
||||||
if (deletePaths) deleteLockFile(i->second, i->first);
|
if (deletePaths) deleteLockFile(i.second, i.first);
|
||||||
|
|
||||||
lockedPaths.erase(i->second);
|
lockedPaths.erase(i.second);
|
||||||
if (close(i->first) == -1)
|
if (close(i.first) == -1)
|
||||||
printMsg(lvlError,
|
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();
|
fds.clear();
|
||||||
|
|||||||
@@ -80,7 +80,25 @@ Path createGeneration(Path profile, Path outPath)
|
|||||||
previous ones. */
|
previous ones. */
|
||||||
int dummy;
|
int dummy;
|
||||||
Generations gens = findGenerations(profile, 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
|
/* Create the new generation. Note that addPermRoot() blocks if
|
||||||
the garbage collector is running to prevent the stuff we've
|
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)
|
void switchLink(Path link, Path target)
|
||||||
{
|
{
|
||||||
/* Hacky. */
|
/* Hacky. */
|
||||||
if (dirOf(target) == dirOf(link)) target = baseNameOf(target);
|
if (dirOf(target) == dirOf(link)) target = baseNameOf(target);
|
||||||
|
|
||||||
Path tmp = canonPath(dirOf(link) + "/.new_" + baseNameOf(link));
|
replaceSymlink(target, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -35,6 +35,14 @@ Path createGeneration(Path profile, Path outPath);
|
|||||||
|
|
||||||
void deleteGeneration(const Path & profile, unsigned int gen);
|
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);
|
void switchLink(Path link, Path target);
|
||||||
|
|
||||||
/* Ensure exclusive access to a profile. Any command that modifies
|
/* Ensure exclusive access to a profile. Any command that modifies
|
||||||
@@ -89,17 +89,17 @@ PathSet scanForReferences(const string & path,
|
|||||||
/* For efficiency (and a higher hit rate), just search for the
|
/* For efficiency (and a higher hit rate), just search for the
|
||||||
hash part of the file name. (This assumes that all references
|
hash part of the file name. (This assumes that all references
|
||||||
have the form `HASH-bla'). */
|
have the form `HASH-bla'). */
|
||||||
foreach (PathSet::const_iterator, i, refs) {
|
for (auto & i : refs) {
|
||||||
string baseName = baseNameOf(*i);
|
string baseName = baseNameOf(i);
|
||||||
string::size_type pos = baseName.find('-');
|
string::size_type pos = baseName.find('-');
|
||||||
if (pos == string::npos)
|
if (pos == string::npos)
|
||||||
throw Error(format("bad reference ‘%1%’") % *i);
|
throw Error(format("bad reference ‘%1%’") % i);
|
||||||
string s = string(baseName, 0, pos);
|
string s = string(baseName, 0, pos);
|
||||||
assert(s.size() == refLength);
|
assert(s.size() == refLength);
|
||||||
assert(backMap.find(s) == backMap.end());
|
assert(backMap.find(s) == backMap.end());
|
||||||
// parseHash(htSHA256, s);
|
// parseHash(htSHA256, s);
|
||||||
sink.hashes.insert(s);
|
sink.hashes.insert(s);
|
||||||
backMap[s] = *i;
|
backMap[s] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look for the hashes in the NAR dump of the path. */
|
/* Look for the hashes in the NAR dump of the path. */
|
||||||
@@ -107,9 +107,9 @@ PathSet scanForReferences(const string & path,
|
|||||||
|
|
||||||
/* Map the hashes found back to their store paths. */
|
/* Map the hashes found back to their store paths. */
|
||||||
PathSet found;
|
PathSet found;
|
||||||
foreach (StringSet::iterator, i, sink.seen) {
|
for (auto & i : sink.seen) {
|
||||||
std::map<string, Path>::iterator j;
|
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);
|
found.insert(j->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+95
-109
@@ -5,6 +5,7 @@
|
|||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "affinity.hh"
|
#include "affinity.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -31,7 +32,7 @@ Path readStorePath(Source & from)
|
|||||||
template<class T> T readStorePaths(Source & from)
|
template<class T> T readStorePaths(Source & from)
|
||||||
{
|
{
|
||||||
T paths = readStrings<T>(from);
|
T paths = readStrings<T>(from);
|
||||||
foreach (typename T::iterator, i, paths) assertStorePath(*i);
|
for (auto & i : paths) assertStorePath(i);
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ void RemoteStore::openConnection(bool reserveSpace)
|
|||||||
|
|
||||||
/* Send the magic greeting, check for the reply. */
|
/* Send the magic greeting, check for the reply. */
|
||||||
try {
|
try {
|
||||||
writeInt(WORKER_MAGIC_1, to);
|
to << WORKER_MAGIC_1;
|
||||||
to.flush();
|
to.flush();
|
||||||
unsigned int magic = readInt(from);
|
unsigned int magic = readInt(from);
|
||||||
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
|
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
|
||||||
@@ -71,19 +72,18 @@ void RemoteStore::openConnection(bool reserveSpace)
|
|||||||
daemonVersion = readInt(from);
|
daemonVersion = readInt(from);
|
||||||
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
||||||
throw Error("Nix daemon protocol version not supported");
|
throw Error("Nix daemon protocol version not supported");
|
||||||
writeInt(PROTOCOL_VERSION, to);
|
to << PROTOCOL_VERSION;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
|
||||||
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
|
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
|
||||||
if (cpu != -1) {
|
if (cpu != -1)
|
||||||
writeInt(1, to);
|
to << 1 << cpu;
|
||||||
writeInt(cpu, to);
|
else
|
||||||
} else
|
to << 0;
|
||||||
writeInt(0, to);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
|
||||||
writeInt(reserveSpace, to);
|
to << reserveSpace;
|
||||||
|
|
||||||
processStderr();
|
processStderr();
|
||||||
}
|
}
|
||||||
@@ -141,35 +141,31 @@ RemoteStore::~RemoteStore()
|
|||||||
|
|
||||||
void RemoteStore::setOptions()
|
void RemoteStore::setOptions()
|
||||||
{
|
{
|
||||||
writeInt(wopSetOptions, to);
|
to << wopSetOptions
|
||||||
|
<< settings.keepFailed
|
||||||
writeInt(settings.keepFailed, to);
|
<< settings.keepGoing
|
||||||
writeInt(settings.keepGoing, to);
|
<< settings.tryFallback
|
||||||
writeInt(settings.tryFallback, to);
|
<< verbosity
|
||||||
writeInt(verbosity, to);
|
<< settings.maxBuildJobs
|
||||||
writeInt(settings.maxBuildJobs, to);
|
<< settings.maxSilentTime;
|
||||||
writeInt(settings.maxSilentTime, to);
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
|
||||||
writeInt(settings.useBuildHook, to);
|
to << settings.useBuildHook;
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4)
|
||||||
writeInt(settings.buildVerbosity, to);
|
to << settings.buildVerbosity
|
||||||
writeInt(logType, to);
|
<< logType
|
||||||
writeInt(settings.printBuildTrace, to);
|
<< settings.printBuildTrace;
|
||||||
}
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
|
||||||
writeInt(settings.buildCores, to);
|
to << settings.buildCores;
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
|
||||||
writeInt(settings.useSubstitutes, to);
|
to << settings.useSubstitutes;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) {
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) {
|
||||||
Settings::SettingsMap overrides = settings.getOverrides();
|
Settings::SettingsMap overrides = settings.getOverrides();
|
||||||
if (overrides["ssh-auth-sock"] == "")
|
if (overrides["ssh-auth-sock"] == "")
|
||||||
overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK");
|
overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK");
|
||||||
writeInt(overrides.size(), to);
|
to << overrides.size();
|
||||||
foreach (Settings::SettingsMap::iterator, i, overrides) {
|
for (auto & i : overrides)
|
||||||
writeString(i->first, to);
|
to << i.first << i.second;
|
||||||
writeString(i->second, to);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processStderr();
|
processStderr();
|
||||||
@@ -179,8 +175,7 @@ void RemoteStore::setOptions()
|
|||||||
bool RemoteStore::isValidPath(const Path & path)
|
bool RemoteStore::isValidPath(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopIsValidPath, to);
|
to << wopIsValidPath << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
unsigned int reply = readInt(from);
|
unsigned int reply = readInt(from);
|
||||||
return reply != 0;
|
return reply != 0;
|
||||||
@@ -192,12 +187,11 @@ PathSet RemoteStore::queryValidPaths(const PathSet & paths)
|
|||||||
openConnection();
|
openConnection();
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
||||||
PathSet res;
|
PathSet res;
|
||||||
foreach (PathSet::const_iterator, i, paths)
|
for (auto & i : paths)
|
||||||
if (isValidPath(*i)) res.insert(*i);
|
if (isValidPath(i)) res.insert(i);
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
writeInt(wopQueryValidPaths, to);
|
to << wopQueryValidPaths << paths;
|
||||||
writeStrings(paths, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePaths<PathSet>(from);
|
return readStorePaths<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -207,7 +201,7 @@ PathSet RemoteStore::queryValidPaths(const PathSet & paths)
|
|||||||
PathSet RemoteStore::queryAllValidPaths()
|
PathSet RemoteStore::queryAllValidPaths()
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryAllValidPaths, to);
|
to << wopQueryAllValidPaths;
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePaths<PathSet>(from);
|
return readStorePaths<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -218,16 +212,14 @@ PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths)
|
|||||||
openConnection();
|
openConnection();
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
||||||
PathSet res;
|
PathSet res;
|
||||||
foreach (PathSet::const_iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
writeInt(wopHasSubstitutes, to);
|
to << wopHasSubstitutes << i;
|
||||||
writeString(*i, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
if (readInt(from)) res.insert(*i);
|
if (readInt(from)) res.insert(i);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
writeInt(wopQuerySubstitutablePaths, to);
|
to << wopQuerySubstitutablePaths << paths;
|
||||||
writeStrings(paths, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePaths<PathSet>(from);
|
return readStorePaths<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -245,10 +237,9 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
|
|||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
||||||
|
|
||||||
foreach (PathSet::const_iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
SubstitutablePathInfo info;
|
SubstitutablePathInfo info;
|
||||||
writeInt(wopQuerySubstitutablePathInfo, to);
|
to << wopQuerySubstitutablePathInfo << i;
|
||||||
writeString(*i, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
unsigned int reply = readInt(from);
|
unsigned int reply = readInt(from);
|
||||||
if (reply == 0) continue;
|
if (reply == 0) continue;
|
||||||
@@ -257,13 +248,12 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
|
|||||||
info.references = readStorePaths<PathSet>(from);
|
info.references = readStorePaths<PathSet>(from);
|
||||||
info.downloadSize = readLongLong(from);
|
info.downloadSize = readLongLong(from);
|
||||||
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
|
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
|
||||||
infos[*i] = info;
|
infos[i] = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
writeInt(wopQuerySubstitutablePathInfos, to);
|
to << wopQuerySubstitutablePathInfos << paths;
|
||||||
writeStrings(paths, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
unsigned int count = readInt(from);
|
unsigned int count = readInt(from);
|
||||||
for (unsigned int n = 0; n < count; n++) {
|
for (unsigned int n = 0; n < count; n++) {
|
||||||
@@ -283,8 +273,7 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
|
|||||||
ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
|
ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryPathInfo, to);
|
to << wopQueryPathInfo << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
ValidPathInfo info;
|
ValidPathInfo info;
|
||||||
info.path = path;
|
info.path = path;
|
||||||
@@ -301,8 +290,7 @@ ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
|
|||||||
Hash RemoteStore::queryPathHash(const Path & path)
|
Hash RemoteStore::queryPathHash(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryPathHash, to);
|
to << wopQueryPathHash << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
string hash = readString(from);
|
string hash = readString(from);
|
||||||
return parseHash(htSHA256, hash);
|
return parseHash(htSHA256, hash);
|
||||||
@@ -313,8 +301,7 @@ void RemoteStore::queryReferences(const Path & path,
|
|||||||
PathSet & references)
|
PathSet & references)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryReferences, to);
|
to << wopQueryReferences << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
PathSet references2 = readStorePaths<PathSet>(from);
|
PathSet references2 = readStorePaths<PathSet>(from);
|
||||||
references.insert(references2.begin(), references2.end());
|
references.insert(references2.begin(), references2.end());
|
||||||
@@ -325,8 +312,7 @@ void RemoteStore::queryReferrers(const Path & path,
|
|||||||
PathSet & referrers)
|
PathSet & referrers)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryReferrers, to);
|
to << wopQueryReferrers << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
PathSet referrers2 = readStorePaths<PathSet>(from);
|
PathSet referrers2 = readStorePaths<PathSet>(from);
|
||||||
referrers.insert(referrers2.begin(), referrers2.end());
|
referrers.insert(referrers2.begin(), referrers2.end());
|
||||||
@@ -336,8 +322,7 @@ void RemoteStore::queryReferrers(const Path & path,
|
|||||||
Path RemoteStore::queryDeriver(const Path & path)
|
Path RemoteStore::queryDeriver(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryDeriver, to);
|
to << wopQueryDeriver << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
Path drvPath = readString(from);
|
Path drvPath = readString(from);
|
||||||
if (drvPath != "") assertStorePath(drvPath);
|
if (drvPath != "") assertStorePath(drvPath);
|
||||||
@@ -348,8 +333,7 @@ Path RemoteStore::queryDeriver(const Path & path)
|
|||||||
PathSet RemoteStore::queryValidDerivers(const Path & path)
|
PathSet RemoteStore::queryValidDerivers(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryValidDerivers, to);
|
to << wopQueryValidDerivers << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePaths<PathSet>(from);
|
return readStorePaths<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -358,8 +342,7 @@ PathSet RemoteStore::queryValidDerivers(const Path & path)
|
|||||||
PathSet RemoteStore::queryDerivationOutputs(const Path & path)
|
PathSet RemoteStore::queryDerivationOutputs(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryDerivationOutputs, to);
|
to << wopQueryDerivationOutputs << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePaths<PathSet>(from);
|
return readStorePaths<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -368,8 +351,7 @@ PathSet RemoteStore::queryDerivationOutputs(const Path & path)
|
|||||||
PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
|
PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryDerivationOutputNames, to);
|
to << wopQueryDerivationOutputNames << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStrings<PathSet>(from);
|
return readStrings<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -378,8 +360,7 @@ PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
|
|||||||
Path RemoteStore::queryPathFromHashPart(const string & hashPart)
|
Path RemoteStore::queryPathFromHashPart(const string & hashPart)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryPathFromHashPart, to);
|
to << wopQueryPathFromHashPart << hashPart;
|
||||||
writeString(hashPart, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
Path path = readString(from);
|
Path path = readString(from);
|
||||||
if (!path.empty()) assertStorePath(path);
|
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)
|
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
|
||||||
{
|
{
|
||||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
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));
|
Path srcPath(absPath(_srcPath));
|
||||||
|
|
||||||
writeInt(wopAddToStore, to);
|
to << wopAddToStore << name
|
||||||
writeString(baseNameOf(srcPath), to);
|
<< ((hashAlgo == htSHA256 && recursive) ? 0 : 1) /* backwards compatibility hack */
|
||||||
/* backwards compatibility hack */
|
<< (recursive ? 1 : 0)
|
||||||
writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
|
<< printHashType(hashAlgo);
|
||||||
writeInt(recursive ? 1 : 0, to);
|
|
||||||
writeString(printHashType(hashAlgo), to);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
to.written = 0;
|
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");
|
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
||||||
|
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopAddTextToStore, to);
|
to << wopAddTextToStore << name << s << references;
|
||||||
writeString(name, to);
|
|
||||||
writeString(s, to);
|
|
||||||
writeStrings(references, to);
|
|
||||||
|
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePath(from);
|
return readStorePath(from);
|
||||||
@@ -443,9 +419,7 @@ void RemoteStore::exportPath(const Path & path, bool sign,
|
|||||||
Sink & sink)
|
Sink & sink)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopExportPath, to);
|
to << wopExportPath << path << (sign ? 1 : 0);
|
||||||
writeString(path, to);
|
|
||||||
writeInt(sign ? 1 : 0, to);
|
|
||||||
processStderr(&sink); /* sink receives the actual data */
|
processStderr(&sink); /* sink receives the actual data */
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
@@ -454,7 +428,7 @@ void RemoteStore::exportPath(const Path & path, bool sign,
|
|||||||
Paths RemoteStore::importPaths(bool requireSignature, Source & source)
|
Paths RemoteStore::importPaths(bool requireSignature, Source & source)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopImportPaths, to);
|
to << wopImportPaths;
|
||||||
/* We ignore requireSignature, since the worker forces it to true
|
/* We ignore requireSignature, since the worker forces it to true
|
||||||
anyway. */
|
anyway. */
|
||||||
processStderr(0, &source);
|
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");
|
if (buildMode != bmNormal) throw Error("repairing or checking is not supported when building through the Nix daemon");
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopBuildPaths, to);
|
to << wopBuildPaths;
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
|
||||||
writeStrings(drvPaths, to);
|
to << drvPaths;
|
||||||
else {
|
else {
|
||||||
/* For backwards compatibility with old daemons, strip output
|
/* For backwards compatibility with old daemons, strip output
|
||||||
identifiers. */
|
identifiers. */
|
||||||
PathSet drvPaths2;
|
PathSet drvPaths2;
|
||||||
foreach (PathSet::const_iterator, i, drvPaths)
|
for (auto & i : drvPaths)
|
||||||
drvPaths2.insert(string(*i, 0, i->find('!')));
|
drvPaths2.insert(string(i, 0, i.find('!')));
|
||||||
writeStrings(drvPaths2, to);
|
to << drvPaths2;
|
||||||
}
|
}
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
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)
|
void RemoteStore::ensurePath(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopEnsurePath, to);
|
to << wopEnsurePath << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
@@ -495,8 +482,7 @@ void RemoteStore::ensurePath(const Path & path)
|
|||||||
void RemoteStore::addTempRoot(const Path & path)
|
void RemoteStore::addTempRoot(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopAddTempRoot, to);
|
to << wopAddTempRoot << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
@@ -505,8 +491,7 @@ void RemoteStore::addTempRoot(const Path & path)
|
|||||||
void RemoteStore::addIndirectRoot(const Path & path)
|
void RemoteStore::addIndirectRoot(const Path & path)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopAddIndirectRoot, to);
|
to << wopAddIndirectRoot << path;
|
||||||
writeString(path, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
@@ -515,7 +500,7 @@ void RemoteStore::addIndirectRoot(const Path & path)
|
|||||||
void RemoteStore::syncWithGC()
|
void RemoteStore::syncWithGC()
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopSyncWithGC, to);
|
to << wopSyncWithGC;
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
@@ -524,7 +509,7 @@ void RemoteStore::syncWithGC()
|
|||||||
Roots RemoteStore::findRoots()
|
Roots RemoteStore::findRoots()
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopFindRoots, to);
|
to << wopFindRoots;
|
||||||
processStderr();
|
processStderr();
|
||||||
unsigned int count = readInt(from);
|
unsigned int count = readInt(from);
|
||||||
Roots result;
|
Roots result;
|
||||||
@@ -541,17 +526,11 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||||||
{
|
{
|
||||||
openConnection(false);
|
openConnection(false);
|
||||||
|
|
||||||
writeInt(wopCollectGarbage, to);
|
to << wopCollectGarbage << options.action << options.pathsToDelete << options.ignoreLiveness
|
||||||
writeInt(options.action, to);
|
<< options.maxFreed << 0;
|
||||||
writeStrings(options.pathsToDelete, to);
|
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5)
|
||||||
writeInt(options.ignoreLiveness, to);
|
|
||||||
writeLongLong(options.maxFreed, to);
|
|
||||||
writeInt(0, to);
|
|
||||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) {
|
|
||||||
/* removed options */
|
/* removed options */
|
||||||
writeInt(0, to);
|
to << 0 << 0;
|
||||||
writeInt(0, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
processStderr();
|
processStderr();
|
||||||
|
|
||||||
@@ -564,7 +543,7 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
|
|||||||
PathSet RemoteStore::queryFailedPaths()
|
PathSet RemoteStore::queryFailedPaths()
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopQueryFailedPaths, to);
|
to << wopQueryFailedPaths;
|
||||||
processStderr();
|
processStderr();
|
||||||
return readStorePaths<PathSet>(from);
|
return readStorePaths<PathSet>(from);
|
||||||
}
|
}
|
||||||
@@ -573,8 +552,7 @@ PathSet RemoteStore::queryFailedPaths()
|
|||||||
void RemoteStore::clearFailedPaths(const PathSet & paths)
|
void RemoteStore::clearFailedPaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopClearFailedPaths, to);
|
to << wopClearFailedPaths << paths;
|
||||||
writeStrings(paths, to);
|
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
@@ -582,11 +560,19 @@ void RemoteStore::clearFailedPaths(const PathSet & paths)
|
|||||||
void RemoteStore::optimiseStore()
|
void RemoteStore::optimiseStore()
|
||||||
{
|
{
|
||||||
openConnection();
|
openConnection();
|
||||||
writeInt(wopOptimiseStore, to);
|
to << wopOptimiseStore;
|
||||||
processStderr();
|
processStderr();
|
||||||
readInt(from);
|
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)
|
void RemoteStore::processStderr(Sink * sink, Source * source)
|
||||||
{
|
{
|
||||||
to.flush();
|
to.flush();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public:
|
|||||||
void querySubstitutablePathInfos(const PathSet & paths,
|
void querySubstitutablePathInfos(const PathSet & paths,
|
||||||
SubstitutablePathInfos & infos);
|
SubstitutablePathInfos & infos);
|
||||||
|
|
||||||
Path addToStore(const Path & srcPath,
|
Path addToStore(const string & name, const Path & srcPath,
|
||||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||||
PathFilter & filter = defaultPathFilter, bool repair = false);
|
PathFilter & filter = defaultPathFilter, bool repair = false);
|
||||||
|
|
||||||
@@ -67,6 +67,9 @@ public:
|
|||||||
|
|
||||||
void buildPaths(const PathSet & paths, BuildMode buildMode);
|
void buildPaths(const PathSet & paths, BuildMode buildMode);
|
||||||
|
|
||||||
|
BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
|
||||||
|
BuildMode buildMode) override;
|
||||||
|
|
||||||
void ensurePath(const Path & path);
|
void ensurePath(const Path & path);
|
||||||
|
|
||||||
void addTempRoot(const Path & path);
|
void addTempRoot(const Path & path);
|
||||||
@@ -85,6 +88,7 @@ public:
|
|||||||
|
|
||||||
void optimiseStore();
|
void optimiseStore();
|
||||||
|
|
||||||
|
bool verifyStore(bool checkContents, bool repair);
|
||||||
private:
|
private:
|
||||||
AutoCloseFD fdSocket;
|
AutoCloseFD fdSocket;
|
||||||
FdSink to;
|
FdSink to;
|
||||||
|
|||||||
+19
-29
@@ -2,20 +2,10 @@
|
|||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include <climits>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
GCOptions::GCOptions()
|
|
||||||
{
|
|
||||||
action = gcDeleteDead;
|
|
||||||
ignoreLiveness = false;
|
|
||||||
maxFreed = ULLONG_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool isInStore(const Path & path)
|
bool isInStore(const Path & path)
|
||||||
{
|
{
|
||||||
return isInDir(path, settings.nixStore);
|
return isInDir(path, settings.nixStore);
|
||||||
@@ -82,14 +72,14 @@ void checkStoreName(const string & name)
|
|||||||
reasons (e.g., "." and ".."). */
|
reasons (e.g., "." and ".."). */
|
||||||
if (string(name, 0, 1) == ".")
|
if (string(name, 0, 1) == ".")
|
||||||
throw Error(format("illegal name: ‘%1%’") % name);
|
throw Error(format("illegal name: ‘%1%’") % name);
|
||||||
foreach (string::const_iterator, i, name)
|
for (auto & i : name)
|
||||||
if (!((*i >= 'A' && *i <= 'Z') ||
|
if (!((i >= 'A' && i <= 'Z') ||
|
||||||
(*i >= 'a' && *i <= 'z') ||
|
(i >= 'a' && i <= 'z') ||
|
||||||
(*i >= '0' && *i <= '9') ||
|
(i >= '0' && i <= '9') ||
|
||||||
validChars.find(*i) != string::npos))
|
validChars.find(i) != string::npos))
|
||||||
{
|
{
|
||||||
throw Error(format("invalid character ‘%1%’ in name ‘%2%’")
|
throw Error(format("invalid character ‘%1%’ in name ‘%2%’")
|
||||||
% *i % name);
|
% i % name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
hacky, but we can't put them in `s' since that would be
|
||||||
ambiguous. */
|
ambiguous. */
|
||||||
string type = "text";
|
string type = "text";
|
||||||
foreach (PathSet::const_iterator, i, references) {
|
for (auto & i : references) {
|
||||||
type += ":";
|
type += ":";
|
||||||
type += *i;
|
type += i;
|
||||||
}
|
}
|
||||||
return makeStorePath(type, hash, name);
|
return makeStorePath(type, hash, name);
|
||||||
}
|
}
|
||||||
@@ -235,10 +225,10 @@ string StoreAPI::makeValidityRegistration(const PathSet & paths,
|
|||||||
{
|
{
|
||||||
string s = "";
|
string s = "";
|
||||||
|
|
||||||
foreach (PathSet::iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
s += *i + "\n";
|
s += i + "\n";
|
||||||
|
|
||||||
ValidPathInfo info = queryPathInfo(*i);
|
ValidPathInfo info = queryPathInfo(i);
|
||||||
|
|
||||||
if (showHash) {
|
if (showHash) {
|
||||||
s += printHash(info.hash) + "\n";
|
s += printHash(info.hash) + "\n";
|
||||||
@@ -250,8 +240,8 @@ string StoreAPI::makeValidityRegistration(const PathSet & paths,
|
|||||||
|
|
||||||
s += (format("%1%\n") % info.references.size()).str();
|
s += (format("%1%\n") % info.references.size()).str();
|
||||||
|
|
||||||
foreach (PathSet::iterator, j, info.references)
|
for (auto & j : info.references)
|
||||||
s += *j + "\n";
|
s += j + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -286,9 +276,9 @@ ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
|
|||||||
string showPaths(const PathSet & paths)
|
string showPaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
string s;
|
string s;
|
||||||
foreach (PathSet::const_iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
if (s.size() != 0) s += ", ";
|
if (s.size() != 0) s += ", ";
|
||||||
s += "‘" + *i + "’";
|
s += "‘" + i + "’";
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -297,11 +287,11 @@ string showPaths(const PathSet & paths)
|
|||||||
void exportPaths(StoreAPI & store, const Paths & paths,
|
void exportPaths(StoreAPI & store, const Paths & paths,
|
||||||
bool sign, Sink & sink)
|
bool sign, Sink & sink)
|
||||||
{
|
{
|
||||||
foreach (Paths::const_iterator, i, paths) {
|
for (auto & i : paths) {
|
||||||
writeInt(1, sink);
|
sink << 1;
|
||||||
store.exportPath(*i, sign, sink);
|
store.exportPath(i, sign, sink);
|
||||||
}
|
}
|
||||||
writeInt(0, sink);
|
sink << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -36,21 +37,19 @@ struct GCOptions
|
|||||||
gcDeleteSpecific,
|
gcDeleteSpecific,
|
||||||
} GCAction;
|
} GCAction;
|
||||||
|
|
||||||
GCAction action;
|
GCAction action{gcDeleteDead};
|
||||||
|
|
||||||
/* If `ignoreLiveness' is set, then reachability from the roots is
|
/* If `ignoreLiveness' is set, then reachability from the roots is
|
||||||
ignored (dangerous!). However, the paths must still be
|
ignored (dangerous!). However, the paths must still be
|
||||||
unreferenced *within* the store (i.e., there can be no other
|
unreferenced *within* the store (i.e., there can be no other
|
||||||
store paths that depend on them). */
|
store paths that depend on them). */
|
||||||
bool ignoreLiveness;
|
bool ignoreLiveness{false};
|
||||||
|
|
||||||
/* For `gcDeleteSpecific', the paths to delete. */
|
/* For `gcDeleteSpecific', the paths to delete. */
|
||||||
PathSet pathsToDelete;
|
PathSet pathsToDelete;
|
||||||
|
|
||||||
/* Stop after at least `maxFreed' bytes have been freed. */
|
/* Stop after at least `maxFreed' bytes have been freed. */
|
||||||
unsigned long long maxFreed;
|
unsigned long long maxFreed{std::numeric_limits<unsigned long long>::max()};
|
||||||
|
|
||||||
GCOptions();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -100,6 +99,32 @@ typedef list<ValidPathInfo> ValidPathInfos;
|
|||||||
enum BuildMode { bmNormal, bmRepair, bmCheck };
|
enum BuildMode { bmNormal, bmRepair, bmCheck };
|
||||||
|
|
||||||
|
|
||||||
|
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
|
class StoreAPI
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -164,7 +189,7 @@ public:
|
|||||||
validity the resulting path. The resulting path is returned.
|
validity the resulting path. The resulting path is returned.
|
||||||
The function object `filter' can be used to exclude files (see
|
The function object `filter' can be used to exclude files (see
|
||||||
libutil/archive.hh). */
|
libutil/archive.hh). */
|
||||||
virtual Path addToStore(const Path & srcPath,
|
virtual Path addToStore(const string & name, const Path & srcPath,
|
||||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||||
PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
|
PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
|
||||||
|
|
||||||
@@ -194,6 +219,12 @@ public:
|
|||||||
not derivations, substitute them. */
|
not derivations, substitute them. */
|
||||||
virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) = 0;
|
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
|
/* 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
|
may be made valid by running a substitute (if defined for the
|
||||||
path). */
|
path). */
|
||||||
@@ -254,6 +285,10 @@ public:
|
|||||||
/* Optimise the disk space usage of the Nix store by hard-linking files
|
/* Optimise the disk space usage of the Nix store by hard-linking files
|
||||||
with the same contents. */
|
with the same contents. */
|
||||||
virtual void optimiseStore() = 0;
|
virtual void optimiseStore() = 0;
|
||||||
|
|
||||||
|
/* Check the integrity of the Nix store. Returns true if errors
|
||||||
|
remain. */
|
||||||
|
virtual bool verifyStore(bool checkContents, bool repair) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ typedef enum {
|
|||||||
wopQueryValidPaths = 31,
|
wopQueryValidPaths = 31,
|
||||||
wopQuerySubstitutablePaths = 32,
|
wopQuerySubstitutablePaths = 32,
|
||||||
wopQueryValidDerivers = 33,
|
wopQueryValidDerivers = 33,
|
||||||
wopOptimiseStore = 34
|
wopOptimiseStore = 34,
|
||||||
|
wopVerifyStore = 35,
|
||||||
|
wopBuildDerivation = 36,
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+12
-25
@@ -39,8 +39,7 @@ PathFilter defaultPathFilter;
|
|||||||
static void dumpContents(const Path & path, size_t size,
|
static void dumpContents(const Path & path, size_t size,
|
||||||
Sink & sink)
|
Sink & sink)
|
||||||
{
|
{
|
||||||
writeString("contents", sink);
|
sink << "contents" << size;
|
||||||
writeLongLong(size, sink);
|
|
||||||
|
|
||||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
|
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
|
||||||
if (fd == -1) throw SysError(format("opening file ‘%1%’") % path);
|
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))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path ‘%1%’") % path);
|
throw SysError(format("getting attributes of path ‘%1%’") % path);
|
||||||
|
|
||||||
writeString("(", sink);
|
sink << "(";
|
||||||
|
|
||||||
if (S_ISREG(st.st_mode)) {
|
if (S_ISREG(st.st_mode)) {
|
||||||
writeString("type", sink);
|
sink << "type" << "regular";
|
||||||
writeString("regular", sink);
|
if (st.st_mode & S_IXUSR)
|
||||||
if (st.st_mode & S_IXUSR) {
|
sink << "executable" << "";
|
||||||
writeString("executable", sink);
|
|
||||||
writeString("", sink);
|
|
||||||
}
|
|
||||||
dumpContents(path, (size_t) st.st_size, sink);
|
dumpContents(path, (size_t) st.st_size, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (S_ISDIR(st.st_mode)) {
|
else if (S_ISDIR(st.st_mode)) {
|
||||||
writeString("type", sink);
|
sink << "type" << "directory";
|
||||||
writeString("directory", sink);
|
|
||||||
|
|
||||||
/* If we're on a case-insensitive system like Mac OS X, undo
|
/* If we're on a case-insensitive system like Mac OS X, undo
|
||||||
the case hack applied by restorePath(). */
|
the case hack applied by restorePath(). */
|
||||||
@@ -101,32 +96,24 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
|
|||||||
|
|
||||||
for (auto & i : unhacked)
|
for (auto & i : unhacked)
|
||||||
if (filter(path + "/" + i.first)) {
|
if (filter(path + "/" + i.first)) {
|
||||||
writeString("entry", sink);
|
sink << "entry" << "(" << "name" << i.first << "node";
|
||||||
writeString("(", sink);
|
|
||||||
writeString("name", sink);
|
|
||||||
writeString(i.first, sink);
|
|
||||||
writeString("node", sink);
|
|
||||||
dump(path + "/" + i.second, sink, filter);
|
dump(path + "/" + i.second, sink, filter);
|
||||||
writeString(")", sink);
|
sink << ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (S_ISLNK(st.st_mode)) {
|
else if (S_ISLNK(st.st_mode))
|
||||||
writeString("type", sink);
|
sink << "type" << "symlink" << "target" << readLink(path);
|
||||||
writeString("symlink", sink);
|
|
||||||
writeString("target", sink);
|
|
||||||
writeString(readLink(path), sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
else throw Error(format("file ‘%1%’ has an unsupported type") % path);
|
else throw Error(format("file ‘%1%’ has an unsupported type") % path);
|
||||||
|
|
||||||
writeString(")", sink);
|
sink << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
|
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
|
||||||
{
|
{
|
||||||
writeString(archiveVersion1, sink);
|
sink << archiveVersion1;
|
||||||
dump(path, sink, filter);
|
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