Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b4577ac9d | ||
|
|
d704abc0f3 | ||
|
|
71e5b937fb | ||
|
|
0824f40760 | ||
|
|
a1bf934044 | ||
|
|
a6d52fcd95 | ||
|
|
8d22b26448 | ||
|
|
786046cf13 | ||
|
|
8906eda2f9 | ||
|
|
00903fa799 | ||
|
|
0cad1f8049 | ||
|
|
1c57ab8b31 | ||
|
|
ef7c2d8b3e | ||
|
|
4202b17666 | ||
|
|
10a6aa3ad4 | ||
|
|
458711e4ee | ||
|
|
9aac1861f7 | ||
|
|
94595f42eb | ||
|
|
2ef7e2e795 | ||
|
|
02a66b3fd7 | ||
|
|
caaaff3954 | ||
|
|
d2c0958e1e | ||
|
|
4f3cf06c97 | ||
|
|
39d1da7b51 | ||
|
|
0eb200e569 | ||
|
|
80ebd60e7c | ||
|
|
9a4706eb19 | ||
|
|
14080f3e4b | ||
|
|
6298afc047 | ||
|
|
36b9ce24b1 | ||
|
|
607d91517e | ||
|
|
743e310046 | ||
|
|
29c0199d5c | ||
|
|
5eb93556f1 | ||
|
|
f770b9e6c5 | ||
|
|
71a93a5f0e | ||
|
|
9be037448c | ||
|
|
f476535265 | ||
|
|
77ad443bd1 | ||
|
|
b8258a4475 | ||
|
|
52120123a5 | ||
|
|
68fd01f42a | ||
|
|
f696af0fab | ||
|
|
a9faa7bbce | ||
|
|
338880ee6f | ||
|
|
b07b3b0264 | ||
|
|
32ea8a9898 | ||
|
|
399397c907 | ||
|
|
8f67325a7c | ||
|
|
7431932b29 | ||
|
|
65bd82d42a | ||
|
|
9018deab6c | ||
|
|
d1e3bf01bc | ||
|
|
8063fc497a | ||
|
|
69b9d8fdbd | ||
|
|
f327970129 | ||
|
|
9533532ce2 | ||
|
|
bfb6c4876a | ||
|
|
c0d4173263 | ||
|
|
efd6a8c9f6 | ||
|
|
0ab4d905e7 | ||
|
|
27d6ed5c68 | ||
|
|
a0f0733413 | ||
|
|
afef347157 | ||
|
|
4c1c7e5812 | ||
|
|
cad40adce5 | ||
|
|
cf141abfe9 | ||
|
|
6c10bd7c5e | ||
|
|
279fa8f618 | ||
|
|
e0bd114e09 | ||
|
|
4921223160 | ||
|
|
b9b7bb1806 | ||
|
|
5deb7fbdfb | ||
|
|
33f2fbcb62 | ||
|
|
6ad10591ce | ||
|
|
36f7fcc157 | ||
|
|
8a74a125bc | ||
|
|
1d3529e93a | ||
|
|
9b4cd20752 | ||
|
|
58d2fac91d | ||
|
|
4390142315 | ||
|
|
bd09a4c967 | ||
|
|
4876bb012e | ||
|
|
d760c2638c | ||
|
|
22dfd023fa | ||
|
|
8a7f0dfd68 | ||
|
|
b39622a487 | ||
|
|
9ee15abe30 | ||
|
|
8fdd156a65 | ||
|
|
96c2ebf004 | ||
|
|
4384bbd2e1 | ||
|
|
7759a56bed | ||
|
|
8a2a3b6579 | ||
|
|
8a76bc9fb0 | ||
|
|
cdb346c65e | ||
|
|
5bc12454fd | ||
|
|
6f1743b1a5 | ||
|
|
a6ca68a70c | ||
|
|
c10ef2d134 | ||
|
|
3b52e54e31 | ||
|
|
85aeedb9bc | ||
|
|
d6bd51ffbe | ||
|
|
9a963c8adc | ||
|
|
95c66cc950 | ||
|
|
b83fb35f79 | ||
|
|
1f735a3440 | ||
|
|
dae5dc7ade | ||
|
|
5c28943e8f | ||
|
|
71039becd1 | ||
|
|
f6a224bd68 | ||
|
|
45b2f32cc1 | ||
|
|
fef8c3a5ab | ||
|
|
9155fbd386 | ||
|
|
af1218a86a | ||
|
|
992cda1b11 | ||
|
|
76f3ba42fd | ||
|
|
2dc8e19a17 | ||
|
|
f5a7739171 | ||
|
|
3f65504164 | ||
|
|
ff6953cb03 | ||
|
|
15c7a79ed3 | ||
|
|
5bf4085000 | ||
|
|
6a8cee19b3 | ||
|
|
1fc0fe1baf | ||
|
|
d4cac051f7 | ||
|
|
4b8c71b4ef | ||
|
|
e770f941d6 | ||
|
|
6dbc9e02ec | ||
|
|
1ea2aa9e99 | ||
|
|
f5b32ff47d | ||
|
|
01615b5f63 | ||
|
|
5db358d4d7 | ||
|
|
357d31b339 | ||
|
|
058d1950c0 | ||
|
|
a7dd26961d | ||
|
|
164487a5ba | ||
|
|
5f1a3201ec | ||
|
|
133a421bb4 | ||
|
|
2075ec83e3 | ||
|
|
51cf4455b2 | ||
|
|
8a87521636 | ||
|
|
055ab1fc8d | ||
|
|
14f7e1fc42 | ||
|
|
21ecd106ed | ||
|
|
f08449ccbd | ||
|
|
e4abf8610b | ||
|
|
b54f447df9 | ||
|
|
7888b2ba15 | ||
|
|
12c1776df9 | ||
|
|
c999ef70e8 | ||
|
|
cbc177ca01 | ||
|
|
1abda8e173 | ||
|
|
9ffc4f4363 | ||
|
|
57db2aa220 | ||
|
|
06f29fafe8 | ||
|
|
a3c4eb0964 | ||
|
|
bec3c31608 | ||
|
|
bdc4a0b54d | ||
|
|
a49514a2e2 | ||
|
|
aaf8a1c16b | ||
|
|
fd74296e2f | ||
|
|
4ba6bc184c | ||
|
|
3d91bfc8f8 | ||
|
|
42808fa281 | ||
|
|
12a4aea93d | ||
|
|
64f9b511be | ||
|
|
e5c3e054ec | ||
|
|
55b1146150 | ||
|
|
71a5161365 | ||
|
|
6e06a18d1b | ||
|
|
73332fd342 | ||
|
|
d711fe8f5e | ||
|
|
ce97523a7c | ||
|
|
5e4b2109b5 | ||
|
|
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 |
@@ -13,6 +13,7 @@ makefiles = \
|
||||
src/nix-collect-garbage/local.mk \
|
||||
src/download-via-ssh/local.mk \
|
||||
src/nix-log2xml/local.mk \
|
||||
src/nix-prefetch-url/local.mk \
|
||||
src/bsdiff-4.3/local.mk \
|
||||
perl/local.mk \
|
||||
scripts/local.mk \
|
||||
|
||||
+2
-1
@@ -3,12 +3,13 @@ CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
HAVE_OPENSSL = @HAVE_OPENSSL@
|
||||
HAVE_SODIUM = @HAVE_SODIUM@
|
||||
LIBCURL_LIBS = @LIBCURL_LIBS@
|
||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
SODIUM_LIBS = @SODIUM_LIBS@
|
||||
SQLITE3_LIBS = @SQLITE3_LIBS@
|
||||
bash = @bash@
|
||||
bindir = @bindir@
|
||||
bsddiff_compat_include = @bsddiff_compat_include@
|
||||
|
||||
+24
-25
@@ -48,6 +48,7 @@ test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
|
||||
|
||||
|
||||
# Solaris-specific stuff.
|
||||
AC_STRUCT_DIRENT_D_TYPE
|
||||
if test "$sys_name" = sunos; then
|
||||
# Solaris requires -lsocket -lnsl for network functions
|
||||
LIBS="-lsocket -lnsl $LIBS"
|
||||
@@ -76,18 +77,7 @@ static char buf[1024];]],
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
# Check for chroot support (requires chroot() and bind mounts).
|
||||
AC_CHECK_FUNCS([chroot])
|
||||
AC_CHECK_FUNCS([unshare])
|
||||
AC_CHECK_FUNCS([statvfs])
|
||||
AC_CHECK_HEADERS([sched.h])
|
||||
AC_CHECK_HEADERS([sys/param.h])
|
||||
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
[#ifdef HAVE_SYS_PARAM_H
|
||||
# include <sys/param.h>
|
||||
# endif
|
||||
])
|
||||
AC_CHECK_HEADERS([sys/syscall.h])
|
||||
|
||||
|
||||
# Check for lutimes, optionally used for changing the mtime of
|
||||
@@ -95,10 +85,6 @@ AC_CHECK_HEADERS([sys/syscall.h])
|
||||
AC_CHECK_FUNCS([lutimes])
|
||||
|
||||
|
||||
# Check for sched_setaffinity.
|
||||
AC_CHECK_FUNCS([sched_setaffinity])
|
||||
|
||||
|
||||
# Check whether the store optimiser can optimise symlinks.
|
||||
AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
|
||||
ln -s bla tmp_link
|
||||
@@ -122,10 +108,6 @@ AC_CHECK_HEADER([err.h], [], [bsddiff_compat_include="-Icompat-include"])
|
||||
AC_SUBST([bsddiff_compat_include])
|
||||
|
||||
|
||||
# Check for <linux/fs.h> (for immutable file support).
|
||||
AC_CHECK_HEADERS([linux/fs.h])
|
||||
|
||||
|
||||
AC_DEFUN([NEED_PROG],
|
||||
[
|
||||
AC_PATH_PROG($1, $2)
|
||||
@@ -183,16 +165,12 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
|
||||
AC_SUBST(storedir)
|
||||
|
||||
|
||||
# Look for OpenSSL, an optional dependency.
|
||||
# Look for OpenSSL, a required dependency.
|
||||
AC_PATH_PROG(openssl, openssl, openssl) # if not found, call openssl in $PATH
|
||||
AC_SUBST(openssl)
|
||||
AC_DEFINE_UNQUOTED(OPENSSL_PATH, ["$openssl"], [Path of the OpenSSL binary])
|
||||
|
||||
PKG_CHECK_MODULES([OPENSSL], [libcrypto],
|
||||
[AC_DEFINE([HAVE_OPENSSL], [1], [Whether to use OpenSSL.])
|
||||
CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"
|
||||
have_openssl=1], [have_openssl=])
|
||||
AC_SUBST(HAVE_OPENSSL, [$have_openssl])
|
||||
PKG_CHECK_MODULES([OPENSSL], [libcrypto], [CXXFLAGS="$OPENSSL_CFLAGS $CXXFLAGS"])
|
||||
|
||||
|
||||
# Look for libbz2, a required dependency.
|
||||
@@ -218,6 +196,10 @@ PKG_CHECK_MODULES([SODIUM], [libsodium],
|
||||
AC_SUBST(HAVE_SODIUM, [$have_sodium])
|
||||
|
||||
|
||||
# Look for liblzma, a required dependency.
|
||||
PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"])
|
||||
|
||||
|
||||
# Whether to use the Boehm garbage collector.
|
||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=no]]),
|
||||
@@ -261,6 +243,23 @@ AC_MSG_RESULT(yes)
|
||||
AC_SUBST(perlFlags)
|
||||
|
||||
|
||||
# Check for otool, an optional dependency on Darwin.
|
||||
AC_PATH_PROG(otool, otool)
|
||||
AC_MSG_CHECKING([that otool works])
|
||||
case $host_os in
|
||||
darwin*)
|
||||
if test -z "$otool" || ! $otool --version 2>/dev/null; then
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_ERROR([Can't get version from otool; do you need to install developer tools?])
|
||||
fi
|
||||
AC_MSG_RESULT(yes)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT(not needed)
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# Whether to build the Perl bindings
|
||||
AC_MSG_CHECKING([whether to build the Perl bindings])
|
||||
AC_ARG_ENABLE(perl-bindings, AC_HELP_STRING([--enable-perl-bindings],
|
||||
|
||||
+19
-1
@@ -8,7 +8,7 @@ derivation {
|
||||
builder = perl;
|
||||
args = [ "-w" ./buildenv.pl ];
|
||||
|
||||
manifest = manifest;
|
||||
inherit manifest;
|
||||
|
||||
# !!! grmbl, need structured data for passing this in a clean way.
|
||||
derivations =
|
||||
@@ -23,5 +23,23 @@ derivation {
|
||||
# network traffic, so don't do that.
|
||||
preferLocalBuild = true;
|
||||
|
||||
# Also don't bother substituting.
|
||||
allowSubstitutes = false;
|
||||
|
||||
__sandboxProfile = ''
|
||||
(allow sysctl-read)
|
||||
(allow file-read*
|
||||
(literal "/usr/lib/libSystem.dylib")
|
||||
(literal "/usr/lib/libSystem.B.dylib")
|
||||
(literal "/usr/lib/libobjc.A.dylib")
|
||||
(literal "/usr/lib/libobjc.dylib")
|
||||
(literal "/usr/lib/libauto.dylib")
|
||||
(literal "/usr/lib/libc++abi.dylib")
|
||||
(literal "/usr/lib/libc++.1.dylib")
|
||||
(literal "/usr/lib/libDiagnosticMessagesClient.dylib")
|
||||
(subpath "/usr/lib/system")
|
||||
(subpath "/dev"))
|
||||
'';
|
||||
|
||||
inherit chrootDeps;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ foreach my $pkg (@pkgs) {
|
||||
|
||||
|
||||
# Symlink to the packages that have been "propagated" by packages
|
||||
# installed by the user (i.e., package X declares that it want Y
|
||||
# installed by the user (i.e., package X declares that it wants Y
|
||||
# installed as well). We do these later because they have a lower
|
||||
# priority in case of collisions.
|
||||
my $priorityCounter = 1000; # don't care about collisions
|
||||
|
||||
@@ -12,7 +12,6 @@ in rec {
|
||||
tar = "@tar@";
|
||||
tarFlags = "@tarFlags@";
|
||||
tr = "@tr@";
|
||||
curl = "@curl@";
|
||||
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
|
||||
nixPrefix = "@prefix@";
|
||||
|
||||
|
||||
+12
-18
@@ -1,33 +1,29 @@
|
||||
with import <nix/config.nix>;
|
||||
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? "", executable ? false}:
|
||||
{ system ? builtins.currentSystem
|
||||
, url
|
||||
, outputHash ? ""
|
||||
, outputHashAlgo ? ""
|
||||
, md5 ? "", sha1 ? "", sha256 ? ""
|
||||
, executable ? false
|
||||
, unpack ? false
|
||||
, name ? baseNameOf (toString url)
|
||||
}:
|
||||
|
||||
assert (outputHash != "" && outputHashAlgo != "")
|
||||
|| md5 != "" || sha1 != "" || sha256 != "";
|
||||
|
||||
let
|
||||
|
||||
builder = builtins.toFile "fetchurl.sh"
|
||||
(''
|
||||
echo "downloading $url into $out"
|
||||
${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out"
|
||||
'' + (if executable then "${coreutils}/chmod +x $out" else ""));
|
||||
|
||||
in
|
||||
|
||||
derivation {
|
||||
name = baseNameOf (toString url);
|
||||
builder = shell;
|
||||
args = [ "-e" builder ];
|
||||
builder = "builtin:fetchurl";
|
||||
|
||||
# New-style output content requirements.
|
||||
outputHashAlgo = if outputHashAlgo != "" then outputHashAlgo else
|
||||
if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5";
|
||||
outputHash = if outputHash != "" then outputHash else
|
||||
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
|
||||
outputHashMode = if executable then "recursive" else "flat";
|
||||
outputHashMode = if unpack || executable then "recursive" else "flat";
|
||||
|
||||
inherit system url;
|
||||
inherit name system url executable unpack;
|
||||
|
||||
# No need to double the amount of network traffic
|
||||
preferLocalBuild = true;
|
||||
@@ -39,6 +35,4 @@ derivation {
|
||||
# by definition pure.
|
||||
"http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
|
||||
];
|
||||
|
||||
inherit chrootDeps;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
attrs @ { drvPath, outputs, ... }:
|
||||
attrs @ { drvPath, outputs, name, ... }:
|
||||
|
||||
let
|
||||
|
||||
commonAttrs = (builtins.listToAttrs outputsList) //
|
||||
{ all = map (x: x.value) outputsList;
|
||||
inherit drvPath;
|
||||
inherit drvPath name;
|
||||
type = "derivation";
|
||||
};
|
||||
|
||||
|
||||
@@ -224,16 +224,16 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-use-chroot</literal></term>
|
||||
<varlistentry><term><literal>build-use-sandbox</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, builds will be
|
||||
performed in a <emphasis>chroot environment</emphasis>, i.e.,
|
||||
performed in a <emphasis>sandboxed environment</emphasis>, i.e.,
|
||||
they’re isolated from the normal file system hierarchy and will
|
||||
only see their dependencies in the Nix store, the temporary build
|
||||
directory, private versions of <filename>/proc</filename>,
|
||||
<filename>/dev</filename>, <filename>/dev/shm</filename> and
|
||||
<filename>/dev/pts</filename>, and the paths configured with the
|
||||
<link linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
|
||||
<filename>/dev/pts</filename> (on Linux), and the paths configured with the
|
||||
<link linkend='conf-build-sandbox-paths'><literal>build-sandbox-paths</literal>
|
||||
option</link>. This is useful to prevent undeclared dependencies
|
||||
on files in directories such as <filename>/usr/bin</filename>. In
|
||||
addition, on Linux, builds run in private PID, mount, network, IPC
|
||||
@@ -241,8 +241,8 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
system (except that fixed-output derivations do not run in private
|
||||
network namespace to ensure they can access the network).</para>
|
||||
|
||||
<para>Currently, chroots only work on Linux and Mac OS X. The use
|
||||
of a chroot requires that Nix is run as root (so you should use
|
||||
<para>Currently, sandboxing only work on Linux and Mac OS X. The use
|
||||
of a sandbox requires that Nix is run as root (so you should use
|
||||
the <link linkend='conf-build-users-group'>“build users”
|
||||
feature</link> to perform the actual builds under different users
|
||||
than root).</para>
|
||||
@@ -250,7 +250,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</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>
|
||||
<literal>true</literal> do not run in sandboxes.</para>
|
||||
|
||||
<para>The default is <literal>false</literal>.</para>
|
||||
|
||||
@@ -259,17 +259,16 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
<varlistentry xml:id="conf-build-sandbox-paths">
|
||||
<term><literal>build-sandbox-paths</literal></term>
|
||||
|
||||
<listitem><para>A list of paths bind-mounted into Nix chroot
|
||||
environments. Contrary to what the name suggests, the specified
|
||||
paths do not have to be directories; you can bind-mount other
|
||||
types of files as well. You can use the syntax
|
||||
<listitem><para>A list of paths bind-mounted into Nix sandbox
|
||||
environments. You can use the syntax
|
||||
<literal><replaceable>target</replaceable>=<replaceable>source</replaceable></literal>
|
||||
to mount a path in a different location in the chroot; for
|
||||
to mount a path in a different location in the sandbox; for
|
||||
instance, <literal>/bin=/nix-bin</literal> will mount the path
|
||||
<literal>/nix-bin</literal> as <literal>/bin</literal> inside the
|
||||
chroot.</para>
|
||||
sandbox.</para>
|
||||
|
||||
<para>Depending on how Nix was built, the default value for this option
|
||||
may be empty or provide <filename>/bin/sh</filename> as a
|
||||
@@ -278,10 +277,11 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-extra-chroot-dirs"><term><literal>build-extra-chroot-dirs</literal></term>
|
||||
<varlistentry xml:id="conf-build-extra-sandbox-paths">
|
||||
<term><literal>build-extra-sandbox-paths</literal></term>
|
||||
|
||||
<listitem><para>A list of additional paths appended to
|
||||
<option>build-chroot-dirs</option>. Useful if you want to extend
|
||||
<option>build-sandbox-paths</option>. Useful if you want to extend
|
||||
its default value.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -426,7 +426,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
<listitem><para>The maximum number of parallel HTTP connections
|
||||
used by the binary cache substituter to get NAR info files. This
|
||||
number should be high to minimise latency. It defaults to
|
||||
150.</para></listitem>
|
||||
25.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -593,19 +593,21 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
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
|
||||
<para>The hook is passed the derivation path and, if sandboxes are enabled,
|
||||
the sandbox directory. It can then modify the sandbox 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>
|
||||
<varlistentry xml:id="extra-sandbox-paths">
|
||||
<term><literal>extra-sandbox-paths</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>
|
||||
sandbox for this build. One entry per line, terminated by an empty
|
||||
line. Entries have the same format as
|
||||
<literal>build-sandbox-paths</literal>.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -616,6 +618,18 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-repeat"><term><literal>build-repeat</literal></term>
|
||||
|
||||
<listitem><para>How many times to repeat builds to check whether
|
||||
they are deterministic. The default value is 0. If the value is
|
||||
non-zero, every build is repeated the specified number of
|
||||
times. If the contents of any of the runs differs from the
|
||||
previous ones, the build is rejected and the resulting store paths
|
||||
are not registered as “valid” in Nix’s database.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<arg choice='plain'><option>--print-dead</option></arg>
|
||||
<arg choice='plain'><option>--delete</option></arg>
|
||||
</group>
|
||||
<arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
|
||||
<arg><option>--dry-run</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<arg><option>--include-outputs</option></arg>
|
||||
<arg><option>--use-substitutes</option></arg>
|
||||
<arg><option>-s</option></arg>
|
||||
<arg><option>-v</option></arg>
|
||||
<arg choice='plain'>
|
||||
<replaceable>user@</replaceable><replaceable>machine</replaceable>
|
||||
</arg>
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
<para><command>nix-copy-closure</command> gives you an easy and
|
||||
efficient way to exchange software between machines. Given one or
|
||||
more Nix store paths <replaceable>paths</replaceable> on the local
|
||||
more Nix store <replaceable>paths</replaceable> on the local
|
||||
machine, <command>nix-copy-closure</command> computes the closure of
|
||||
those paths (i.e. all their dependencies in the Nix store), and copies
|
||||
all paths in the closure to the remote machine via the
|
||||
@@ -138,6 +139,12 @@ those paths. If this bothers you, use
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>-v</option></term>
|
||||
|
||||
<listitem><para>Show verbose output.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -378,7 +378,7 @@ number of possible ways:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
|
||||
<varlistentry><term><option>--prebuilt-only</option> / <option>-b</option></term>
|
||||
|
||||
<listitem><para>Use only derivations for which a substitute is
|
||||
registered, i.e., there is a pre-built binary available that can
|
||||
@@ -1012,7 +1012,7 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
|
||||
<varlistentry><term><option>--prebuilt-only</option> / <option>-b</option></term>
|
||||
|
||||
<listitem><para>Show only derivations for which a substitute is
|
||||
registered, i.e., there is a pre-built binary available that can
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
version="5.0"
|
||||
xml:id="sec-nix-prefetch-url">
|
||||
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-prefetch-url</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
@@ -20,6 +20,7 @@
|
||||
<cmdsynopsis>
|
||||
<command>nix-prefetch-url</command>
|
||||
<arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
|
||||
<arg><option>--print-path</option></arg>
|
||||
<arg choice='plain'><replaceable>url</replaceable></arg>
|
||||
<arg><replaceable>hash</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
@@ -54,8 +55,8 @@ error if signaled if the actual hash of the file does not match the
|
||||
specified hash.</para>
|
||||
|
||||
<para>This command prints the hash on standard output. Additionally,
|
||||
if the environment variable <envar>PRINT_PATH</envar> is set, the path
|
||||
of the downloaded file in the Nix store is also printed.</para>
|
||||
if the option <option>--print-path</option> is used, the path of the
|
||||
downloaded file in the Nix store is also printed.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -63,7 +64,7 @@ of the downloaded file in the Nix store is also printed.</para>
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
|
||||
|
||||
<listitem><para>Use the specified cryptographic hash algorithm,
|
||||
@@ -73,6 +74,35 @@ of the downloaded file in the Nix store is also printed.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--print-path</option></term>
|
||||
|
||||
<listitem><para>Print the store path of the downloaded file on
|
||||
standard output.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--unpack</option></term>
|
||||
|
||||
<listitem><para>Unpack the archive (which must be a tarball or zip
|
||||
file) and add the result to the Nix store. The resulting hash can
|
||||
be used with functions such as Nixpkgs’s
|
||||
<varname>fetchzip</varname> or
|
||||
<varname>fetchFromGitHub</varname>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--name</option></term>
|
||||
|
||||
<listitem><para>Override the name of the file in the Nix store. By
|
||||
default, this is
|
||||
<literal><replaceable>hash</replaceable>-<replaceable>basename</replaceable></literal>,
|
||||
where <replaceable>basename</replaceable> is the last component of
|
||||
<replaceable>url</replaceable>. Overriding the name is necessary
|
||||
when <replaceable>basename</replaceable> contains characters that
|
||||
are not allowed in Nix store paths.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
@@ -81,14 +111,19 @@ of the downloaded file in the Nix store is also printed.</para>
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<screen>
|
||||
$ nix-prefetch-url ftp://ftp.nluug.nl/pub/gnu/make/make-3.80.tar.bz2
|
||||
0bbd1df101bc0294d440471e50feca71
|
||||
$ nix-prefetch-url ftp://ftp.gnu.org/pub/gnu/hello/hello-2.10.tar.gz
|
||||
0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
|
||||
|
||||
$ PRINT_PATH=1 nix-prefetch-url ftp://ftp.nluug.nl/pub/gnu/make/make-3.80.tar.bz2
|
||||
0bbd1df101bc0294d440471e50feca71
|
||||
/nix/store/wvyz8ifdn7wyz1p3pqyn0ra45ka2l492-make-3.80.tar.bz2</screen>
|
||||
$ nix-prefetch-url --print-path mirror://gnu/hello/hello-2.10.tar.gz
|
||||
0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
|
||||
/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz
|
||||
|
||||
$ nix-prefetch-url --unpack --print-path https://github.com/NixOS/patchelf/archive/0.8.tar.gz
|
||||
079agjlv0hrv7fxnx9ngipx14gyncbkllxrp9cccnh3a50fxcmy7
|
||||
/nix/store/19zrmhm3m40xxaw81c8cqm6aljgrnwj2-0.8.tar.gz
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -176,6 +176,22 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Environment variables</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><envar>NIX_BUILD_SHELL</envar></term>
|
||||
|
||||
<listitem><para>Shell used to start the interactive environment.
|
||||
Defaults to the <command>bash</command> found in <envar>PATH</envar>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To build the dependencies of the package Pan, and start an
|
||||
@@ -252,8 +268,8 @@ 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>
|
||||
many operating systems only allow one argument in
|
||||
<literal>#!</literal> lines.</para>
|
||||
|
||||
<para>For example, here is a Python script that depends on Python and
|
||||
the <literal>prettytable</literal> package:
|
||||
|
||||
@@ -194,6 +194,25 @@ printed.)</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--check</option></term>
|
||||
|
||||
<listitem><para>This option allows you to check whether a
|
||||
derivation is deterministic. It rebuilds the specified derivation
|
||||
and checks whether the result is bitwise-identical with the
|
||||
existing outputs, printing an error if that’s not the case. The
|
||||
outputs of the specified derivation must already exist. When used
|
||||
with <option>-K</option>, if an output path is not identical to
|
||||
the corresponding output from the previous build, the new output
|
||||
path is left in
|
||||
<filename>/nix/store/<replaceable>name</replaceable>-check.</filename></para>
|
||||
|
||||
<para>See also the <option>build-repeat</option> configuration
|
||||
option, which repeats a derivation a number of times and prevents
|
||||
its outputs from being registered as “valid” in the Nix store
|
||||
unless they are identical.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
@@ -212,6 +231,14 @@ $ nix-store -r $(nix-instantiate ./test.nix)
|
||||
This is essentially what <link
|
||||
linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
|
||||
|
||||
<para>To test whether a previously-built derivation is deterministic:
|
||||
|
||||
<screen>
|
||||
$ nix-build -r '<nixpkgs>' -A hello --check -K
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,28 @@ available as <function>builtins.derivation</function>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.all</function>
|
||||
<replaceable>pred</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if the function
|
||||
<replaceable>pred</replaceable> returns <literal>true</literal>
|
||||
for all elements of <replaceable>list</replaceable>,
|
||||
and <literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.any</function>
|
||||
<replaceable>pred</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if the function
|
||||
<replaceable>pred</replaceable> returns <literal>true</literal>
|
||||
for at least one element of <replaceable>list</replaceable>,
|
||||
and <literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.attrNames</function>
|
||||
<replaceable>set</replaceable></term>
|
||||
|
||||
@@ -313,6 +335,37 @@ stdenv.mkDerivation {
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.foldl’</function>
|
||||
<replaceable>op</replaceable> <replaceable>nul</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Reduce a list by applying a binary operator, from
|
||||
left to right, e.g. <literal>foldl’ op nul [x0 x1 x2 ...] = op (op
|
||||
(op nul x0) x1) x2) ...</literal>. The operator is applied
|
||||
strictly, i.e., its arguments are evaluated first. For example,
|
||||
<literal>foldl’ (x: y: x + y) 0 [1 2 3]</literal> evaluates to
|
||||
6.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.functionArgs</function>
|
||||
<replaceable>f</replaceable></term>
|
||||
|
||||
<listitem><para>
|
||||
Return a set containing the names of the formal arguments expected
|
||||
by the function <replaceable>f</replaceable>.
|
||||
The value of each attribute is a Boolean denoting whether the corresponding
|
||||
argument has a default value. For instance,
|
||||
<literal>functionArgs ({ x, y ? 123}: ...) = { x = false; y = true; }</literal>.
|
||||
</para>
|
||||
|
||||
<para>"Formal argument" here refers to the attributes pattern-matched by
|
||||
the function. Plain lambdas are not included, e.g.
|
||||
<literal>functionArgs (x: ...) = { }</literal>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.fromJSON</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Convert a JSON string to a Nix
|
||||
@@ -329,6 +382,24 @@ builtins.fromJSON ''{"x": [1, 2, 3], "y": null}''
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.genList</function>
|
||||
<replaceable>generator</replaceable> <replaceable>length</replaceable></term>
|
||||
|
||||
<listitem><para>Generate list of size
|
||||
<replaceable>length</replaceable>, with each element
|
||||
<replaceable>i></replaceable> equal to the value returned by
|
||||
<replaceable>generator</replaceable> <literal>i</literal>. For
|
||||
example,
|
||||
|
||||
<programlisting>
|
||||
builtins.genList (x: x * x) 5
|
||||
</programlisting>
|
||||
|
||||
returns the list <literal>[ 0 1 4 9 16 ]</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>set</replaceable></term>
|
||||
|
||||
@@ -663,7 +734,7 @@ in config.someSetting</programlisting>
|
||||
./A</literal> will return the set
|
||||
|
||||
<programlisting>
|
||||
{ A = "regular"; B = "directory"; }</programlisting>
|
||||
{ B = "regular"; C = "directory"; }</programlisting>
|
||||
|
||||
The possible values for the file type are
|
||||
<literal>"regular"</literal>, <literal>"directory"</literal>,
|
||||
@@ -698,6 +769,23 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.replaceStrings</function>
|
||||
<replaceable>from</replaceable> <replaceable>to</replaceable> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Given string <replaceable>s</replaceable>, replace
|
||||
every occurrence of the strings in <replaceable>from</replaceable>
|
||||
with the corresponding string in
|
||||
<replaceable>to</replaceable>. For example,
|
||||
|
||||
<programlisting>
|
||||
builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar"
|
||||
</programlisting>
|
||||
|
||||
evaluates to <literal>"fabir"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.seq</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -709,6 +797,29 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.sort</function>
|
||||
<replaceable>comparator</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Return <replaceable>list</replaceable> in sorted
|
||||
order. It repeatedly calls the function
|
||||
<replaceable>comparator</replaceable> with two elements. The
|
||||
comparator should return <literal>true</literal> if the first
|
||||
element is less than the second, and <literal>false</literal>
|
||||
otherwise. For example,
|
||||
|
||||
<programlisting>
|
||||
builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
|
||||
</programlisting>
|
||||
|
||||
produces the list <literal>[ 42 77 147 249 483 526
|
||||
]</literal>.</para>
|
||||
|
||||
<para>This is a stable sort: it preserves the relative order of
|
||||
elements deemed equal by the comparator.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.stringLength</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ $ chown alice /nix
|
||||
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<para>You can also manually download and install a binary package.
|
||||
Binary packages of the latest stable release are available for Fedora,
|
||||
Debian, Ubuntu, Mac OS X and various other systems from the <link
|
||||
@@ -53,12 +54,12 @@ $ dpkg -i nix_1.8-1_amd64.deb</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>For other platforms, including Mac OS X and other Linux
|
||||
distributions, you can download a binary tarball that contains Nix and
|
||||
all its dependencies. (This is what the install script at
|
||||
<uri>https://nixos.org/nix/install</uri> uses.) You should unpack it
|
||||
somewhere (e.g. in <filename>/tmp</filename>), and then run the script
|
||||
named <command>install</command> inside the binary tarball:
|
||||
<para>You can also download a binary tarball that contains Nix and all
|
||||
its dependencies. (This is what the install script at
|
||||
<uri>https://nixos.org/nix/install</uri> does automatically.) You
|
||||
should unpack it somewhere (e.g. in <filename>/tmp</filename>), and
|
||||
then run the script named <command>install</command> inside the binary
|
||||
tarball:
|
||||
|
||||
<screen>
|
||||
alice$ cd /tmp
|
||||
@@ -77,6 +78,15 @@ other auxiliary data, if desired:
|
||||
<screen>
|
||||
$ rm -rf /nix</screen>
|
||||
|
||||
</para>
|
||||
-->
|
||||
|
||||
<para>You can uninstall Nix simply by running:
|
||||
|
||||
<screen>
|
||||
$ rm -rf /nix
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</chapter>
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
or higher. If your distribution does not provide it, please install
|
||||
it from <link xlink:href="http://www.sqlite.org/" />.</para></listitem>
|
||||
|
||||
<listitem><para>The Perl DBI and DBD::SQLite libraries, which are
|
||||
<listitem><para>The Perl DBI, DBD::SQLite, and WWW::Curl libraries, which are
|
||||
available from <link
|
||||
xlink:href="http://search.cpan.org/">CPAN</link> if your
|
||||
distribution does not provide them.</para></listitem>
|
||||
|
||||
<listitem><para>The <link
|
||||
xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm
|
||||
xlink:href="http://www.hboehm.info/gc/">Boehm
|
||||
garbage collector</link> to reduce the evaluator’s memory
|
||||
consumption (optional). To enable it, install
|
||||
<literal>pkgconfig</literal> and the Boehm garbage collector, and
|
||||
@@ -70,4 +70,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
</partintro>
|
||||
-->
|
||||
|
||||
<xi:include href="rl-1.11.xml" />
|
||||
<xi:include href="rl-1.10.xml" />
|
||||
<xi:include href="rl-1.9.xml" />
|
||||
<xi:include href="rl-1.8.xml" />
|
||||
<xi:include href="rl-1.7.xml" />
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.10.1">
|
||||
|
||||
<title>Release 0.10.1 (October 11, 2006)</title>
|
||||
<title>Release 0.10.1 (2006-10-11)</title>
|
||||
|
||||
<para>This release fixes two somewhat obscure bugs that occur when
|
||||
evaluating Nix expressions that are stored inside the Nix store
|
||||
(<literal>NIX-67</literal>). These do not affect most users.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.10">
|
||||
|
||||
<title>Release 0.10 (October 6, 2006)</title>
|
||||
<title>Release 0.10 (2006-10-06)</title>
|
||||
|
||||
<note><para>This version of Nix uses Berkeley DB 4.4 instead of 4.3.
|
||||
The database is upgraded automatically, but you should be careful not
|
||||
@@ -320,4 +320,4 @@ irreversible.</para></warning>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-0.11">
|
||||
|
||||
<title>Release 0.11 (December 31, 2007)</title>
|
||||
<title>Release 0.11 (2007-12-31)</title>
|
||||
|
||||
<para>Nix 0.11 has many improvements over the previous stable release.
|
||||
The most important improvement is secure multi-user support. It also
|
||||
@@ -258,4 +258,4 @@ on Nix. Here is an (incomplete) list:</para>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-0.12">
|
||||
|
||||
<title>Release 0.12 (November 20, 2008)</title>
|
||||
<title>Release 0.12 (2008-11-20)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
@@ -172,4 +172,4 @@ the following paths will be downloaded/copied (30.02 MiB):
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-0.13">
|
||||
|
||||
<title>Release 0.13 (November 5, 2009)</title>
|
||||
<title>Release 0.13 (2009-11-05)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It has some new
|
||||
features:</para>
|
||||
@@ -103,4 +103,4 @@ features:</para>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-0.14"><title>Release 0.14 (February 4, 2010)</title>
|
||||
xml:id="ssec-relnotes-0.14">
|
||||
|
||||
<title>Release 0.14 (2010-02-04)</title>
|
||||
|
||||
<para>This release has the following improvements:</para>
|
||||
|
||||
@@ -41,4 +43,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-0.15">
|
||||
|
||||
<title>Release 0.15 (March 17, 2010)</title>
|
||||
<title>Release 0.15 (2010-03-17)</title>
|
||||
|
||||
<para>This is a bug-fix release. Among other things, it fixes
|
||||
building on Mac OS X (Snow Leopard), and improves the contents of
|
||||
<filename>/etc/passwd</filename> and <filename>/etc/group</filename>
|
||||
in <literal>chroot</literal> builds.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-0.16">
|
||||
|
||||
<title>Release 0.16 (August 17, 2010)</title>
|
||||
<title>Release 0.16 (2010-08-17)</title>
|
||||
|
||||
<para>This release has the following improvements:</para>
|
||||
|
||||
@@ -52,4 +52,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.6">
|
||||
|
||||
<title>Release 0.6 (November 14, 2004)</title>
|
||||
<title>Release 0.6 (2004-11-14)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
@@ -119,4 +119,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.7">
|
||||
|
||||
<title>Release 0.7 (January 12, 2005)</title>
|
||||
<title>Release 0.7 (2005-01-12)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
@@ -32,4 +32,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.8.1">
|
||||
|
||||
<title>Release 0.8.1 (April 13, 2005)</title>
|
||||
<title>Release 0.8.1 (2005-04-13)</title>
|
||||
|
||||
<para>This is a bug fix release.</para>
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.8">
|
||||
|
||||
<title>Release 0.8 (April 11, 2005)</title>
|
||||
<title>Release 0.8 (2005-04-11)</title>
|
||||
|
||||
<para>NOTE: the hashing scheme in Nix 0.8 changed (as detailed below).
|
||||
As a result, <command>nix-pull</command> manifests and channels built
|
||||
@@ -243,4 +243,4 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.9.1">
|
||||
|
||||
<title>Release 0.9.1 (September 20, 2005)</title>
|
||||
<title>Release 0.9.1 (2005-09-20)</title>
|
||||
|
||||
<para>This bug fix release addresses a problem with the ATerm library
|
||||
when the <option>--with-aterm</option> flag in
|
||||
<command>configure</command> was <emphasis>not</emphasis> used.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.9.2">
|
||||
|
||||
<title>Release 0.9.2 (September 21, 2005)</title>
|
||||
<title>Release 0.9.2 (2005-09-21)</title>
|
||||
|
||||
<para>This bug fix release fixes two problems on Mac OS X:
|
||||
|
||||
@@ -25,4 +25,4 @@
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ch-relnotes-0.9">
|
||||
|
||||
<title>Release 0.9 (September 16, 2005)</title>
|
||||
<title>Release 0.9 (2005-09-16)</title>
|
||||
|
||||
<para>NOTE: this version of Nix uses Berkeley DB 4.3 instead of 4.2.
|
||||
The database is upgraded automatically, but you should be careful not
|
||||
@@ -95,4 +95,4 @@ svnService = derivation {
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.0">
|
||||
|
||||
<title>Release 1.0 (May 11, 2012)</title>
|
||||
<title>Release 1.0 (2012-05-11)</title>
|
||||
|
||||
<para>There have been numerous improvements and bug fixes since the
|
||||
previous release. Here are the most significant:</para>
|
||||
@@ -116,4 +116,4 @@ previous release. Here are the most significant:</para>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.1">
|
||||
|
||||
<title>Release 1.1 (July 18, 2012)</title>
|
||||
<title>Release 1.1 (2012-07-18)</title>
|
||||
|
||||
<para>This release has the following improvements:</para>
|
||||
|
||||
@@ -97,4 +97,4 @@
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -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 (2015-09-03)</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>
|
||||
@@ -0,0 +1,141 @@
|
||||
<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.11">
|
||||
|
||||
<title>Release 1.11 (2016-01-19)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It also has a number of new
|
||||
features:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-prefetch-url</command> can now download URLs
|
||||
specified in a Nix expression. For example,
|
||||
|
||||
<screen>
|
||||
$ nix-prefetch-url -A hello.src
|
||||
</screen>
|
||||
|
||||
will prefetch the file specified by the
|
||||
<function>fetchurl</function> call in the attribute
|
||||
<literal>hello.src</literal> from the Nix expression in the
|
||||
current directory, and print the cryptographic hash of the
|
||||
resulting file on stdout. This differs from <literal>nix-build -A
|
||||
hello.src</literal> in that it doesn't verify the hash, and is
|
||||
thus useful when you’re updating a Nix expression.</para>
|
||||
|
||||
<para>You can also prefetch the result of functions that unpack a
|
||||
tarball, such as <function>fetchFromGitHub</function>. For example:
|
||||
|
||||
<screen>
|
||||
$ nix-prefetch-url --unpack https://github.com/NixOS/patchelf/archive/0.8.tar.gz
|
||||
</screen>
|
||||
|
||||
or from a Nix expression:
|
||||
|
||||
<screen>
|
||||
$ nix-prefetch-url -A nix-repl.src
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The builtin function
|
||||
<function><nix/fetchurl.nix></function> now supports
|
||||
downloading and unpacking NARs. This removes the need to have
|
||||
multiple downloads in the Nixpkgs stdenv bootstrap process (like a
|
||||
separate busybox binary for Linux, or curl/mkdir/sh/bzip2 for
|
||||
Darwin). Now all those files can be combined into a single NAR,
|
||||
optionally compressed using <command>xz</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now supports SHA-512 hashes for verifying fixed-output
|
||||
derivations, and in <function>builtins.hashString</function>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The new flag <option>--option build-repeat
|
||||
<replaceable>N</replaceable></option> will cause every build to
|
||||
be executed <replaceable>N</replaceable>+1 times. If the build
|
||||
output differs between any round, the build is rejected, and the
|
||||
output paths are not registered as valid. This is primarily
|
||||
useful to verify build determinism. (We already had a
|
||||
<option>--check</option> option to repeat a previously succeeded
|
||||
build. However, with <option>--check</option>, non-deterministic
|
||||
builds are registered in the DB. Preventing that is useful for
|
||||
Hydra to ensure that non-deterministic builds don't end up
|
||||
getting published to the binary cache.)
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The options <option>--check</option> and <option>--option
|
||||
build-repeat <replaceable>N</replaceable></option>, if they
|
||||
detect a difference between two runs of the same derivation and
|
||||
<option>-K</option> is given, will make the output of the other
|
||||
run available under
|
||||
<filename><replaceable>store-path</replaceable>-check</filename>. This
|
||||
makes it easier to investigate the non-determinism using tools
|
||||
like <command>diffoscope</command>, e.g.,
|
||||
|
||||
<screen>
|
||||
$ nix-build pkgs/stdenv/linux -A stage1.pkgs.zlib --check -K
|
||||
error: derivation ‘/nix/store/l54i8wlw2265…-zlib-1.2.8.drv’ may not
|
||||
be deterministic: output ‘/nix/store/11a27shh6n2i…-zlib-1.2.8’
|
||||
differs from ‘/nix/store/11a27shh6n2i…-zlib-1.2.8-check’
|
||||
|
||||
$ diffoscope /nix/store/11a27shh6n2i…-zlib-1.2.8 /nix/store/11a27shh6n2i…-zlib-1.2.8-check
|
||||
…
|
||||
├── lib/libz.a
|
||||
│ ├── metadata
|
||||
│ │ @@ -1,15 +1,15 @@
|
||||
│ │ -rw-r--r-- 30001/30000 3096 Jan 12 15:20 2016 adler32.o
|
||||
…
|
||||
│ │ +rw-r--r-- 30001/30000 3096 Jan 12 15:28 2016 adler32.o
|
||||
…
|
||||
</screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Improved FreeBSD support.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-env -qa --xml --meta</command> now prints
|
||||
license information.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The maximum number of parallel TCP connections that the
|
||||
binary cache substituter will use has been decreased from 150 to
|
||||
25. This should prevent upsetting some broken NAT routers, and
|
||||
also improves performance.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>All "chroot"-containing strings got renamed to "sandbox".
|
||||
In particular, some Nix options got renamed, but the old names
|
||||
are still accepted as lower-priority aliases.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>This release has contributions from Anders Claesson, Anthony
|
||||
Cowley, Bjørn Forsman, Brian McKenna, Danny Wilson, davidak, Eelco Dolstra,
|
||||
Fabian Schmitthenner, FrankHB, Ilya Novoselov, janus, Jim Garrison, John
|
||||
Ericson, Jude Taylor, Ludovic Courtès, Manuel Jacob, Mathnerd314,
|
||||
Pascal Wittmann, Peter Simons, Philip Potter, Preston Bennes, Rommel
|
||||
M. Martinez, Sander van der Burg, Shea Levy, Tim Cuthbertson, Tuomas
|
||||
Tynkkynen, Utku Demir and Vladimír Čunát.</para>
|
||||
|
||||
</section>
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.2">
|
||||
|
||||
<title>Release 1.2 (December 6, 2012)</title>
|
||||
<title>Release 1.2 (2012-12-06)</title>
|
||||
|
||||
<para>This release has the following improvements and changes:</para>
|
||||
|
||||
@@ -154,4 +154,4 @@ $ mount -o remount,ro,bind /nix/store
|
||||
<para>This release has contributions from Eelco Dolstra, Florian
|
||||
Friesdorf, Mats Erik Andersson and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.3">
|
||||
|
||||
<title>Release 1.3 (January 4, 2013)</title>
|
||||
<title>Release 1.3 (2013-01-04)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. When this version is first
|
||||
run on Linux, it removes any immutable bits from the Nix store and
|
||||
@@ -16,4 +16,4 @@ efficient.)</para>
|
||||
<para>This release has contributions from Eelco Dolstra and Stuart
|
||||
Pernsteiner.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.4">
|
||||
|
||||
<title>Release 1.4 (February 26, 2013)</title>
|
||||
<title>Release 1.4 (2013-02-26)</title>
|
||||
|
||||
<para>This release fixes a security bug in multi-user operation. It
|
||||
was possible for derivations to cause the mode of files outside of the
|
||||
@@ -36,4 +36,4 @@ xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.5.1">
|
||||
|
||||
<title>Release 1.5.1 (February 28, 2013)</title>
|
||||
<title>Release 1.5.1 (2013-02-28)</title>
|
||||
|
||||
<para>The bug fix to the bug fix had a bug itself, of course. But
|
||||
this time it will work for sure!</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.5.2">
|
||||
|
||||
<title>Release 1.5.2 (May 13, 2013)</title>
|
||||
<title>Release 1.5.2 (2013-05-13)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It has contributions from
|
||||
Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.5">
|
||||
|
||||
<title>Release 1.5 (February 27, 2013)</title>
|
||||
<title>Release 1.5 (2013-02-27)</title>
|
||||
|
||||
<para>This is a brown paper bag release to fix a regression introduced
|
||||
by the hard link security fix in 1.4.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.6.1">
|
||||
|
||||
<title>Release 1.6.1 (October 28, 2013)</title>
|
||||
<title>Release 1.6.1 (2013-10-28)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. Changes of interest
|
||||
are:</para>
|
||||
@@ -66,4 +66,4 @@ are:</para>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.6.0">
|
||||
|
||||
<title>Release 1.6 (September 10, 2013)</title>
|
||||
<title>Release 1.6 (2013-09-10)</title>
|
||||
|
||||
<para>In addition to the usual bug fixes, this release has several new
|
||||
features:</para>
|
||||
@@ -124,4 +124,4 @@ in pkgs.bar
|
||||
Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
|
||||
Levy.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.7">
|
||||
|
||||
<title>Release 1.7 (April 11, 2014)</title>
|
||||
<title>Release 1.7 (2014-04-11)</title>
|
||||
|
||||
<para>In addition to the usual bug fixes, this release has the
|
||||
following new features:</para>
|
||||
@@ -260,4 +260,4 @@ error: attribute `nixUnstabl' missing, at /etc/nixos/configurations/misc/eelco/m
|
||||
Eelco Dolstra, Ian-Woo Kim, Ludovic Courtès, Maxim Ivanov, Petr
|
||||
Rockai, Ricardo M. Correia and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.8">
|
||||
|
||||
<title>Release 1.8 (December 14, 2014)</title>
|
||||
<title>Release 1.8 (2014-12-14)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
@@ -60,7 +60,7 @@ $ nix-store -l $(which xterm)
|
||||
<listitem><para><command>nix-copy-closure</command> now uses
|
||||
<command>nix-store --serve</command> on the remote side to send or
|
||||
receive closures. This fixes a race condition between
|
||||
<command>nix-copy-closureE</command> and the garbage
|
||||
<command>nix-copy-closure</command> and the garbage
|
||||
collector.</para></listitem>
|
||||
|
||||
<listitem><para>Derivations can specify the new special attribute
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
version="5.0"
|
||||
xml:id="ssec-relnotes-1.9">
|
||||
|
||||
<title>Release 1.9 (June 12, 2015)</title>
|
||||
<title>Release 1.9 (2015-06-12)</title>
|
||||
|
||||
<para>In addition to the usual bug fixes, this release has the
|
||||
following new features:</para>
|
||||
@@ -75,7 +75,7 @@ $ nix-shell -p pan -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/
|
||||
<listitem><para>In <command>nixos-rebuild</command> (on NixOS):
|
||||
|
||||
<screen>
|
||||
$ nixos-rebuild test -I nixpkgs=nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-unstable.tar.gz
|
||||
$ nixos-rebuild test -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-unstable.tar.gz
|
||||
</screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
ifeq ($(MAKECMDGOALS), dist)
|
||||
dist-files += $(shell git ls-files)
|
||||
# Make sure we are in repo root with `--git-dir`
|
||||
dist-files += $(shell git --git-dir=.git ls-files || find * -type f)
|
||||
endif
|
||||
|
||||
dist-files += configure config.h.in nix.spec
|
||||
|
||||
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 \
|
||||
-Wno-unneeded-internal-declaration
|
||||
|
||||
$(foreach i, config.h $(call rwildcard, src/lib*, *.hh) src/nix-store/serve-protocol.hh, \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix, 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
|
||||
+10
-10
@@ -9,16 +9,16 @@
|
||||
;;; Code:
|
||||
|
||||
(defconst nix-font-lock-keywords
|
||||
'("\\<if\\>" "\\<then\\>" "\\<else\\>" "\\<assert\\>" "\\<with\\>"
|
||||
"\\<let\\>" "\\<in\\>" "\\<rec\\>" "\\<inherit\\>" "\\<or\\>"
|
||||
("\\<true\\>" . font-lock-builtin-face)
|
||||
("\\<false\\>" . font-lock-builtin-face)
|
||||
("\\<null\\>" . font-lock-builtin-face)
|
||||
("\\<import\\>" . font-lock-builtin-face)
|
||||
("\\<derivation\\>" . font-lock-builtin-face)
|
||||
("\\<baseNameOf\\>" . font-lock-builtin-face)
|
||||
("\\<toString\\>" . font-lock-builtin-face)
|
||||
("\\<isNull\\>" . font-lock-builtin-face)
|
||||
'("\\_<if\\_>" "\\_<then\\_>" "\\_<else\\_>" "\\_<assert\\_>" "\\_<with\\_>"
|
||||
"\\_<let\\_>" "\\_<in\\_>" "\\_<rec\\_>" "\\_<inherit\\_>" "\\_<or\\_>"
|
||||
("\\_<true\\_>" . font-lock-builtin-face)
|
||||
("\\_<false\\_>" . font-lock-builtin-face)
|
||||
("\\_<null\\_>" . font-lock-builtin-face)
|
||||
("\\_<import\\_>" . font-lock-builtin-face)
|
||||
("\\_<derivation\\_>" . font-lock-builtin-face)
|
||||
("\\_<baseNameOf\\_>" . font-lock-builtin-face)
|
||||
("\\_<toString\\_>" . font-lock-builtin-face)
|
||||
("\\_<isNull\\_>" . font-lock-builtin-face)
|
||||
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
|
||||
. font-lock-constant-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\-\.]*\\)[ \t]*="
|
||||
|
||||
@@ -61,7 +61,9 @@ ifeq ($(BUILD_SHARED_LIBS), 1)
|
||||
endif
|
||||
ifneq ($(OS), Darwin)
|
||||
ifneq ($(OS), SunOS)
|
||||
GLOBAL_LDFLAGS += -Wl,--no-copy-dt-needed-entries
|
||||
ifneq ($(OS), FreeBSD)
|
||||
GLOBAL_LDFLAGS += -Wl,--no-copy-dt-needed-entries
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
SET_RPATH_TO_LIBS ?= 1
|
||||
|
||||
@@ -26,6 +26,7 @@ Requires: gzip
|
||||
Requires: xz
|
||||
BuildRequires: bzip2-devel
|
||||
BuildRequires: sqlite-devel
|
||||
BuildRequires: libcurl-devel
|
||||
|
||||
# Hack to make that shitty RPM scanning hack shut up.
|
||||
Provides: perl(Nix::SSH)
|
||||
|
||||
@@ -21,6 +21,7 @@ our @EXPORT = qw(
|
||||
signString checkSignature
|
||||
addToStore makeFixedOutputPath
|
||||
derivationFromPath
|
||||
addTempRoot
|
||||
);
|
||||
|
||||
our $VERSION = '0.15';
|
||||
|
||||
+31
-21
@@ -29,7 +29,7 @@ void doInit()
|
||||
settings.lockCPU = false;
|
||||
store = openStore();
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ int isValidPath(char * path)
|
||||
doInit();
|
||||
RETVAL = store->isValidPath(path);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
@@ -74,7 +74,7 @@ SV * queryReferences(char * path)
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
|
||||
XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ SV * queryPathHash(char * path)
|
||||
string s = "sha256:" + printHash32(hash);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ SV * queryDeriver(char * path)
|
||||
if (deriver == "") XSRETURN_UNDEF;
|
||||
XPUSHs(sv_2mortal(newSVpv(deriver.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ SV * queryPathInfo(char * path, int base32)
|
||||
av_push(arr, newSVpv(i->c_str(), 0));
|
||||
XPUSHs(sv_2mortal(newRV((SV *) arr)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ SV * queryPathFromHashPart(char * hashPart)
|
||||
Path path = store->queryPathFromHashPart(hashPart);
|
||||
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ SV * computeFSClosure(int flipDirection, int includeOutputs, ...)
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
|
||||
XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ SV * topoSortPaths(...)
|
||||
for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i)
|
||||
XPUSHs(sv_2mortal(newSVpv(i->c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ SV * followLinksToStorePath(char * path)
|
||||
doInit();
|
||||
RETVAL = newSVpv(followLinksToStorePath(path).c_str(), 0);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
@@ -184,7 +184,7 @@ void exportPaths(int fd, int sign, ...)
|
||||
FdSink sink(fd);
|
||||
exportPaths(*store, paths, sign, sink);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ void importPaths(int fd)
|
||||
FdSource source(fd);
|
||||
store->importPaths(false, source);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ SV * hashPath(char * algo, int base32, char * path)
|
||||
string s = base32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ SV * hashFile(char * algo, int base32, char * path)
|
||||
string s = base32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ SV * hashString(char * algo, int base32, char * s)
|
||||
string s = base32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
|
||||
string s = toBase32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ SV * signString(SV * secretKey_, char * msg)
|
||||
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
|
||||
#endif
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ int checkSignature(SV * publicKey_, SV * sig_, char * msg)
|
||||
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
|
||||
#endif
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
@@ -296,7 +296,7 @@ SV * addToStore(char * srcPath, int recursive, char * algo)
|
||||
Path path = store->addToStore(baseNameOf(srcPath), srcPath, recursive, parseHashType(algo));
|
||||
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
|
||||
parseHash16or32(ht, hash), name);
|
||||
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -352,7 +352,17 @@ SV * derivationFromPath(char * drvPath)
|
||||
|
||||
RETVAL = newRV_noinc((SV *)hash);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
croak("%s", e.what());
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
||||
void addTempRoot(char * storePath)
|
||||
PPCODE:
|
||||
try {
|
||||
doInit();
|
||||
store->addTempRoot(storePath);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
|
||||
+3
-1
@@ -24,7 +24,9 @@ ifeq ($(perlbindings), yes)
|
||||
|
||||
Store_CXXFLAGS = \
|
||||
-I$(shell $(perl) -e 'use Config; print $$Config{archlibexp};')/CORE \
|
||||
-D_FILE_OFFSET_BITS=64 -Wno-unused-variable -Wno-literal-suffix -Wno-reserved-user-defined-literal
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-Wno-unknown-warning-option -Wno-unused-variable -Wno-literal-suffix \
|
||||
-Wno-reserved-user-defined-literal -Wno-duplicate-decl-specifier -Wno-pointer-bool-conversion
|
||||
|
||||
Store_LIBS = libstore libutil
|
||||
|
||||
|
||||
+16
-10
@@ -23,8 +23,8 @@ let
|
||||
inherit officialRelease;
|
||||
|
||||
buildInputs =
|
||||
[ curl bison flex perl libxml2 libxslt bzip2
|
||||
tetex dblatex nukeReferences pkgconfig sqlite libsodium
|
||||
[ curl bison flex perl libxml2 libxslt bzip2 xz
|
||||
dblatex (dblatex.tex or tetex) nukeReferences pkgconfig sqlite libsodium
|
||||
docbook5 docbook5_xsl
|
||||
] ++ lib.optional (!lib.inNixShell) git;
|
||||
|
||||
@@ -36,7 +36,9 @@ let
|
||||
|
||||
postUnpack = ''
|
||||
# Clean up when building from a working tree.
|
||||
(cd $sourceRoot && (git ls-files -o | xargs -r rm -v))
|
||||
if [[ -d $sourceRoot/.git ]]; then
|
||||
git -C $sourceRoot clean -fd
|
||||
fi
|
||||
'';
|
||||
|
||||
preConfigure = ''
|
||||
@@ -74,14 +76,15 @@ let
|
||||
|
||||
build = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
with import <nixpkgs> { inherit system; };
|
||||
# FIXME: temporarily use a different branch for the Darwin build.
|
||||
with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; };
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix";
|
||||
src = tarball;
|
||||
|
||||
buildInputs =
|
||||
[ curl perl bzip2 openssl pkgconfig sqlite boehmgc ]
|
||||
[ curl perl bzip2 xz openssl pkgconfig sqlite boehmgc ]
|
||||
++ lib.optional stdenv.isLinux libsodium;
|
||||
|
||||
configureFlags = ''
|
||||
@@ -108,7 +111,8 @@ let
|
||||
|
||||
binaryTarball = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
with import <nixpkgs> { inherit system; };
|
||||
# FIXME: temporarily use a different branch for the Darwin build.
|
||||
with import (if system == "x86_64-darwin" then <nixpkgs-darwin> else <nixpkgs>) { inherit system; };
|
||||
|
||||
let
|
||||
toplevel = builtins.getAttr system jobs.build;
|
||||
@@ -150,7 +154,7 @@ let
|
||||
src = tarball;
|
||||
|
||||
buildInputs =
|
||||
[ curl perl bzip2 openssl pkgconfig sqlite
|
||||
[ curl perl bzip2 openssl pkgconfig sqlite xz libsodium
|
||||
# These are for "make check" only:
|
||||
graphviz libxml2 libxslt
|
||||
];
|
||||
@@ -279,7 +283,7 @@ let
|
||||
src = jobs.tarball;
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages =
|
||||
[ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" "libcurl-devel" ]
|
||||
[ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" "libcurl-devel" "openssl-devel" "xz-devel" ]
|
||||
++ extraPackages; };
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = 50;
|
||||
@@ -300,12 +304,14 @@ let
|
||||
src = jobs.tarball;
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages =
|
||||
[ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" "libcurl-dev" ]
|
||||
[ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" "libcurl-dev" "libcurl3-nss" "libssl-dev" "liblzma-dev" ]
|
||||
++ extraPackages; };
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = 50;
|
||||
configureFlags = "--sysconfdir=/etc";
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ];
|
||||
debRequires =
|
||||
[ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" "libssl1.0.0" "liblzma5" ]
|
||||
++ lib.optionals (lib.elem "libsodium-dev" extraPackages) [ "libsodium13" ] ;
|
||||
debMaintainer = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
|
||||
doInstallCheck = true;
|
||||
};
|
||||
|
||||
@@ -16,8 +16,6 @@ binmode STDERR, ":encoding(utf8)";
|
||||
|
||||
my $debug = defined $ENV{NIX_DEBUG_HOOK};
|
||||
|
||||
setVerbosity(0); # make exportPath() less verbose
|
||||
|
||||
|
||||
# General operation:
|
||||
#
|
||||
|
||||
@@ -21,7 +21,7 @@ Nix::Config::readConfig;
|
||||
my @caches;
|
||||
my $gotCaches = 0;
|
||||
|
||||
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
|
||||
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 25);
|
||||
$maxParallelRequests = 1 if $maxParallelRequests < 1;
|
||||
|
||||
my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
|
||||
@@ -80,7 +80,12 @@ sub addRequest {
|
||||
$curl->setopt(CURLOPT_WRITEDATA, $fh);
|
||||
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
||||
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
||||
$curl->setopt(CURLOPT_SSL_VERIFYPEER, 0) unless isTrue($Nix::Config::config{"verify-https-binary-caches"} // "1");
|
||||
|
||||
unless (isTrue($Nix::Config::config{"verify-https-binary-caches"} // "1")) {
|
||||
$curl->setopt(CURLOPT_SSL_VERIFYPEER, 0);
|
||||
$curl->setopt(CURLOPT_SSL_VERIFYHOST, 0);
|
||||
}
|
||||
|
||||
$curl->setopt(CURLOPT_USERAGENT, $userAgent);
|
||||
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
||||
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
||||
@@ -561,7 +566,7 @@ sub downloadBinary {
|
||||
die if $requireSignedBinaryCaches && !defined $info->{signedBy};
|
||||
print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n";
|
||||
checkURL $url;
|
||||
if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
||||
if (system("$Nix::Config::curl --fail --location --connect-timeout $curlConnectTimeout -A '$userAgent' '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
||||
warn "download of ‘$url’ failed" . ($! ? ": $!" : "") . "\n";
|
||||
next;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ my $logFile = "$Nix::Config::logDir/downloads";
|
||||
# estimating the expected download size.
|
||||
my $fast = 1;
|
||||
|
||||
# ‘--insecure’ is fine because Nix verifies the hash of the result.
|
||||
my $curl = "$Nix::Config::curl --fail --location --insecure";
|
||||
my $curl = "$Nix::Config::curl --fail --location";
|
||||
|
||||
|
||||
# Open the manifest cache and update it if necessary.
|
||||
|
||||
+2
-1
@@ -4,7 +4,6 @@ nix_bin_scripts := \
|
||||
$(d)/nix-copy-closure \
|
||||
$(d)/nix-generate-patches \
|
||||
$(d)/nix-install-package \
|
||||
$(d)/nix-prefetch-url \
|
||||
$(d)/nix-pull \
|
||||
$(d)/nix-push
|
||||
|
||||
@@ -18,6 +17,7 @@ nix_substituters := \
|
||||
nix_noinst_scripts := \
|
||||
$(d)/build-remote.pl \
|
||||
$(d)/find-runtime-roots.pl \
|
||||
$(d)/resolve-system-dependencies.pl \
|
||||
$(d)/nix-http-export.cgi \
|
||||
$(d)/nix-profile.sh \
|
||||
$(d)/nix-reduce-build \
|
||||
@@ -30,6 +30,7 @@ profiledir = $(sysconfdir)/profile.d
|
||||
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
|
||||
$(eval $(call install-program-in, $(d)/find-runtime-roots.pl, $(libexecdir)/nix))
|
||||
$(eval $(call install-program-in, $(d)/build-remote.pl, $(libexecdir)/nix))
|
||||
$(eval $(call install-program-in, $(d)/resolve-system-dependencies.pl, $(libexecdir)/nix))
|
||||
$(foreach prog, $(nix_substituters), $(eval $(call install-program-in, $(prog), $(libexecdir)/nix/substituters)))
|
||||
$(eval $(call install-symlink, nix-build, $(bindir)/nix-shell))
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use File::Basename;
|
||||
use Text::ParseWords;
|
||||
use Cwd;
|
||||
|
||||
binmode STDERR, ":encoding(utf8)";
|
||||
@@ -56,7 +57,7 @@ if ($runEnv && defined $ARGV[0] && $ARGV[0] !~ /nix-shell/) {
|
||||
while (<SCRIPT>) {
|
||||
chomp;
|
||||
if (/^\#\!\s*nix-shell (.*)$/) {
|
||||
push @ARGV, split(/ /, $1);
|
||||
push @ARGV, shellwords(/ /, $1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,7 +270,7 @@ foreach my $expr (@exprs) {
|
||||
my $tmp = $ENV{"TMPDIR"} // $ENV{"XDG_RUNTIME_DIR"} // "/tmp";
|
||||
if ($pure) {
|
||||
foreach my $name (keys %ENV) {
|
||||
next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER");
|
||||
next if grep { $_ eq $name } ("HOME", "USER", "LOGNAME", "DISPLAY", "PATH", "TERM", "IN_NIX_SHELL", "TZ", "PAGER", "NIX_BUILD_SHELL");
|
||||
delete $ENV{$name};
|
||||
}
|
||||
# NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
|
||||
|
||||
@@ -105,8 +105,8 @@ sub update {
|
||||
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`;
|
||||
die "$0: unable to check ‘$url’\n" if $? != 0;
|
||||
chomp $url;
|
||||
die "$0: unable to check ‘$url’\n" if $? != 0;
|
||||
|
||||
# If the URL contains a version number, append it to the name
|
||||
# attribute (so that "nix-env -q" on the channels profile
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use Nix::SSH;
|
||||
use Nix::Config;
|
||||
@@ -25,6 +26,7 @@ my $toMode = 1;
|
||||
my $includeOutputs = 0;
|
||||
my $dryRun = 0;
|
||||
my $useSubstitutes = 0;
|
||||
my $verbosity = 1;
|
||||
|
||||
|
||||
# !!! Copied from nix-pack-closure, should put this in a module.
|
||||
@@ -61,6 +63,10 @@ while (@ARGV) {
|
||||
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
|
||||
$useSubstitutes = 1;
|
||||
}
|
||||
elsif ($arg eq "-v") {
|
||||
$verbosity++;
|
||||
setVerbosity($verbosity);
|
||||
}
|
||||
elsif (!defined $sshHost) {
|
||||
$sshHost = $arg;
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use File::Basename;
|
||||
use File::stat;
|
||||
use Nix::Store;
|
||||
use Nix::Config;
|
||||
use Nix::Utils;
|
||||
|
||||
binmode STDERR, ":encoding(utf8)";
|
||||
|
||||
|
||||
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256"; # obsolete
|
||||
my $cacheDir = $ENV{'NIX_DOWNLOAD_CACHE'};
|
||||
|
||||
my @args;
|
||||
my $arg;
|
||||
while ($arg = shift) {
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-prefetch-url" or die;
|
||||
} elsif ($arg eq "--type") {
|
||||
$hashType = shift;
|
||||
die "$0: ‘$arg’ requires an argument\n" unless defined $hashType;
|
||||
} elsif (substr($arg, 0, 1) eq "-") {
|
||||
die "$0: unknown flag ‘$arg’\n";
|
||||
} else {
|
||||
push @args, $arg;
|
||||
}
|
||||
}
|
||||
|
||||
my $url = $args[0];
|
||||
my $expHash = $args[1];
|
||||
|
||||
|
||||
if (!defined $url || $url eq "") {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-prefetch-url URL [EXPECTED-HASH]
|
||||
EOF
|
||||
;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $tmpDir = mkTempDir("nix-prefetch-url");
|
||||
|
||||
# Hack to support the mirror:// scheme from Nixpkgs.
|
||||
if ($url =~ /^mirror:\/\//) {
|
||||
system("$Nix::Config::binDir/nix-build '<nixpkgs>' -A resolveMirrorURLs --argstr url '$url' -o $tmpDir/urls > /dev/null") == 0
|
||||
or die "$0: nix-build failed; maybe \$NIX_PATH is not set properly\n";
|
||||
my @expanded = split ' ', readFile("$tmpDir/urls");
|
||||
die "$0: cannot resolve ‘$url’" unless scalar @expanded > 0;
|
||||
print STDERR "$url expands to $expanded[0]\n";
|
||||
$url = $expanded[0];
|
||||
}
|
||||
|
||||
# Handle escaped characters in the URI. `+', `=' and `?' are the only
|
||||
# characters that are valid in Nix store path names but have a special
|
||||
# meaning in URIs.
|
||||
my $name = basename $url;
|
||||
die "cannot figure out file name for ‘$url’\n" if $name eq "";
|
||||
$name =~ s/%2b/+/g;
|
||||
$name =~ s/%3d/=/g;
|
||||
$name =~ s/%3f/?/g;
|
||||
|
||||
my $finalPath;
|
||||
my $hash;
|
||||
|
||||
# If the hash was given, a file with that hash may already be in the
|
||||
# store.
|
||||
if (defined $expHash) {
|
||||
$finalPath = makeFixedOutputPath(0, $hashType, $expHash, $name);
|
||||
if (isValidPath($finalPath)) { $hash = $expHash; } else { $finalPath = undef; }
|
||||
}
|
||||
|
||||
# If we don't know the hash or a file with that hash doesn't exist,
|
||||
# download the file and add it to the store.
|
||||
if (!defined $finalPath) {
|
||||
|
||||
my $tmpFile = "$tmpDir/$name";
|
||||
|
||||
# Optionally do timestamp-based caching of the download.
|
||||
# Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
|
||||
# the hash and the timestamp of the file at $url. The caching of
|
||||
# the file *contents* is done in Nix store, where it can be
|
||||
# garbage-collected independently.
|
||||
my ($cachedTimestampFN, $cachedHashFN, @cacheFlags);
|
||||
if (defined $cacheDir) {
|
||||
my $urlHash = hashString("sha256", 1, $url);
|
||||
writeFile "$cacheDir/$urlHash.url", $url;
|
||||
$cachedHashFN = "$cacheDir/$urlHash.$hashType";
|
||||
$cachedTimestampFN = "$cacheDir/$urlHash.stamp";
|
||||
@cacheFlags = ("--time-cond", $cachedTimestampFN) if -f $cachedHashFN && -f $cachedTimestampFN;
|
||||
}
|
||||
|
||||
# Perform the download.
|
||||
my @curlFlags = ("curl", $url, "-o", $tmpFile, "--fail", "--location", "--max-redirs", "20", "--disable-epsv", "--cookie-jar", "$tmpDir/cookies", "--remote-time", (split " ", ($ENV{NIX_CURL_FLAGS} || "")));
|
||||
(system $Nix::Config::curl @curlFlags, @cacheFlags) == 0 or die "$0: download of ‘$url’ failed\n";
|
||||
|
||||
if (defined $cacheDir && ! -e $tmpFile) {
|
||||
# Curl didn't create $tmpFile, so apparently there's no newer
|
||||
# file on the server.
|
||||
$hash = readFile $cachedHashFN or die;
|
||||
$finalPath = makeFixedOutputPath(0, $hashType, $hash, $name);
|
||||
unless (isValidPath $finalPath) {
|
||||
print STDERR "cached contents of ‘$url’ disappeared, redownloading...\n";
|
||||
$finalPath = undef;
|
||||
(system $Nix::Config::curl @curlFlags) == 0 or die "$0: download of ‘$url’ failed\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $finalPath) {
|
||||
|
||||
# Compute the hash.
|
||||
$hash = hashFile($hashType, $hashType ne "md5", $tmpFile);
|
||||
|
||||
if (defined $cacheDir) {
|
||||
writeFile $cachedHashFN, $hash;
|
||||
my $st = stat($tmpFile) or die;
|
||||
open STAMP, ">$cachedTimestampFN" or die; close STAMP;
|
||||
utime($st->atime, $st->mtime, $cachedTimestampFN) or die;
|
||||
}
|
||||
|
||||
# Add the downloaded file to the Nix store.
|
||||
$finalPath = addToStore($tmpFile, 0, $hashType);
|
||||
}
|
||||
|
||||
die "$0: hash mismatch for ‘$url’\n" if defined $expHash && $expHash ne $hash;
|
||||
}
|
||||
|
||||
print STDERR "path is ‘$finalPath’\n" unless $ENV{'QUIET'};
|
||||
print "$hash\n";
|
||||
print "$finalPath\n" if $ENV{'PRINT_PATH'};
|
||||
@@ -9,7 +9,6 @@ if [ -n "$HOME" ]; then
|
||||
fi
|
||||
|
||||
export PATH=$NIX_LINK/bin:$NIX_LINK/sbin:$PATH
|
||||
export MANPATH=$NIX_LINK/share/man:$MANPATH
|
||||
|
||||
# Subscribe the user to the Nixpkgs channel by default.
|
||||
if [ ! -e $HOME/.nix-channels ]; then
|
||||
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Cwd qw(realpath);
|
||||
use Errno;
|
||||
use File::Basename qw(dirname);
|
||||
use File::Path qw(make_path);
|
||||
use File::Spec::Functions qw(catfile);
|
||||
use List::Util qw(reduce);
|
||||
use IPC::Open3;
|
||||
use Nix::Config;
|
||||
use Nix::Store qw(derivationFromPath);
|
||||
use POSIX qw(uname);
|
||||
use Storable qw(lock_retrieve lock_store);
|
||||
|
||||
my ($sysname, undef, $version, undef, $machine) = uname;
|
||||
$sysname =~ /Darwin/ or die "This tool is only meant to be used on Darwin systems.";
|
||||
|
||||
my $cache = "$Nix::Config::stateDir/dependency-maps/$machine-$sysname-$version.map";
|
||||
|
||||
make_path dirname($cache);
|
||||
|
||||
our $DEPS;
|
||||
eval {
|
||||
$DEPS = lock_retrieve($cache);
|
||||
};
|
||||
|
||||
if($!{ENOENT}) {
|
||||
lock_store {}, $cache;
|
||||
$DEPS = {};
|
||||
} elsif($@) {
|
||||
die "Unable to obtain a lock on dependency-map file $cache: $@";
|
||||
}
|
||||
|
||||
sub mkset(@) {
|
||||
my %set;
|
||||
@set{@_} = ();
|
||||
\%set
|
||||
}
|
||||
|
||||
sub union($$) {
|
||||
my ($set1, $set2) = @_;
|
||||
my %new = (%$set1, %$set2);
|
||||
\%new
|
||||
}
|
||||
|
||||
sub cache_filepath($) {
|
||||
my $fp = shift;
|
||||
$fp =~ s/-/--/g;
|
||||
$fp =~ s/\//-/g;
|
||||
$fp =~ s/^-//g;
|
||||
catfile $cache, $fp
|
||||
}
|
||||
|
||||
sub resolve_tree {
|
||||
sub resolve_tree_inner {
|
||||
my ($lib, $TREE) = @_;
|
||||
return if (defined $TREE->{$lib});
|
||||
$TREE->{$lib} = mkset(@{cache_get($lib)});
|
||||
foreach my $dep (keys %{$TREE->{$lib}}) {
|
||||
resolve_tree_inner($dep, $TREE);
|
||||
}
|
||||
values %$TREE
|
||||
}
|
||||
|
||||
reduce { union($a, $b) } {}, resolve_tree_inner(@_)
|
||||
}
|
||||
|
||||
sub cache_get {
|
||||
my $key = shift;
|
||||
if (defined $DEPS->{$key}) {
|
||||
$DEPS->{$key}
|
||||
} else {
|
||||
cache_insert($key);
|
||||
cache_get($key)
|
||||
}
|
||||
}
|
||||
|
||||
sub cache_insert($) {
|
||||
my $key = shift;
|
||||
print STDERR "Finding dependencies for $key...\n";
|
||||
my @deps = find_deps($key);
|
||||
$DEPS->{$key} = \@deps;
|
||||
}
|
||||
|
||||
sub find_deps($) {
|
||||
my $lib = shift;
|
||||
my($chld_in, $chld_out, $chld_err);
|
||||
my $pid = open3($chld_in, $chld_out, $chld_err, "@otool@", "-L", "-arch", "x86_64", $lib);
|
||||
waitpid($pid, 0);
|
||||
my $line = readline $chld_out;
|
||||
if($? == 0 and $line !~ /not an object file/) {
|
||||
my @libs;
|
||||
while(<$chld_out>) {
|
||||
my $dep = (split /\s+/)[1];
|
||||
push @libs, $dep unless $dep eq $lib or $dep =~ /\@rpath/;
|
||||
}
|
||||
@libs
|
||||
} elsif (-l $lib) {
|
||||
(realpath($lib))
|
||||
} else {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $ARGV[0]) {
|
||||
my $deps = derivationFromPath($ARGV[0])->{"env"}->{"__impureHostDeps"};
|
||||
if (defined $deps) {
|
||||
my @files = split(/\s+/, $deps);
|
||||
my $depcache = {};
|
||||
my $depset = reduce { union($a, $b) } (map { resolve_tree($_, $depcache) } @files);
|
||||
print "extra-chroot-dirs\n";
|
||||
print join("\n", keys %$depset);
|
||||
print "\n";
|
||||
}
|
||||
lock_store($DEPS, $cache);
|
||||
} else {
|
||||
print STDERR "Usage: $0 path/to/derivation.drv\n";
|
||||
exit 1
|
||||
}
|
||||
@@ -43,8 +43,7 @@ static std::pair<FdSink, FdSource> connect(const string & conn)
|
||||
|
||||
static void substitute(std::pair<FdSink, FdSource> & pipes, Path storePath, Path destPath)
|
||||
{
|
||||
writeInt(cmdDumpStorePath, pipes.first);
|
||||
writeString(storePath, pipes.first);
|
||||
pipes.first << cmdDumpStorePath << storePath;
|
||||
pipes.first.flush();
|
||||
restorePath(destPath, pipes.second);
|
||||
std::cout << std::endl;
|
||||
@@ -58,17 +57,17 @@ static void query(std::pair<FdSink, FdSource> & pipes)
|
||||
string cmd = tokenized.front();
|
||||
tokenized.pop_front();
|
||||
if (cmd == "have") {
|
||||
writeInt(cmdQueryValidPaths, pipes.first);
|
||||
writeInt(0, pipes.first); // don't lock
|
||||
writeInt(0, pipes.first); // don't substitute
|
||||
writeStrings(tokenized, pipes.first);
|
||||
pipes.first
|
||||
<< cmdQueryValidPaths
|
||||
<< 0 // don't lock
|
||||
<< 0 // don't substitute
|
||||
<< tokenized;
|
||||
pipes.first.flush();
|
||||
PathSet paths = readStrings<PathSet>(pipes.second);
|
||||
foreach (PathSet::iterator, i, paths)
|
||||
std::cout << *i << std::endl;
|
||||
for (auto & i : paths)
|
||||
std::cout << i << std::endl;
|
||||
} else if (cmd == "info") {
|
||||
writeInt(cmdQueryPathInfos, pipes.first);
|
||||
writeStrings(tokenized, pipes.first);
|
||||
pipes.first << cmdQueryPathInfos << tokenized;
|
||||
pipes.first.flush();
|
||||
while (1) {
|
||||
Path path = readString(pipes.second);
|
||||
@@ -80,8 +79,8 @@ static void query(std::pair<FdSink, FdSource> & pipes)
|
||||
std::cout << deriver << std::endl;
|
||||
PathSet references = readStorePaths<PathSet>(pipes.second);
|
||||
std::cout << references.size() << std::endl;
|
||||
foreach (PathSet::iterator, i, references)
|
||||
std::cout << *i << std::endl;
|
||||
for (auto & i : references)
|
||||
std::cout << i << std::endl;
|
||||
std::cout << readLongLong(pipes.second) << std::endl;
|
||||
std::cout << readLongLong(pipes.second) << std::endl;
|
||||
}
|
||||
@@ -116,13 +115,13 @@ int main(int argc, char * * argv)
|
||||
std::pair<FdSink, FdSource> pipes = connect(host);
|
||||
|
||||
/* Exchange the greeting */
|
||||
writeInt(SERVE_MAGIC_1, pipes.first);
|
||||
pipes.first << SERVE_MAGIC_1;
|
||||
pipes.first.flush();
|
||||
unsigned int magic = readInt(pipes.second);
|
||||
if (magic != SERVE_MAGIC_2)
|
||||
throw Error("protocol mismatch");
|
||||
readInt(pipes.second); // Server version, unused for now
|
||||
writeInt(SERVE_PROTOCOL_VERSION, pipes.first);
|
||||
pipes.first << SERVE_PROTOCOL_VERSION;
|
||||
pipes.first.flush();
|
||||
|
||||
string arg = argv[1];
|
||||
|
||||
@@ -42,11 +42,10 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
Value * v = &vIn;
|
||||
|
||||
foreach (Strings::iterator, i, tokens) {
|
||||
for (auto & attr : tokens) {
|
||||
|
||||
/* Is *i an index (integer) or a normal attribute name? */
|
||||
/* Is i an index (integer) or a normal attribute name? */
|
||||
enum { apAttr, apIndex } apType = apAttr;
|
||||
string attr = *i;
|
||||
unsigned int attrIndex;
|
||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||
|
||||
@@ -77,15 +76,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
else if (apType == apIndex) {
|
||||
|
||||
if (v->type != tList)
|
||||
if (!v->isList())
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path ‘%1%’ should be a list but is %2%")
|
||||
% attrPath % showType(*v));
|
||||
|
||||
if (attrIndex >= v->list.length)
|
||||
if (attrIndex >= v->listSize())
|
||||
throw Error(format("list index %1% in selection path ‘%2%’ is out of range") % attrIndex % attrPath);
|
||||
|
||||
v = v->list.elems[attrIndex];
|
||||
v = v->listElems()[attrIndex];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "attr-set.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static void * allocBytes(size_t n)
|
||||
{
|
||||
void * p;
|
||||
#if HAVE_BOEHMGC
|
||||
p = GC_malloc(n);
|
||||
#else
|
||||
p = malloc(n);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate a new array of attributes for an attribute set with a specific
|
||||
capacity. The space is implicitly reserved after the Bindings
|
||||
structure. */
|
||||
Bindings * EvalState::allocBindings(Bindings::size_t capacity)
|
||||
{
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings(capacity);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::mkAttrs(Value & v, unsigned int capacity)
|
||||
{
|
||||
if (capacity == 0) {
|
||||
v = vEmptySet;
|
||||
return;
|
||||
}
|
||||
clearValue(v);
|
||||
v.type = tAttrs;
|
||||
v.attrs = allocBindings(capacity);
|
||||
nrAttrsets++;
|
||||
nrAttrsInAttrsets += capacity;
|
||||
}
|
||||
|
||||
|
||||
/* Create a new attribute named 'name' on an existing attribute set stored
|
||||
in 'vAttrs' and return the newly allocated Value which is associated with
|
||||
this attribute. */
|
||||
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
||||
{
|
||||
Value * v = allocValue();
|
||||
vAttrs.attrs->push_back(Attr(name, v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
void Bindings::sort()
|
||||
{
|
||||
std::sort(begin(), end());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "symbol-table.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
class EvalState;
|
||||
struct Value;
|
||||
|
||||
/* Map one attribute name to its value. */
|
||||
struct Attr
|
||||
{
|
||||
Symbol name;
|
||||
Value * value;
|
||||
Pos * pos;
|
||||
Attr(Symbol name, Value * value, Pos * pos = &noPos)
|
||||
: name(name), value(value), pos(pos) { };
|
||||
Attr() : pos(&noPos) { };
|
||||
bool operator < (const Attr & a) const
|
||||
{
|
||||
return name < a.name;
|
||||
}
|
||||
};
|
||||
|
||||
/* Bindings contains all the attributes of an attribute set. It is defined
|
||||
by its size and its capacity, the capacity being the number of Attr
|
||||
elements allocated after this structure, while the size corresponds to
|
||||
the number of elements already inserted in this structure. */
|
||||
class Bindings
|
||||
{
|
||||
public:
|
||||
typedef uint32_t size_t;
|
||||
|
||||
private:
|
||||
size_t size_, capacity_;
|
||||
Attr attrs[0];
|
||||
|
||||
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||
Bindings(const Bindings & bindings) = delete;
|
||||
|
||||
public:
|
||||
size_t size() const { return size_; }
|
||||
|
||||
bool empty() const { return !size_; }
|
||||
|
||||
typedef Attr * iterator;
|
||||
|
||||
void push_back(const Attr & attr)
|
||||
{
|
||||
assert(size_ < capacity_);
|
||||
attrs[size_++] = attr;
|
||||
}
|
||||
|
||||
iterator find(const Symbol & name)
|
||||
{
|
||||
Attr key(name, 0);
|
||||
iterator i = std::lower_bound(begin(), end(), key);
|
||||
if (i != end() && i->name == name) return i;
|
||||
return end();
|
||||
}
|
||||
|
||||
iterator begin() { return &attrs[0]; }
|
||||
iterator end() { return &attrs[size_]; }
|
||||
|
||||
Attr & operator[](size_t pos)
|
||||
{
|
||||
return attrs[pos];
|
||||
}
|
||||
|
||||
void sort();
|
||||
|
||||
size_t capacity() { return capacity_; }
|
||||
|
||||
friend class EvalState;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s))
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw EvalError(s);
|
||||
throw EvalError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||
@@ -24,7 +24,7 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v, const
|
||||
}
|
||||
|
||||
|
||||
void EvalState::forceValue(Value & v)
|
||||
void EvalState::forceValue(Value & v, const Pos & pos)
|
||||
{
|
||||
if (v.type == tThunk) {
|
||||
Env * env = v.thunk.env;
|
||||
@@ -43,7 +43,7 @@ void EvalState::forceValue(Value & v)
|
||||
else if (v.type == tApp)
|
||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||
else if (v.type == tBlackhole)
|
||||
throwEvalError("infinite recursion encountered");
|
||||
throwEvalError("infinite recursion encountered, at %1%", pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
||||
inline void EvalState::forceList(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tList)
|
||||
if (!v.isList())
|
||||
throwTypeError("value is %1% while a list was expected", v);
|
||||
}
|
||||
|
||||
@@ -74,9 +74,8 @@ inline void EvalState::forceList(Value & v)
|
||||
inline void EvalState::forceList(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tList)
|
||||
if (!v.isList())
|
||||
throwTypeError("value is %1% while a list was expected, at %2%", v, pos);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+146
-130
@@ -55,14 +55,10 @@ static void * allocBytes(size_t n)
|
||||
}
|
||||
|
||||
|
||||
void Bindings::sort()
|
||||
{
|
||||
std::sort(begin(), end());
|
||||
}
|
||||
|
||||
|
||||
static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (active.find(&v) != active.end()) {
|
||||
str << "<CYCLE>";
|
||||
return;
|
||||
@@ -96,8 +92,8 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
||||
str << "{ ";
|
||||
typedef std::map<string, Value *> Sorted;
|
||||
Sorted sorted;
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
sorted[i->name] = i->value;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted[i.name] = i.value;
|
||||
for (auto & i : sorted) {
|
||||
str << i.first << " = ";
|
||||
printValue(str, active, *i.second);
|
||||
@@ -106,10 +102,12 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
||||
str << "}";
|
||||
break;
|
||||
}
|
||||
case tList:
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
str << "[ ";
|
||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||
printValue(str, active, *v.list.elems[n]);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
printValue(str, active, *v.listElems()[n]);
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
@@ -155,7 +153,7 @@ string showType(const Value & v)
|
||||
case tPath: return "a path";
|
||||
case tNull: return "null";
|
||||
case tAttrs: return "a set";
|
||||
case tList: return "a list";
|
||||
case tList1: case tList2: case tListN: return "a list";
|
||||
case tThunk: return "a thunk";
|
||||
case tApp: return "a function application";
|
||||
case tLambda: return "a function";
|
||||
@@ -234,6 +232,18 @@ void initGC()
|
||||
}
|
||||
|
||||
|
||||
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
|
||||
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
|
||||
static Strings parseNixPath(const string & in)
|
||||
{
|
||||
string marker = "\001//";
|
||||
auto res = tokenizeString<Strings>(replaceStrings(in, "://", marker), ":");
|
||||
for (auto & s : res)
|
||||
s = replaceStrings(s, marker, "://");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
EvalState::EvalState(const Strings & _searchPath)
|
||||
: sWith(symbols.create("<with>"))
|
||||
, sOutPath(symbols.create("outPath"))
|
||||
@@ -251,14 +261,10 @@ EvalState::EvalState(const Strings & _searchPath)
|
||||
, sLine(symbols.create("line"))
|
||||
, sColumn(symbols.create("column"))
|
||||
, sFunctor(symbols.create("__functor"))
|
||||
, repair(false)
|
||||
, sToString(symbols.create("__toString"))
|
||||
, baseEnv(allocEnv(128))
|
||||
, staticBaseEnv(false, 0)
|
||||
, baseEnvDispl(0)
|
||||
{
|
||||
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
||||
nrAttrsets = nrAttrsInAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
nrListConcats = nrPrimOpCalls = nrFunctionCalls = 0;
|
||||
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
||||
|
||||
restricted = settings.get("restrict-eval", false);
|
||||
@@ -266,11 +272,15 @@ EvalState::EvalState(const Strings & _searchPath)
|
||||
assert(gcInitialised);
|
||||
|
||||
/* Initialise the Nix expression search path. */
|
||||
Strings paths = tokenizeString<Strings>(getEnv("NIX_PATH", ""), ":");
|
||||
Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
|
||||
for (auto & i : _searchPath) addToSearchPath(i, true);
|
||||
for (auto & i : paths) addToSearchPath(i);
|
||||
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
||||
|
||||
clearValue(vEmptySet);
|
||||
vEmptySet.type = tAttrs;
|
||||
vEmptySet.attrs = allocBindings(0);
|
||||
|
||||
createBaseEnv();
|
||||
}
|
||||
|
||||
@@ -349,11 +359,6 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||
throw EvalError(format(s) % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw EvalError(format(s) % pos);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const Pos & pos))
|
||||
{
|
||||
throw EvalError(format(s) % s2 % pos);
|
||||
@@ -428,8 +433,8 @@ void mkString(Value & v, const string & s, const PathSet & context)
|
||||
unsigned int n = 0;
|
||||
v.string.context = (const char * *)
|
||||
allocBytes((context.size() + 1) * sizeof(char *));
|
||||
foreach (PathSet::const_iterator, i, context)
|
||||
v.string.context[n++] = dupString(i->c_str());
|
||||
for (auto & i : context)
|
||||
v.string.context[n++] = dupString(i.c_str());
|
||||
v.string.context[n] = 0;
|
||||
}
|
||||
}
|
||||
@@ -491,37 +496,19 @@ Env & EvalState::allocEnv(unsigned int size)
|
||||
}
|
||||
|
||||
|
||||
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
||||
{
|
||||
Value * v = allocValue();
|
||||
vAttrs.attrs->push_back(Attr(name, v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Bindings * EvalState::allocBindings(Bindings::size_t capacity)
|
||||
{
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings(capacity);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::mkList(Value & v, unsigned int length)
|
||||
void EvalState::mkList(Value & v, unsigned int size)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tList;
|
||||
v.list.length = length;
|
||||
v.list.elems = length ? (Value * *) allocBytes(length * sizeof(Value *)) : 0;
|
||||
nrListElems += length;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::mkAttrs(Value & v, unsigned int expected)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tAttrs;
|
||||
v.attrs = allocBindings(expected);
|
||||
nrAttrsets++;
|
||||
nrAttrsInAttrsets += expected;
|
||||
if (size == 1)
|
||||
v.type = tList1;
|
||||
else if (size == 2)
|
||||
v.type = tList2;
|
||||
else {
|
||||
v.type = tListN;
|
||||
v.bigList.size = size;
|
||||
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
|
||||
}
|
||||
nrListElems += size;
|
||||
}
|
||||
|
||||
|
||||
@@ -709,15 +696,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
environment, while the inherited attributes are evaluated
|
||||
in the original environment. */
|
||||
unsigned int displ = 0;
|
||||
foreach (AttrDefs::iterator, i, attrs) {
|
||||
for (auto & i : attrs) {
|
||||
Value * vAttr;
|
||||
if (hasOverrides && !i->second.inherited) {
|
||||
if (hasOverrides && !i.second.inherited) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i->second.e);
|
||||
mkThunk(*vAttr, env2, i.second.e);
|
||||
} else
|
||||
vAttr = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
||||
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
v.attrs->push_back(Attr(i.first, vAttr, &i.second.pos));
|
||||
}
|
||||
|
||||
/* If the rec contains an attribute called `__overrides', then
|
||||
@@ -748,25 +735,25 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
}
|
||||
|
||||
else
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
|
||||
for (auto & i : attrs)
|
||||
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), &i.second.pos));
|
||||
|
||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
|
||||
for (auto & i : dynamicAttrs) {
|
||||
Value nameVal;
|
||||
i->nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||
state.forceValue(nameVal);
|
||||
i.nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||
state.forceValue(nameVal, i.pos);
|
||||
if (nameVal.type == tNull)
|
||||
continue;
|
||||
state.forceStringNoCtx(nameVal);
|
||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
||||
Bindings::iterator j = v.attrs->find(nameSym);
|
||||
if (j != v.attrs->end())
|
||||
throwEvalError("dynamic attribute ‘%1%’ at %2% already defined at %3%", nameSym, i->pos, *j->pos);
|
||||
throwEvalError("dynamic attribute ‘%1%’ at %2% already defined at %3%", nameSym, i.pos, *j->pos);
|
||||
|
||||
i->valueExpr->setName(nameSym);
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
v.attrs->push_back(Attr(nameSym, i->valueExpr->maybeThunk(state, *dynamicEnv), &i->pos));
|
||||
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), &i.pos));
|
||||
v.attrs->sort(); // FIXME: inefficient
|
||||
}
|
||||
}
|
||||
@@ -783,8 +770,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
while the inherited attributes are evaluated in the original
|
||||
environment. */
|
||||
unsigned int displ = 0;
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
||||
for (auto & i : attrs->attrs)
|
||||
env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||
|
||||
body->eval(state, env2, v);
|
||||
}
|
||||
@@ -793,15 +780,15 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
state.mkList(v, elems.size());
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
v.list.elems[n] = elems[n]->maybeThunk(state, env);
|
||||
for (unsigned int n = 0; n < elems.size(); ++n)
|
||||
v.listElems()[n] = elems[n]->maybeThunk(state, env);
|
||||
}
|
||||
|
||||
|
||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value * v2 = state.lookupVar(&env, *this, false);
|
||||
state.forceValue(*v2);
|
||||
state.forceValue(*v2, pos);
|
||||
v = *v2;
|
||||
}
|
||||
|
||||
@@ -835,12 +822,12 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
try {
|
||||
|
||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||
for (auto & i : attrPath) {
|
||||
nrLookups++;
|
||||
Bindings::iterator j;
|
||||
Symbol name = getName(*i, state, env);
|
||||
Symbol name = getName(i, state, env);
|
||||
if (def) {
|
||||
state.forceValue(*vAttrs);
|
||||
state.forceValue(*vAttrs, pos);
|
||||
if (vAttrs->type != tAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
@@ -857,7 +844,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
if (state.countCalls && pos2) state.attrSelects[*pos2]++;
|
||||
}
|
||||
|
||||
state.forceValue(*vAttrs);
|
||||
state.forceValue(*vAttrs, ( pos2 != NULL ? *pos2 : this->pos ) );
|
||||
|
||||
} catch (Error & e) {
|
||||
if (pos2 && pos2->file != state.sDerivationNix)
|
||||
@@ -877,10 +864,10 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
e->eval(state, env, vTmp);
|
||||
|
||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||
for (auto & i : attrPath) {
|
||||
state.forceValue(*vAttrs);
|
||||
Bindings::iterator j;
|
||||
Symbol name = getName(*i, state, env);
|
||||
Symbol name = getName(i, state, env);
|
||||
if (vAttrs->type != tAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
@@ -959,10 +946,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
if (fun.type == tAttrs) {
|
||||
auto found = fun.attrs->find(sFunctor);
|
||||
if (found != fun.attrs->end()) {
|
||||
forceValue(*found->value);
|
||||
forceValue(*found->value, pos);
|
||||
Value * v2 = allocValue();
|
||||
callFunction(*found->value, fun, *v2, pos);
|
||||
forceValue(*v2);
|
||||
forceValue(*v2, pos);
|
||||
return callFunction(*v2, arg, v, pos);
|
||||
}
|
||||
}
|
||||
@@ -993,12 +980,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
there is no matching actual argument but the formal
|
||||
argument has a default, use the default. */
|
||||
unsigned int attrsUsed = 0;
|
||||
foreach (Formals::Formals_::iterator, i, lambda.formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i->name);
|
||||
for (auto & i : lambda.formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i.name);
|
||||
if (j == arg.attrs->end()) {
|
||||
if (!i->def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
|
||||
lambda, i->name, pos);
|
||||
env2.values[displ++] = i->def->maybeThunk(*this, env2);
|
||||
if (!i.def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
|
||||
lambda, i.name, pos);
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
env2.values[displ++] = j->value;
|
||||
@@ -1010,9 +997,9 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
if (!lambda.formals->ellipsis && attrsUsed != arg.attrs->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
foreach (Bindings::iterator, i, *arg.attrs)
|
||||
if (lambda.formals->argNames.find(i->name) == lambda.formals->argNames.end())
|
||||
throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i->name, pos);
|
||||
for (auto & i : *arg.attrs)
|
||||
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
|
||||
throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i.name, pos);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
@@ -1046,6 +1033,17 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||
{
|
||||
forceValue(fun);
|
||||
|
||||
if (fun.type == tAttrs) {
|
||||
auto found = fun.attrs->find(sFunctor);
|
||||
if (found != fun.attrs->end()) {
|
||||
forceValue(*found->value);
|
||||
Value * v = allocValue();
|
||||
callFunction(*found->value, fun, *v, noPos);
|
||||
forceValue(*v);
|
||||
return autoCallFunction(args, *v, res);
|
||||
}
|
||||
}
|
||||
|
||||
if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) {
|
||||
res = fun;
|
||||
return;
|
||||
@@ -1054,12 +1052,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||
Value * actualArgs = allocValue();
|
||||
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
|
||||
|
||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i->name);
|
||||
for (auto & i : fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i.name);
|
||||
if (j != args.end())
|
||||
actualArgs->attrs->push_back(*j);
|
||||
else if (!i->def)
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i->name);
|
||||
else if (!i.def)
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i.name);
|
||||
}
|
||||
|
||||
actualArgs->attrs->sort();
|
||||
@@ -1187,20 +1185,21 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, co
|
||||
unsigned int len = 0;
|
||||
for (unsigned int n = 0; n < nrLists; ++n) {
|
||||
forceList(*lists[n], pos);
|
||||
unsigned int l = lists[n]->list.length;
|
||||
unsigned int l = lists[n]->listSize();
|
||||
len += l;
|
||||
if (l) nonEmpty = lists[n];
|
||||
}
|
||||
|
||||
if (nonEmpty && len == nonEmpty->list.length) {
|
||||
if (nonEmpty && len == nonEmpty->listSize()) {
|
||||
v = *nonEmpty;
|
||||
return;
|
||||
}
|
||||
|
||||
mkList(v, len);
|
||||
auto out = v.listElems();
|
||||
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
|
||||
unsigned int l = lists[n]->list.length;
|
||||
memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *));
|
||||
unsigned int l = lists[n]->listSize();
|
||||
memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
|
||||
pos += l;
|
||||
}
|
||||
}
|
||||
@@ -1215,9 +1214,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
bool first = !forceString;
|
||||
ValueType firstType = tString;
|
||||
|
||||
foreach (vector<Expr *>::iterator, i, *es) {
|
||||
for (auto & i : *es) {
|
||||
Value vTmp;
|
||||
(*i)->eval(state, env, vTmp);
|
||||
i->eval(state, env, vTmp);
|
||||
|
||||
/* If the first element is a path, then the result will also
|
||||
be a path, we don't copy anything (yet - that's done later,
|
||||
@@ -1276,9 +1275,9 @@ void EvalState::forceValueDeep(Value & v)
|
||||
}
|
||||
}
|
||||
|
||||
else if (v.type == tList) {
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
recurse(*v.list.elems[n]);
|
||||
else if (v.isList()) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
recurse(*v.listElems()[n]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1288,7 +1287,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||
|
||||
NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v);
|
||||
forceValue(v, pos);
|
||||
if (v.type != tInt)
|
||||
throwTypeError("value is %1% while an integer was expected, at %2%", v, pos);
|
||||
return v.integer;
|
||||
@@ -1304,17 +1303,23 @@ bool EvalState::forceBool(Value & v)
|
||||
}
|
||||
|
||||
|
||||
bool EvalState::isFunctor(Value & fun)
|
||||
{
|
||||
return fun.type == tAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
|
||||
}
|
||||
|
||||
|
||||
void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
|
||||
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v))
|
||||
throwTypeError("value is %1% while a function was expected, at %2%", v, pos);
|
||||
}
|
||||
|
||||
|
||||
string EvalState::forceString(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v);
|
||||
forceValue(v, pos);
|
||||
if (v.type != tString) {
|
||||
if (pos)
|
||||
throwTypeError("value is %1% while a string was expected, at %2%", v, pos);
|
||||
@@ -1385,7 +1390,14 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
}
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(sOutPath);
|
||||
auto i = v.attrs->find(sToString);
|
||||
if (i != v.attrs->end()) {
|
||||
forceValue(*i->value, pos);
|
||||
Value v1;
|
||||
callFunction(*i->value, v, v1, pos);
|
||||
return coerceToString(pos, v1, context, coerceMore, copyToStore);
|
||||
}
|
||||
i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end()) throwTypeError("cannot coerce a set to a string, at %1%", pos);
|
||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||
}
|
||||
@@ -1399,17 +1411,17 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
shell scripting convenience, just like `null'. */
|
||||
if (v.type == tBool && v.boolean) return "1";
|
||||
if (v.type == tBool && !v.boolean) return "";
|
||||
if (v.type == tInt) return int2String(v.integer);
|
||||
if (v.type == tInt) return std::to_string(v.integer);
|
||||
if (v.type == tNull) return "";
|
||||
|
||||
if (v.type == tList) {
|
||||
if (v.isList()) {
|
||||
string result;
|
||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||
result += coerceToString(pos, *v.list.elems[n],
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
result += coerceToString(pos, *v.listElems()[n],
|
||||
context, coerceMore, copyToStore);
|
||||
if (n < v.list.length - 1
|
||||
if (n < v.listSize() - 1
|
||||
/* !!! not quite correct */
|
||||
&& (v.list.elems[n]->type != tList || v.list.elems[n]->list.length != 0))
|
||||
&& (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
|
||||
result += " ";
|
||||
}
|
||||
return result;
|
||||
@@ -1480,10 +1492,12 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
case tNull:
|
||||
return true;
|
||||
|
||||
case tList:
|
||||
if (v1.list.length != v2.list.length) return false;
|
||||
for (unsigned int n = 0; n < v1.list.length; ++n)
|
||||
if (!eqValues(*v1.list.elems[n], *v2.list.elems[n])) return false;
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
if (v1.listSize() != v2.listSize()) return false;
|
||||
for (unsigned int n = 0; n < v1.listSize(); ++n)
|
||||
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
|
||||
return true;
|
||||
|
||||
case tAttrs: {
|
||||
@@ -1569,25 +1583,25 @@ void EvalState::printStats()
|
||||
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
||||
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
||||
PrimOpCalls_ primOpCalls_;
|
||||
foreach (PrimOpCalls::iterator, i, primOpCalls)
|
||||
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
|
||||
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
|
||||
for (auto & i : primOpCalls)
|
||||
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i.second, i.first));
|
||||
for (auto i = primOpCalls_.rbegin(); i != primOpCalls_.rend(); ++i)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
|
||||
typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
|
||||
FunctionCalls_ functionCalls_;
|
||||
foreach (FunctionCalls::iterator, i, functionCalls)
|
||||
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
|
||||
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
|
||||
for (auto & i : functionCalls)
|
||||
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i.second, i.first));
|
||||
for (auto i = functionCalls_.rbegin(); i != functionCalls_.rend(); ++i)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
|
||||
|
||||
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
|
||||
typedef std::multimap<unsigned int, Pos> AttrSelects_;
|
||||
AttrSelects_ attrSelects_;
|
||||
foreach (AttrSelects::iterator, i, attrSelects)
|
||||
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
||||
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
|
||||
for (auto & i : attrSelects)
|
||||
attrSelects_.insert(std::pair<unsigned int, Pos>(i.second, i.first));
|
||||
for (auto i = attrSelects_.rbegin(); i != attrSelects_.rend(); ++i)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
}
|
||||
@@ -1631,12 +1645,14 @@ size_t valueSize(Value & v)
|
||||
sz += doValue(*i.value);
|
||||
}
|
||||
break;
|
||||
case tList:
|
||||
if (seen.find(v.list.elems) == seen.end()) {
|
||||
seen.insert(v.list.elems);
|
||||
sz += v.list.length * sizeof(Value *);
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
sz += doValue(*v.list.elems[n]);
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
if (seen.find(v.listElems()) == seen.end()) {
|
||||
seen.insert(v.listElems());
|
||||
sz += v.listSize() * sizeof(Value *);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
sz += doValue(*v.listElems()[n]);
|
||||
}
|
||||
break;
|
||||
case tThunk:
|
||||
|
||||
+21
-80
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "attr-set.hh"
|
||||
#include "value.hh"
|
||||
#include "nixexpr.hh"
|
||||
#include "symbol-table.hh"
|
||||
@@ -18,70 +19,6 @@ namespace nix {
|
||||
class EvalState;
|
||||
|
||||
|
||||
struct Attr
|
||||
{
|
||||
Symbol name;
|
||||
Value * value;
|
||||
Pos * pos;
|
||||
Attr(Symbol name, Value * value, Pos * pos = &noPos)
|
||||
: name(name), value(value), pos(pos) { };
|
||||
Attr() : pos(&noPos) { };
|
||||
bool operator < (const Attr & a) const
|
||||
{
|
||||
return name < a.name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Bindings
|
||||
{
|
||||
public:
|
||||
typedef uint32_t size_t;
|
||||
|
||||
private:
|
||||
size_t size_, capacity_;
|
||||
Attr attrs[0];
|
||||
|
||||
Bindings(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;
|
||||
};
|
||||
|
||||
|
||||
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
|
||||
|
||||
@@ -132,17 +69,19 @@ public:
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||
sFile, sLine, sColumn, sFunctor;
|
||||
sFile, sLine, sColumn, sFunctor, sToString;
|
||||
Symbol sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
bool repair;
|
||||
bool repair = false;
|
||||
|
||||
/* If set, don't allow access to files outside of the Nix search
|
||||
path or to environment variables. */
|
||||
bool restricted;
|
||||
|
||||
Value vEmptySet;
|
||||
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
|
||||
@@ -197,7 +136,7 @@ public:
|
||||
of the evaluation of the thunk. If `v' is a delayed function
|
||||
application, call the function and overwrite `v' with the
|
||||
result. Otherwise, this is a no-op. */
|
||||
inline void forceValue(Value & v);
|
||||
inline void forceValue(Value & v, const Pos & pos = noPos);
|
||||
|
||||
/* Force a value, then recursively force list elements and
|
||||
attributes. */
|
||||
@@ -244,7 +183,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
unsigned int baseEnvDispl;
|
||||
unsigned int baseEnvDispl = 0;
|
||||
|
||||
void createBaseEnv();
|
||||
|
||||
@@ -274,6 +213,8 @@ public:
|
||||
elements and attributes are compared recursively. */
|
||||
bool eqValues(Value & v1, Value & v2);
|
||||
|
||||
bool isFunctor(Value & fun);
|
||||
|
||||
void callFunction(Value & fun, Value & arg, Value & v, const Pos & pos);
|
||||
void callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos);
|
||||
|
||||
@@ -290,7 +231,7 @@ public:
|
||||
Bindings * allocBindings(Bindings::size_t capacity);
|
||||
|
||||
void mkList(Value & v, unsigned int length);
|
||||
void mkAttrs(Value & v, unsigned int expected);
|
||||
void mkAttrs(Value & v, unsigned int capacity);
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
void mkPos(Value & v, Pos * pos);
|
||||
|
||||
@@ -301,17 +242,17 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
unsigned long nrEnvs;
|
||||
unsigned long nrValuesInEnvs;
|
||||
unsigned long nrValues;
|
||||
unsigned long nrListElems;
|
||||
unsigned long nrAttrsets;
|
||||
unsigned long nrAttrsInAttrsets;
|
||||
unsigned long nrOpUpdates;
|
||||
unsigned long nrOpUpdateValuesCopied;
|
||||
unsigned long nrListConcats;
|
||||
unsigned long nrPrimOpCalls;
|
||||
unsigned long nrFunctionCalls;
|
||||
unsigned long nrEnvs = 0;
|
||||
unsigned long nrValuesInEnvs = 0;
|
||||
unsigned long nrValues = 0;
|
||||
unsigned long nrListElems = 0;
|
||||
unsigned long nrAttrsets = 0;
|
||||
unsigned long nrAttrsInAttrsets = 0;
|
||||
unsigned long nrOpUpdates = 0;
|
||||
unsigned long nrOpUpdateValuesCopied = 0;
|
||||
unsigned long nrListConcats = 0;
|
||||
unsigned long nrPrimOpCalls = 0;
|
||||
unsigned long nrFunctionCalls = 0;
|
||||
|
||||
bool countCalls;
|
||||
|
||||
|
||||
+19
-19
@@ -39,9 +39,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs()
|
||||
state->forceList(*i->value, *i->pos);
|
||||
|
||||
/* For each output... */
|
||||
for (unsigned int j = 0; j < i->value->list.length; ++j) {
|
||||
for (unsigned int j = 0; j < i->value->listSize(); ++j) {
|
||||
/* Evaluate the corresponding set. */
|
||||
string name = state->forceStringNoCtx(*i->value->list.elems[j], *i->pos);
|
||||
string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
|
||||
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value);
|
||||
@@ -85,8 +85,8 @@ StringSet DrvInfo::queryMetaNames()
|
||||
{
|
||||
StringSet res;
|
||||
if (!getMeta()) return res;
|
||||
foreach (Bindings::iterator, i, *meta)
|
||||
res.insert(i->name);
|
||||
for (auto & i : *meta)
|
||||
res.insert(i.name);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -94,16 +94,16 @@ StringSet DrvInfo::queryMetaNames()
|
||||
bool DrvInfo::checkMeta(Value & v)
|
||||
{
|
||||
state->forceValue(v);
|
||||
if (v.type == tList) {
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
if (!checkMeta(*v.list.elems[n])) return false;
|
||||
if (v.isList()) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
if (!checkMeta(*v.listElems()[n])) return false;
|
||||
return true;
|
||||
}
|
||||
else if (v.type == tAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(state->sOutPath);
|
||||
if (i != v.attrs->end()) return false;
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
if (!checkMeta(*i->value)) return false;
|
||||
for (auto & i : *v.attrs)
|
||||
if (!checkMeta(*i.value)) return false;
|
||||
return true;
|
||||
}
|
||||
else return v.type == tInt || v.type == tBool || v.type == tString;
|
||||
@@ -255,13 +255,13 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
precedence). */
|
||||
typedef std::map<string, Symbol> SortedSymbols;
|
||||
SortedSymbols attrs;
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
attrs.insert(std::pair<string, Symbol>(i->name, i->name));
|
||||
for (auto & i : *v.attrs)
|
||||
attrs.insert(std::pair<string, Symbol>(i.name, i.name));
|
||||
|
||||
foreach (SortedSymbols::iterator, i, attrs) {
|
||||
startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i->first);
|
||||
string pathPrefix2 = addToPath(pathPrefix, i->first);
|
||||
Value & v2(*v.attrs->find(i->second)->value);
|
||||
for (auto & i : attrs) {
|
||||
startNest(nest, lvlDebug, format("evaluating attribute ‘%1%’") % i.first);
|
||||
string pathPrefix2 = addToPath(pathPrefix, i.first);
|
||||
Value & v2(*v.attrs->find(i.second)->value);
|
||||
if (combineChannels)
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
@@ -277,13 +277,13 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
}
|
||||
}
|
||||
|
||||
else if (v.type == tList) {
|
||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||
else if (v.isList()) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
startNest(nest, lvlDebug,
|
||||
format("evaluating list element"));
|
||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
|
||||
s++;
|
||||
state.mkList(v, values.size());
|
||||
for (size_t n = 0; n < values.size(); ++n)
|
||||
v.list.elems[n] = values[n];
|
||||
v.listElems()[n] = values[n];
|
||||
}
|
||||
|
||||
else if (*s == '{') {
|
||||
|
||||
+30
-33
@@ -1,10 +1,14 @@
|
||||
%option reentrant bison-bridge bison-locations
|
||||
%option noyywrap
|
||||
%option never-interactive
|
||||
%option stack
|
||||
%option nodefault
|
||||
%option nounput noyy_top_state
|
||||
|
||||
|
||||
%x STRING
|
||||
%x IND_STRING
|
||||
%x INSIDE_DOLLAR_CURLY
|
||||
|
||||
|
||||
%{
|
||||
@@ -74,6 +78,9 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
#define YY_USER_INIT initLoc(yylloc)
|
||||
#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng);
|
||||
|
||||
#define PUSH_STATE(state) yy_push_state(state, yyscanner)
|
||||
#define POP_STATE() yy_pop_state(yyscanner)
|
||||
|
||||
%}
|
||||
|
||||
|
||||
@@ -87,6 +94,8 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
|
||||
|
||||
%%
|
||||
|
||||
<INITIAL,INSIDE_DOLLAR_CURLY>{
|
||||
|
||||
|
||||
if { return IF; }
|
||||
then { return THEN; }
|
||||
@@ -118,23 +127,28 @@ or { return OR_KW; }
|
||||
return INT;
|
||||
}
|
||||
|
||||
\$\{ { return DOLLAR_CURLY; }
|
||||
\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
|
||||
\{ { return '{'; }
|
||||
}
|
||||
|
||||
\" { BEGIN(STRING); return '"'; }
|
||||
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
|
||||
/* !!! Not quite right: we want a follow restriction on
|
||||
"$", it shouldn't be followed by a "{". Right now
|
||||
"$\"" will be consumed as part of a string, rather
|
||||
than a "$" followed by the string terminator.
|
||||
Disallow "$\"" for now. */
|
||||
\} { return '}'; }
|
||||
<INSIDE_DOLLAR_CURLY>\} { POP_STATE(); return '}'; }
|
||||
|
||||
<INITIAL,INSIDE_DOLLAR_CURLY>\" { PUSH_STATE(STRING); return '"'; }
|
||||
|
||||
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)*\$/\" |
|
||||
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\.|\$\\.)+ {
|
||||
/* It is impossible to match strings ending with '$' with one
|
||||
regex because trailing contexts are only valid at the end
|
||||
of a rule. (A sane but undocumented limitation.) */
|
||||
yylval->e = unescapeStr(data->symbols, yytext);
|
||||
return STR;
|
||||
}
|
||||
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||
<STRING>\" { BEGIN(INITIAL); return '"'; }
|
||||
<STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
|
||||
<STRING>\" { POP_STATE(); return '"'; }
|
||||
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
|
||||
<INITIAL,INSIDE_DOLLAR_CURLY>\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
|
||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||
yylval->e = new ExprIndStr(yytext);
|
||||
return IND_STR;
|
||||
@@ -151,14 +165,16 @@ or { return OR_KW; }
|
||||
yylval->e = unescapeStr(data->symbols, yytext + 2);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
||||
<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
|
||||
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
|
||||
<IND_STRING>\' {
|
||||
yylval->e = new ExprIndStr("'");
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
<INITIAL,INSIDE_DOLLAR_CURLY>{
|
||||
|
||||
{PATH} { yylval->path = strdup(yytext); return PATH; }
|
||||
{HPATH} { yylval->path = strdup(yytext); return HPATH; }
|
||||
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
|
||||
@@ -170,26 +186,7 @@ or { return OR_KW; }
|
||||
|
||||
. return yytext[0];
|
||||
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,9 +6,14 @@ libexpr_DIR := $(d)
|
||||
|
||||
libexpr_SOURCES := $(wildcard $(d)/*.cc) $(d)/lexer-tab.cc $(d)/parser-tab.cc
|
||||
|
||||
libexpr_CXXFLAGS := -Wno-deprecated-register
|
||||
|
||||
libexpr_LIBS = libutil libstore libformat
|
||||
|
||||
libexpr_LDFLAGS = -ldl -lcurl
|
||||
libexpr_LDFLAGS =
|
||||
ifneq ($(OS), FreeBSD)
|
||||
libexpr_LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
# The dependency on libgc must be propagated (i.e. meaning that
|
||||
# programs/libraries that use libexpr must explicitly pass -lgc),
|
||||
|
||||
@@ -98,8 +98,8 @@ int compareVersions(const string & v1, const string & v2)
|
||||
DrvNames drvNamesFromArgs(const Strings & opArgs)
|
||||
{
|
||||
DrvNames result;
|
||||
foreach (Strings::const_iterator, i, opArgs)
|
||||
result.push_back(DrvName(*i));
|
||||
for (auto & i : opArgs)
|
||||
result.push_back(DrvName(i));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+49
-48
@@ -30,8 +30,9 @@ static void showString(std::ostream & str, const string & s)
|
||||
|
||||
static void showId(std::ostream & str, const string & s)
|
||||
{
|
||||
assert(!s.empty());
|
||||
if (s == "if")
|
||||
if (s.empty())
|
||||
str << "\"\"";
|
||||
else if (s == "if") // FIXME: handle other keywords
|
||||
str << '"' << s << '"';
|
||||
else {
|
||||
char c = s[0];
|
||||
@@ -97,21 +98,21 @@ void ExprAttrs::show(std::ostream & str)
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited)
|
||||
str << "inherit " << i->first << " " << "; ";
|
||||
for (auto & i : attrs)
|
||||
if (i.second.inherited)
|
||||
str << "inherit " << i.first << " " << "; ";
|
||||
else
|
||||
str << i->first << " = " << *i->second.e << "; ";
|
||||
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs)
|
||||
str << "\"${" << *i->nameExpr << "}\" = " << *i->valueExpr << "; ";
|
||||
str << i.first << " = " << *i.second.e << "; ";
|
||||
for (auto & i : dynamicAttrs)
|
||||
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
|
||||
str << "}";
|
||||
}
|
||||
|
||||
void ExprList::show(std::ostream & str)
|
||||
{
|
||||
str << "[ ";
|
||||
foreach (vector<Expr *>::iterator, i, elems)
|
||||
str << "(" << **i << ") ";
|
||||
for (auto & i : elems)
|
||||
str << "(" << *i << ") ";
|
||||
str << "]";
|
||||
}
|
||||
|
||||
@@ -121,10 +122,10 @@ void ExprLambda::show(std::ostream & str)
|
||||
if (matchAttrs) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
foreach (Formals::Formals_::iterator, i, formals->formals) {
|
||||
for (auto & i : formals->formals) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << i->name;
|
||||
if (i->def) str << " ? " << *i->def;
|
||||
str << i.name;
|
||||
if (i.def) str << " ? " << *i.def;
|
||||
}
|
||||
if (formals->ellipsis) {
|
||||
if (!first) str << ", ";
|
||||
@@ -140,12 +141,12 @@ void ExprLambda::show(std::ostream & str)
|
||||
void ExprLet::show(std::ostream & str)
|
||||
{
|
||||
str << "(let ";
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
if (i->second.inherited) {
|
||||
str << "inherit " << i->first << "; ";
|
||||
for (auto & i : attrs->attrs)
|
||||
if (i.second.inherited) {
|
||||
str << "inherit " << i.first << "; ";
|
||||
}
|
||||
else
|
||||
str << i->first << " = " << *i->second.e << "; ";
|
||||
str << i.first << " = " << *i.second.e << "; ";
|
||||
str << "in " << *body << ")";
|
||||
}
|
||||
|
||||
@@ -173,9 +174,9 @@ void ExprConcatStrings::show(std::ostream & str)
|
||||
{
|
||||
bool first = true;
|
||||
str << "(";
|
||||
foreach (vector<Expr *>::iterator, i, *es) {
|
||||
for (auto & i : *es) {
|
||||
if (first) first = false; else str << " + ";
|
||||
str << **i;
|
||||
str << *i;
|
||||
}
|
||||
str << ")";
|
||||
}
|
||||
@@ -267,17 +268,17 @@ void ExprSelect::bindVars(const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
if (def) def->bindVars(env);
|
||||
foreach (AttrPath::iterator, i, attrPath)
|
||||
if (!i->symbol.set())
|
||||
i->expr->bindVars(env);
|
||||
for (auto & i : attrPath)
|
||||
if (!i.symbol.set())
|
||||
i.expr->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
foreach (AttrPath::iterator, i, attrPath)
|
||||
if (!i->symbol.set())
|
||||
i->expr->bindVars(env);
|
||||
for (auto & i : attrPath)
|
||||
if (!i.symbol.set())
|
||||
i.expr->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprAttrs::bindVars(const StaticEnv & env)
|
||||
@@ -289,27 +290,27 @@ void ExprAttrs::bindVars(const StaticEnv & env)
|
||||
dynamicEnv = &newEnv;
|
||||
|
||||
unsigned int displ = 0;
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
||||
for (auto & i : attrs)
|
||||
newEnv.vars[i.first] = i.second.displ = displ++;
|
||||
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
||||
}
|
||||
|
||||
else
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
i->second.e->bindVars(env);
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(env);
|
||||
|
||||
foreach (DynamicAttrDefs::iterator, i, dynamicAttrs) {
|
||||
i->nameExpr->bindVars(*dynamicEnv);
|
||||
i->valueExpr->bindVars(*dynamicEnv);
|
||||
for (auto & i : dynamicAttrs) {
|
||||
i.nameExpr->bindVars(*dynamicEnv);
|
||||
i.valueExpr->bindVars(*dynamicEnv);
|
||||
}
|
||||
}
|
||||
|
||||
void ExprList::bindVars(const StaticEnv & env)
|
||||
{
|
||||
foreach (vector<Expr *>::iterator, i, elems)
|
||||
(*i)->bindVars(env);
|
||||
for (auto & i : elems)
|
||||
i->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
@@ -321,11 +322,11 @@ void ExprLambda::bindVars(const StaticEnv & env)
|
||||
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||
|
||||
if (matchAttrs) {
|
||||
foreach (Formals::Formals_::iterator, i, formals->formals)
|
||||
newEnv.vars[i->name] = displ++;
|
||||
for (auto & i : formals->formals)
|
||||
newEnv.vars[i.name] = displ++;
|
||||
|
||||
foreach (Formals::Formals_::iterator, i, formals->formals)
|
||||
if (i->def) i->def->bindVars(newEnv);
|
||||
for (auto & i : formals->formals)
|
||||
if (i.def) i.def->bindVars(newEnv);
|
||||
}
|
||||
|
||||
body->bindVars(newEnv);
|
||||
@@ -336,11 +337,11 @@ void ExprLet::bindVars(const StaticEnv & env)
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
unsigned int displ = 0;
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
||||
for (auto & i : attrs->attrs)
|
||||
newEnv.vars[i.first] = i.second.displ = displ++;
|
||||
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
||||
for (auto & i : attrs->attrs)
|
||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
||||
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
@@ -384,8 +385,8 @@ void ExprOpNot::bindVars(const StaticEnv & env)
|
||||
|
||||
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
{
|
||||
foreach (vector<Expr *>::iterator, i, *es)
|
||||
(*i)->bindVars(env);
|
||||
for (auto & i : *es)
|
||||
i->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprPos::bindVars(const StaticEnv & env)
|
||||
@@ -419,8 +420,8 @@ string ExprLambda::showNamePos() const
|
||||
size_t SymbolTable::totalSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
foreach (Symbols::const_iterator, i, symbols)
|
||||
n += i->size();
|
||||
for (auto & i : symbols)
|
||||
n += i.size();
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
+14
-25
@@ -136,8 +136,8 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||
unsigned int minIndent = 1000000;
|
||||
unsigned int curIndent = 0;
|
||||
foreach (vector<Expr *>::iterator, i, es) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
||||
for (auto & i : es) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
|
||||
if (!e) {
|
||||
/* Anti-quotations end the current start-of-line whitespace. */
|
||||
if (atStartOfLine) {
|
||||
@@ -216,10 +216,6 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||
}
|
||||
|
||||
|
||||
void backToString(yyscan_t scanner);
|
||||
void backToIndString(yyscan_t scanner);
|
||||
|
||||
|
||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||
{
|
||||
return Pos(data->path, loc.first_line, loc.first_column);
|
||||
@@ -404,25 +400,18 @@ string_parts
|
||||
|
||||
string_parts_interpolated
|
||||
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
|
||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
|
||||
| STR DOLLAR_CURLY expr '}'
|
||||
{
|
||||
backToString(scanner);
|
||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
||||
| DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
|
||||
| STR DOLLAR_CURLY expr '}' {
|
||||
$$ = new vector<Expr *>;
|
||||
$$->push_back($1);
|
||||
$$->push_back($3);
|
||||
}
|
||||
| DOLLAR_CURLY expr '}'
|
||||
{
|
||||
backToString(scanner);
|
||||
$$ = new vector<Expr *>;
|
||||
$$->push_back($2);
|
||||
}
|
||||
;
|
||||
|
||||
ind_string_parts
|
||||
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
||||
| { $$ = new vector<Expr *>; }
|
||||
;
|
||||
|
||||
@@ -430,20 +419,20 @@ binds
|
||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
||||
| binds INHERIT attrs ';'
|
||||
{ $$ = $1;
|
||||
foreach (AttrPath::iterator, i, *$3) {
|
||||
if ($$->attrs.find(i->symbol) != $$->attrs.end())
|
||||
dupAttr(i->symbol, makeCurPos(@3, data), $$->attrs[i->symbol].pos);
|
||||
for (auto & i : *$3) {
|
||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
||||
Pos pos = makeCurPos(@3, data);
|
||||
$$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i->symbol), pos, true);
|
||||
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
{ $$ = $1;
|
||||
/* !!! Should ensure sharing of the expression in $4. */
|
||||
foreach (AttrPath::iterator, i, *$6) {
|
||||
if ($$->attrs.find(i->symbol) != $$->attrs.end())
|
||||
dupAttr(i->symbol, makeCurPos(@6, data), $$->attrs[i->symbol].pos);
|
||||
$$->attrs[i->symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i->symbol), makeCurPos(@6, data));
|
||||
for (auto & i : *$6) {
|
||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||
dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
||||
$$->attrs[i.symbol] = ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data));
|
||||
}
|
||||
}
|
||||
| { $$ = new ExprAttrs; }
|
||||
|
||||
+274
-104
@@ -86,9 +86,11 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
|
||||
Derivation drv = readDerivation(path);
|
||||
Value & w = *state.allocValue();
|
||||
state.mkAttrs(w, 2 + drv.outputs.size());
|
||||
state.mkAttrs(w, 3 + drv.outputs.size());
|
||||
Value * v2 = state.allocAttr(w, state.sDrvPath);
|
||||
mkString(*v2, path, singleton<PathSet>("=" + path));
|
||||
v2 = state.allocAttr(w, state.sName);
|
||||
mkString(*v2, drv.env["name"]);
|
||||
Value * outputsVal =
|
||||
state.allocAttr(w, state.symbols.create("outputs"));
|
||||
state.mkList(*outputsVal, drv.outputs.size());
|
||||
@@ -98,8 +100,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
v2 = state.allocAttr(w, state.symbols.create(o.first));
|
||||
mkString(*v2, o.second.path,
|
||||
singleton<PathSet>("!" + o.first + "!" + path));
|
||||
outputsVal->list.elems[outputs_index] = state.allocValue();
|
||||
mkString(*(outputsVal->list.elems[outputs_index++]), o.first);
|
||||
outputsVal->listElems()[outputs_index] = state.allocValue();
|
||||
mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
|
||||
}
|
||||
w.attrs->sort();
|
||||
Value fun;
|
||||
@@ -186,7 +188,7 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
case tPath: t = "path"; break;
|
||||
case tNull: t = "null"; break;
|
||||
case tAttrs: t = "set"; break;
|
||||
case tList: t = "list"; break;
|
||||
case tList1: case tList2: case tListN: t = "list"; break;
|
||||
case tLambda:
|
||||
case tPrimOp:
|
||||
case tPrimOpApp:
|
||||
@@ -246,7 +248,7 @@ struct CompareValues
|
||||
bool operator () (const Value * v1, const Value * v2) const
|
||||
{
|
||||
if (v1->type != v2->type)
|
||||
throw EvalError("cannot compare values of different types");
|
||||
throw EvalError(format("cannot compare %1% with %2%") % showType(*v1) % showType(*v2));
|
||||
switch (v1->type) {
|
||||
case tInt:
|
||||
return v1->integer < v2->integer;
|
||||
@@ -282,8 +284,8 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
state.forceList(*startSet->value, pos);
|
||||
|
||||
ValueList workSet;
|
||||
for (unsigned int n = 0; n < startSet->value->list.length; ++n)
|
||||
workSet.push_back(startSet->value->list.elems[n]);
|
||||
for (unsigned int n = 0; n < startSet->value->listSize(); ++n)
|
||||
workSet.push_back(startSet->value->listElems()[n]);
|
||||
|
||||
/* Get the operator. */
|
||||
Bindings::iterator op =
|
||||
@@ -321,17 +323,17 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
state.forceList(call, pos);
|
||||
|
||||
/* Add the values returned by the operator to the work set. */
|
||||
for (unsigned int n = 0; n < call.list.length; ++n) {
|
||||
state.forceValue(*call.list.elems[n]);
|
||||
workSet.push_back(call.list.elems[n]);
|
||||
for (unsigned int n = 0; n < call.listSize(); ++n) {
|
||||
state.forceValue(*call.listElems()[n]);
|
||||
workSet.push_back(call.listElems()[n]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the result list. */
|
||||
state.mkList(v, res.size());
|
||||
unsigned int n = 0;
|
||||
foreach (ValueList::iterator, i, res)
|
||||
v.list.elems[n++] = *i;
|
||||
for (auto & i : res)
|
||||
v.listElems()[n++] = i;
|
||||
}
|
||||
|
||||
|
||||
@@ -477,24 +479,24 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
StringSet outputs;
|
||||
outputs.insert("out");
|
||||
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
if (i->name == state.sIgnoreNulls) continue;
|
||||
string key = i->name;
|
||||
for (auto & i : *args[0]->attrs) {
|
||||
if (i.name == state.sIgnoreNulls) continue;
|
||||
string key = i.name;
|
||||
startNest(nest, lvlVomit, format("processing attribute ‘%1%’") % key);
|
||||
|
||||
try {
|
||||
|
||||
if (ignoreNulls) {
|
||||
state.forceValue(*i->value);
|
||||
if (i->value->type == tNull) continue;
|
||||
state.forceValue(*i.value);
|
||||
if (i.value->type == tNull) continue;
|
||||
}
|
||||
|
||||
/* The `args' attribute is special: it supplies the
|
||||
command-line arguments to the builder. */
|
||||
if (key == "args") {
|
||||
state.forceList(*i->value, pos);
|
||||
for (unsigned int n = 0; n < i->value->list.length; ++n) {
|
||||
string s = state.coerceToString(posDrvName, *i->value->list.elems[n], context, true);
|
||||
state.forceList(*i.value, pos);
|
||||
for (unsigned int n = 0; n < i.value->listSize(); ++n) {
|
||||
string s = state.coerceToString(posDrvName, *i.value->listElems()[n], context, true);
|
||||
drv.args.push_back(s);
|
||||
}
|
||||
}
|
||||
@@ -502,11 +504,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
/* All other attributes are passed to the builder through
|
||||
the environment. */
|
||||
else {
|
||||
string s = state.coerceToString(posDrvName, *i->value, context, true);
|
||||
string s = state.coerceToString(posDrvName, *i.value, context, true);
|
||||
drv.env[key] = s;
|
||||
if (key == "builder") drv.builder = s;
|
||||
else if (i->name == state.sSystem) drv.platform = s;
|
||||
else if (i->name == state.sName) {
|
||||
else if (i.name == state.sSystem) drv.platform = s;
|
||||
else if (i.name == state.sName) {
|
||||
drvName = s;
|
||||
printMsg(lvlVomit, format("derivation name is ‘%1%’") % drvName);
|
||||
}
|
||||
@@ -520,17 +522,17 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
else if (key == "outputs") {
|
||||
Strings tmp = tokenizeString<Strings>(s);
|
||||
outputs.clear();
|
||||
foreach (Strings::iterator, j, tmp) {
|
||||
if (outputs.find(*j) != outputs.end())
|
||||
throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % *j % posDrvName);
|
||||
/* !!! Check whether *j is a valid attribute
|
||||
for (auto & j : tmp) {
|
||||
if (outputs.find(j) != outputs.end())
|
||||
throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % j % posDrvName);
|
||||
/* !!! Check whether j is a valid attribute
|
||||
name. */
|
||||
/* Derivations cannot be named ‘drv’, because
|
||||
then we'd have an attribute ‘drvPath’ in
|
||||
the resulting set. */
|
||||
if (*j == "drv")
|
||||
if (j == "drv")
|
||||
throw EvalError(format("invalid derivation output name ‘drv’, at %1%") % posDrvName);
|
||||
outputs.insert(*j);
|
||||
outputs.insert(j);
|
||||
}
|
||||
if (outputs.empty())
|
||||
throw EvalError(format("derivation cannot have an empty set of outputs, at %1%") % posDrvName);
|
||||
@@ -547,8 +549,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
/* Everything in the context of the strings in the derivation
|
||||
attributes should be added as dependencies of the resulting
|
||||
derivation. */
|
||||
foreach (PathSet::iterator, i, context) {
|
||||
Path path = *i;
|
||||
for (auto & path : context) {
|
||||
|
||||
/* Paths marked with `=' denote that the path of a derivation
|
||||
is explicitly passed to the builder. Since that allows the
|
||||
@@ -560,10 +561,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
if (path.at(0) == '=') {
|
||||
/* !!! This doesn't work if readOnlyMode is set. */
|
||||
PathSet refs; computeFSClosure(*store, string(path, 1), refs);
|
||||
foreach (PathSet::iterator, j, refs) {
|
||||
drv.inputSrcs.insert(*j);
|
||||
if (isDerivation(*j))
|
||||
drv.inputDrvs[*j] = store->queryDerivationOutputNames(*j);
|
||||
for (auto & j : refs) {
|
||||
drv.inputSrcs.insert(j);
|
||||
if (isDerivation(j))
|
||||
drv.inputDrvs[j] = store->queryDerivationOutputNames(j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,20 +623,20 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
are empty, and the corresponding environment variables have
|
||||
an empty value. This ensures that changes in the set of
|
||||
output names do get reflected in the hash. */
|
||||
foreach (StringSet::iterator, i, outputs) {
|
||||
drv.env[*i] = "";
|
||||
drv.outputs[*i] = DerivationOutput("", "", "");
|
||||
for (auto & i : outputs) {
|
||||
drv.env[i] = "";
|
||||
drv.outputs[i] = DerivationOutput("", "", "");
|
||||
}
|
||||
|
||||
/* Use the masked derivation expression to compute the output
|
||||
path. */
|
||||
Hash h = hashDerivationModulo(*store, drv);
|
||||
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
if (i->second.path == "") {
|
||||
Path outPath = makeOutputPath(i->first, h, drvName);
|
||||
drv.env[i->first] = outPath;
|
||||
i->second.path = outPath;
|
||||
for (auto & i : drv.outputs)
|
||||
if (i.second.path == "") {
|
||||
Path outPath = makeOutputPath(i.first, h, drvName);
|
||||
drv.env[i.first] = outPath;
|
||||
i.second.path = outPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,9 +653,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
state.mkAttrs(v, 1 + drv.outputs.size());
|
||||
mkString(*state.allocAttr(v, state.sDrvPath), drvPath, singleton<PathSet>("=" + drvPath));
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
mkString(*state.allocAttr(v, state.symbols.create(i->first)),
|
||||
i->second.path, singleton<PathSet>("!" + i->first + "!" + drvPath));
|
||||
for (auto & i : drv.outputs) {
|
||||
mkString(*state.allocAttr(v, state.symbols.create(i.first)),
|
||||
i.second.path, singleton<PathSet>("!" + i.first + "!" + drvPath));
|
||||
}
|
||||
v.attrs->sort();
|
||||
}
|
||||
@@ -765,8 +766,8 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
SearchPath searchPath;
|
||||
|
||||
PathSet context;
|
||||
for (unsigned int n = 0; n < args[0]->list.length; ++n) {
|
||||
Value & v2(*args[0]->list.elems[n]);
|
||||
for (unsigned int n = 0; n < args[0]->listSize(); ++n) {
|
||||
Value & v2(*args[0]->listElems()[n]);
|
||||
state.forceAttrs(v2, pos);
|
||||
|
||||
string prefix;
|
||||
@@ -871,11 +872,14 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
|
||||
PathSet refs;
|
||||
|
||||
foreach (PathSet::iterator, i, context) {
|
||||
Path path = *i;
|
||||
for (auto path : context) {
|
||||
if (path.at(0) == '=') path = string(path, 1);
|
||||
if (isDerivation(path))
|
||||
throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
|
||||
if (isDerivation(path)) {
|
||||
/* See prim_unsafeDiscardOutputDependency. */
|
||||
if (path.at(0) != '~')
|
||||
throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
|
||||
path = string(path, 1);
|
||||
}
|
||||
refs.insert(path);
|
||||
}
|
||||
|
||||
@@ -968,9 +972,9 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
|
||||
|
||||
unsigned int n = 0;
|
||||
for (auto & i : *args[0]->attrs)
|
||||
mkString(*(v.list.elems[n++] = state.allocValue()), i.name);
|
||||
mkString(*(v.listElems()[n++] = state.allocValue()), i.name);
|
||||
|
||||
std::sort(v.list.elems, v.list.elems + n,
|
||||
std::sort(v.listElems(), v.listElems() + n,
|
||||
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
|
||||
}
|
||||
|
||||
@@ -985,13 +989,13 @@ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args,
|
||||
|
||||
unsigned int n = 0;
|
||||
for (auto & i : *args[0]->attrs)
|
||||
v.list.elems[n++] = (Value *) &i;
|
||||
v.listElems()[n++] = (Value *) &i;
|
||||
|
||||
std::sort(v.list.elems, v.list.elems + n,
|
||||
std::sort(v.listElems(), v.listElems() + n,
|
||||
[](Value * v1, Value * v2) { return (string) ((Attr *) v1)->name < (string) ((Attr *) v2)->name; });
|
||||
|
||||
for (unsigned int i = 0; i < n; ++i)
|
||||
v.list.elems[i] = ((Attr *) v.list.elems[i])->value;
|
||||
v.listElems()[i] = ((Attr *) v.listElems()[i])->value;
|
||||
}
|
||||
|
||||
|
||||
@@ -1048,18 +1052,18 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||
|
||||
/* Get the attribute names to be removed. */
|
||||
std::set<Symbol> names;
|
||||
for (unsigned int i = 0; i < args[1]->list.length; ++i) {
|
||||
state.forceStringNoCtx(*args[1]->list.elems[i], pos);
|
||||
names.insert(state.symbols.create(args[1]->list.elems[i]->string.s));
|
||||
for (unsigned int i = 0; i < args[1]->listSize(); ++i) {
|
||||
state.forceStringNoCtx(*args[1]->listElems()[i], pos);
|
||||
names.insert(state.symbols.create(args[1]->listElems()[i]->string.s));
|
||||
}
|
||||
|
||||
/* Copy all attributes not in that set. Note that we don't need
|
||||
to sort v.attrs because it's a subset of an already sorted
|
||||
vector. */
|
||||
state.mkAttrs(v, args[0]->attrs->size());
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
if (names.find(i->name) == names.end())
|
||||
v.attrs->push_back(*i);
|
||||
for (auto & i : *args[0]->attrs) {
|
||||
if (names.find(i.name) == names.end())
|
||||
v.attrs->push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,12 +1077,12 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
|
||||
state.mkAttrs(v, args[0]->list.length);
|
||||
state.mkAttrs(v, args[0]->listSize());
|
||||
|
||||
std::set<Symbol> seen;
|
||||
|
||||
for (unsigned int i = 0; i < args[0]->list.length; ++i) {
|
||||
Value & v2(*args[0]->list.elems[i]);
|
||||
for (unsigned int i = 0; i < args[0]->listSize(); ++i) {
|
||||
Value & v2(*args[0]->listElems()[i]);
|
||||
state.forceAttrs(v2, pos);
|
||||
|
||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||
@@ -1111,8 +1115,8 @@ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * ar
|
||||
|
||||
state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
|
||||
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
Bindings::iterator j = args[1]->attrs->find(i->name);
|
||||
for (auto & i : *args[0]->attrs) {
|
||||
Bindings::iterator j = args[1]->attrs->find(i.name);
|
||||
if (j != args[1]->attrs->end())
|
||||
v.attrs->push_back(*j);
|
||||
}
|
||||
@@ -1131,11 +1135,11 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos));
|
||||
state.forceList(*args[1], pos);
|
||||
|
||||
Value * res[args[1]->list.length];
|
||||
Value * res[args[1]->listSize()];
|
||||
unsigned int found = 0;
|
||||
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
|
||||
Value & v2(*args[1]->list.elems[n]);
|
||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
||||
Value & v2(*args[1]->listElems()[n]);
|
||||
state.forceAttrs(v2, pos);
|
||||
Bindings::iterator i = v2.attrs->find(attrName);
|
||||
if (i != v2.attrs->end())
|
||||
@@ -1144,13 +1148,13 @@ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
|
||||
state.mkList(v, found);
|
||||
for (unsigned int n = 0; n < found; ++n)
|
||||
v.list.elems[n] = res[n];
|
||||
v.listElems()[n] = res[n];
|
||||
}
|
||||
|
||||
|
||||
/* Return a set containing the names of the formal arguments expected
|
||||
by the function `f'. The value of each attribute is a Boolean
|
||||
denoting whether has a default value. For instance,
|
||||
denoting whether the corresponding argument has a default value. For instance,
|
||||
|
||||
functionArgs ({ x, y ? 123}: ...)
|
||||
=> { x = false; y = true; }
|
||||
@@ -1173,9 +1177,9 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
|
||||
}
|
||||
|
||||
state.mkAttrs(v, args[0]->lambda.fun->formals->formals.size());
|
||||
foreach (Formals::Formals_::iterator, i, args[0]->lambda.fun->formals->formals)
|
||||
for (auto & i : args[0]->lambda.fun->formals->formals)
|
||||
// !!! should optimise booleans (allocate only once)
|
||||
mkBool(*state.allocAttr(v, i->name), i->def);
|
||||
mkBool(*state.allocAttr(v, i.name), i.def);
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
||||
@@ -1189,17 +1193,17 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
|
||||
static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
mkBool(v, args[0]->type == tList);
|
||||
mkBool(v, args[0]->isList());
|
||||
}
|
||||
|
||||
|
||||
static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
|
||||
{
|
||||
state.forceList(list, pos);
|
||||
if (n < 0 || (unsigned int) n >= list.list.length)
|
||||
if (n < 0 || (unsigned int) n >= list.listSize())
|
||||
throw Error(format("list index %1% is out of bounds, at %2%") % n % pos);
|
||||
state.forceValue(*list.list.elems[n]);
|
||||
v = *list.list.elems[n];
|
||||
state.forceValue(*list.listElems()[n]);
|
||||
v = *list.listElems()[n];
|
||||
}
|
||||
|
||||
|
||||
@@ -1223,11 +1227,11 @@ static void prim_head(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
if (args[0]->list.length == 0)
|
||||
if (args[0]->listSize() == 0)
|
||||
throw Error(format("‘tail’ called on an empty list, at %1%") % pos);
|
||||
state.mkList(v, args[0]->list.length - 1);
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
v.list.elems[n] = args[0]->list.elems[n + 1];
|
||||
state.mkList(v, args[0]->listSize() - 1);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
v.listElems()[n] = args[0]->listElems()[n + 1];
|
||||
}
|
||||
|
||||
|
||||
@@ -1237,11 +1241,11 @@ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value &
|
||||
state.forceFunction(*args[0], pos);
|
||||
state.forceList(*args[1], pos);
|
||||
|
||||
state.mkList(v, args[1]->list.length);
|
||||
state.mkList(v, args[1]->listSize());
|
||||
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
mkApp(*(v.list.elems[n] = state.allocValue()),
|
||||
*args[0], *args[1]->list.elems[n]);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
mkApp(*(v.listElems()[n] = state.allocValue()),
|
||||
*args[0], *args[1]->listElems()[n]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1254,15 +1258,15 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
state.forceList(*args[1], pos);
|
||||
|
||||
// FIXME: putting this on the stack is risky.
|
||||
Value * vs[args[1]->list.length];
|
||||
Value * vs[args[1]->listSize()];
|
||||
unsigned int k = 0;
|
||||
|
||||
bool same = true;
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
|
||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
||||
Value res;
|
||||
state.callFunction(*args[0], *args[1]->list.elems[n], res, noPos);
|
||||
state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
|
||||
if (state.forceBool(res))
|
||||
vs[k++] = args[1]->list.elems[n];
|
||||
vs[k++] = args[1]->listElems()[n];
|
||||
else
|
||||
same = false;
|
||||
}
|
||||
@@ -1271,7 +1275,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
v = *args[1];
|
||||
else {
|
||||
state.mkList(v, k);
|
||||
for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n];
|
||||
for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1281,8 +1285,8 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
{
|
||||
bool res = false;
|
||||
state.forceList(*args[1], pos);
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n)
|
||||
if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
|
||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
|
||||
if (state.eqValues(*args[0], *args[1]->listElems()[n])) {
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
@@ -1294,7 +1298,7 @@ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
state.concatLists(v, args[0]->list.length, args[0]->list.elems, pos);
|
||||
state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1302,7 +1306,114 @@ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args,
|
||||
static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
mkInt(v, args[0]->list.length);
|
||||
mkInt(v, args[0]->listSize());
|
||||
}
|
||||
|
||||
|
||||
/* Reduce a list by applying a binary operator, from left to
|
||||
right. The operator is applied strictly. */
|
||||
static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceFunction(*args[0], pos);
|
||||
state.forceList(*args[2], pos);
|
||||
|
||||
Value * vCur = args[1];
|
||||
|
||||
if (args[2]->listSize())
|
||||
for (unsigned int n = 0; n < args[2]->listSize(); ++n) {
|
||||
Value vTmp;
|
||||
state.callFunction(*args[0], *vCur, vTmp, pos);
|
||||
vCur = n == args[2]->listSize() - 1 ? &v : state.allocValue();
|
||||
state.callFunction(vTmp, *args[2]->listElems()[n], *vCur, pos);
|
||||
}
|
||||
else
|
||||
v = *vCur;
|
||||
|
||||
state.forceValue(v);
|
||||
}
|
||||
|
||||
|
||||
static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceFunction(*args[0], pos);
|
||||
state.forceList(*args[1], pos);
|
||||
|
||||
Value vTmp;
|
||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
||||
state.callFunction(*args[0], *args[1]->listElems()[n], vTmp, pos);
|
||||
bool res = state.forceBool(vTmp);
|
||||
if (res == any) {
|
||||
mkBool(v, any);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mkBool(v, !any);
|
||||
}
|
||||
|
||||
|
||||
static void prim_any(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
anyOrAll(true, state, pos, args, v);
|
||||
}
|
||||
|
||||
|
||||
static void prim_all(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
anyOrAll(false, state, pos, args, v);
|
||||
}
|
||||
|
||||
|
||||
static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceFunction(*args[0], pos);
|
||||
auto len = state.forceInt(*args[1], pos);
|
||||
|
||||
if (len < 0)
|
||||
throw EvalError(format("cannot create list of size %1%, at %2%") % len % pos);
|
||||
|
||||
state.mkList(v, len);
|
||||
|
||||
for (unsigned int n = 0; n < (unsigned int) 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1407,11 +1518,8 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po
|
||||
string s = state.coerceToString(pos, *args[0], context);
|
||||
|
||||
PathSet context2;
|
||||
foreach (PathSet::iterator, i, context) {
|
||||
Path p = *i;
|
||||
if (p.at(0) == '=') p = "~" + string(p, 1);
|
||||
context2.insert(p);
|
||||
}
|
||||
for (auto & p : context)
|
||||
context2.insert(p.at(0) == '=' ? "~" + string(p, 1) : p);
|
||||
|
||||
mkString(v, s, context2);
|
||||
}
|
||||
@@ -1452,13 +1560,68 @@ static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
for (unsigned int n = 0; n < len; ++n) {
|
||||
auto i = subs.find(n);
|
||||
if (i == subs.end())
|
||||
mkNull(*(v.list.elems[n] = state.allocValue()));
|
||||
mkNull(*(v.listElems()[n] = state.allocValue()));
|
||||
else
|
||||
mkString(*(v.list.elems[n] = state.allocValue()), i->second);
|
||||
mkString(*(v.listElems()[n] = state.allocValue()), i->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prim_concatStringSep(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
|
||||
auto sep = state.forceString(*args[0], context, pos);
|
||||
state.forceList(*args[1], pos);
|
||||
|
||||
string res;
|
||||
res.reserve((args[1]->listSize() + 32) * sep.size());
|
||||
bool first = true;
|
||||
|
||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
|
||||
if (first) first = false; else res += sep;
|
||||
res += state.coerceToString(pos, *args[1]->listElems()[n], context);
|
||||
}
|
||||
|
||||
mkString(v, res, context);
|
||||
}
|
||||
|
||||
|
||||
static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
state.forceList(*args[1], pos);
|
||||
if (args[0]->listSize() != args[1]->listSize())
|
||||
throw EvalError(format("‘from’ and ‘to’ arguments to ‘replaceStrings’ have different lengths, at %1%") % pos);
|
||||
|
||||
Strings from;
|
||||
for (unsigned int n = 0; n < args[0]->listSize(); ++n)
|
||||
from.push_back(state.forceStringNoCtx(*args[0]->listElems()[n], pos));
|
||||
|
||||
Strings to;
|
||||
for (unsigned int n = 0; n < args[1]->listSize(); ++n)
|
||||
to.push_back(state.forceStringNoCtx(*args[1]->listElems()[n], pos));
|
||||
|
||||
PathSet context;
|
||||
auto s = state.forceString(*args[2], context, pos);
|
||||
|
||||
string res;
|
||||
for (size_t p = 0; p < s.size(); ) {
|
||||
bool found = false;
|
||||
for (auto i = from.begin(), j = to.begin(); i != from.end(); ++i, ++j)
|
||||
if (s.compare(p, i->size(), *i) == 0) {
|
||||
found = true;
|
||||
p += i->size();
|
||||
res += *j;
|
||||
break;
|
||||
}
|
||||
if (!found) res += s[p++];
|
||||
}
|
||||
|
||||
mkString(v, res, context);
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Versions
|
||||
*************************************************************/
|
||||
@@ -1646,6 +1809,11 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__elem", 2, prim_elem);
|
||||
addPrimOp("__concatLists", 1, prim_concatLists);
|
||||
addPrimOp("__length", 1, prim_length);
|
||||
addPrimOp("__foldl'", 3, prim_foldlStrict);
|
||||
addPrimOp("__any", 2, prim_any);
|
||||
addPrimOp("__all", 2, prim_all);
|
||||
addPrimOp("__genList", 2, prim_genList);
|
||||
addPrimOp("__sort", 2, prim_sort);
|
||||
|
||||
// Integer arithmetic
|
||||
addPrimOp("__add", 2, prim_add);
|
||||
@@ -1662,6 +1830,8 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||
addPrimOp("__hashString", 2, prim_hashString);
|
||||
addPrimOp("__match", 2, prim_match);
|
||||
addPrimOp("__concatStringsSep", 2, prim_concatStringSep);
|
||||
addPrimOp("__replaceStrings", 3, prim_replaceStrings);
|
||||
|
||||
// Versions
|
||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||
@@ -1685,7 +1855,7 @@ void EvalState::createBaseEnv()
|
||||
mkList(v, searchPath.size());
|
||||
int n = 0;
|
||||
for (auto & i : searchPath) {
|
||||
v2 = v.list.elems[n++] = allocValue();
|
||||
v2 = v.listElems()[n++] = allocValue();
|
||||
mkAttrs(*v2, 2);
|
||||
mkString(*allocAttr(*v2, symbols.create("path")), i.second);
|
||||
mkString(*allocAttr(*v2, symbols.create("prefix")), i.first);
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace nix {
|
||||
void escapeJSON(std::ostream & str, const string & s)
|
||||
{
|
||||
str << "\"";
|
||||
foreach (string::const_iterator, i, s)
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
else if (*i == '\t') str << "\\t";
|
||||
else if (*i >= 0 && *i < 32)
|
||||
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
|
||||
else str << *i;
|
||||
for (auto & i : s)
|
||||
if (i == '\"' || i == '\\') str << "\\" << i;
|
||||
else if (i == '\n') str << "\\n";
|
||||
else if (i == '\r') str << "\\r";
|
||||
else if (i == '\t') str << "\\t";
|
||||
else if (i >= 0 && i < 32)
|
||||
str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) i << std::dec;
|
||||
else str << i;
|
||||
str << "\"";
|
||||
}
|
||||
|
||||
@@ -59,11 +59,11 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
if (i == v.attrs->end()) {
|
||||
JSONObject json(str);
|
||||
StringSet names;
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
names.insert(i->name);
|
||||
foreach (StringSet::iterator, i, names) {
|
||||
Attr & a(*v.attrs->find(state.symbols.create(*i)));
|
||||
json.attr(*i);
|
||||
for (auto & j : *v.attrs)
|
||||
names.insert(j.name);
|
||||
for (auto & j : names) {
|
||||
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||
json.attr(j);
|
||||
printValueAsJSON(state, strict, *a.value, str, context);
|
||||
}
|
||||
} else
|
||||
@@ -71,16 +71,16 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
break;
|
||||
}
|
||||
|
||||
case tList: {
|
||||
case tList1: case tList2: case tListN: {
|
||||
JSONList json(str);
|
||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
json.elem();
|
||||
printValueAsJSON(state, strict, *v.list.elems[n], str, context);
|
||||
printValueAsJSON(state, strict, *v.listElems()[n], str, context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case tExternal:
|
||||
case tExternal:
|
||||
v.external->printValueAsJSON(state, strict, str, context);
|
||||
break;
|
||||
|
||||
|
||||
+23
-23
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
static XMLAttrs singletonAttrs(const string & name, const string & value)
|
||||
{
|
||||
XMLAttrs attrs;
|
||||
@@ -33,17 +33,17 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
Bindings & attrs, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
|
||||
{
|
||||
StringSet names;
|
||||
|
||||
foreach (Bindings::iterator, i, attrs)
|
||||
names.insert(i->name);
|
||||
|
||||
foreach (StringSet::iterator, i, names) {
|
||||
Attr & a(*attrs.find(state.symbols.create(*i)));
|
||||
|
||||
|
||||
for (auto & i : attrs)
|
||||
names.insert(i.name);
|
||||
|
||||
for (auto & i : names) {
|
||||
Attr & a(*attrs.find(state.symbols.create(i)));
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
xmlAttrs["name"] = *i;
|
||||
xmlAttrs["name"] = i;
|
||||
if (location && a.pos != &noPos) posToXML(xmlAttrs, *a.pos);
|
||||
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
*a.value, doc, context, drvsSeen);
|
||||
@@ -57,7 +57,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
checkInterrupt();
|
||||
|
||||
if (strict) state.forceValue(v);
|
||||
|
||||
|
||||
switch (v.type) {
|
||||
|
||||
case tInt:
|
||||
@@ -85,7 +85,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
case tAttrs:
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
|
||||
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
|
||||
|
||||
Path drvPath;
|
||||
@@ -95,7 +95,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
if (a->value->type == tString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
||||
}
|
||||
|
||||
|
||||
a = v.attrs->find(state.sOutPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value);
|
||||
@@ -116,13 +116,13 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
XMLOpenElement _(doc, "attrs");
|
||||
showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case tList: {
|
||||
case tList1: case tList2: case tListN: {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
printValueAsXML(state, strict, location, *v.list.elems[n], doc, context, drvsSeen);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -130,17 +130,17 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
|
||||
if (v.lambda.fun->matchAttrs) {
|
||||
XMLAttrs attrs;
|
||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
foreach (Formals::Formals_::iterator, i, v.lambda.fun->formals->formals)
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", i->name));
|
||||
for (auto & i : v.lambda.fun->formals->formals)
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -166,9 +166,9 @@ void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
{
|
||||
XMLWriter doc(true, out);
|
||||
XMLOpenElement root(doc, "expr");
|
||||
PathSet drvsSeen;
|
||||
PathSet drvsSeen;
|
||||
printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+26
-3
@@ -12,7 +12,9 @@ typedef enum {
|
||||
tPath,
|
||||
tNull,
|
||||
tAttrs,
|
||||
tList,
|
||||
tList1,
|
||||
tList2,
|
||||
tListN,
|
||||
tThunk,
|
||||
tApp,
|
||||
tLambda,
|
||||
@@ -119,9 +121,10 @@ struct Value
|
||||
const char * path;
|
||||
Bindings * attrs;
|
||||
struct {
|
||||
unsigned int length;
|
||||
unsigned int size;
|
||||
Value * * elems;
|
||||
} list;
|
||||
} bigList;
|
||||
Value * smallList[2];
|
||||
struct {
|
||||
Env * env;
|
||||
Expr * expr;
|
||||
@@ -139,6 +142,26 @@ struct Value
|
||||
} primOpApp;
|
||||
ExternalValueBase * external;
|
||||
};
|
||||
|
||||
bool isList() const
|
||||
{
|
||||
return type == tList1 || type == tList2 || type == tListN;
|
||||
}
|
||||
|
||||
Value * * listElems()
|
||||
{
|
||||
return type == tList1 || type == tList2 ? smallList : bigList.elems;
|
||||
}
|
||||
|
||||
const Value * const * listElems() const
|
||||
{
|
||||
return type == tList1 || type == tList2 ? smallList : bigList.elems;
|
||||
}
|
||||
|
||||
unsigned int listSize() const
|
||||
{
|
||||
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ static void setLogType(string lt)
|
||||
if (lt == "pretty") logType = ltPretty;
|
||||
else if (lt == "escapes") logType = ltEscapes;
|
||||
else if (lt == "flat") logType = ltFlat;
|
||||
else if (lt == "systemd") logType = ltSystemd;
|
||||
else throw UsageError("unknown log type");
|
||||
}
|
||||
|
||||
@@ -116,6 +117,9 @@ void initNix()
|
||||
|
||||
std::ios::sync_with_stdio(false);
|
||||
|
||||
if (getEnv("IN_SYSTEMD") == "1")
|
||||
logType = ltSystemd;
|
||||
|
||||
settings.processEnvironment();
|
||||
settings.loadConfFile();
|
||||
|
||||
@@ -239,6 +243,20 @@ void parseCmdLine(int argc, char * * argv,
|
||||
void printVersion(const string & programName)
|
||||
{
|
||||
std::cout << format("%1% (Nix) %2%") % programName % nixVersion << std::endl;
|
||||
if (verbosity > lvlInfo) {
|
||||
Strings cfg;
|
||||
#if HAVE_BOEHMGC
|
||||
cfg.push_back("gc");
|
||||
#endif
|
||||
#if HAVE_SODIUM
|
||||
cfg.push_back("signed-caches");
|
||||
#endif
|
||||
std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
|
||||
std::cout << "Configuration file: " << settings.nixConfDir + "/nix.conf" << "\n";
|
||||
std::cout << "Store directory: " << settings.nixStore << "\n";
|
||||
std::cout << "State directory: " << settings.nixStateDir << "\n";
|
||||
std::cout << "Database directory: " << settings.nixDBPath << "\n";
|
||||
}
|
||||
throw Exit();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ extern volatile ::sig_atomic_t blockInt;
|
||||
|
||||
string showBytes(unsigned long long bytes);
|
||||
|
||||
class GCResults;
|
||||
struct GCResults;
|
||||
|
||||
struct PrintFreed
|
||||
{
|
||||
|
||||
+675
-435
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
#include "builtins.hh"
|
||||
#include "download.hh"
|
||||
#include "store-api.hh"
|
||||
#include "archive.hh"
|
||||
#include "compression.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void builtinFetchurl(const BasicDerivation & drv)
|
||||
{
|
||||
auto url = drv.env.find("url");
|
||||
if (url == drv.env.end()) throw Error("attribute ‘url’ missing");
|
||||
|
||||
/* No need to do TLS verification, because we check the hash of
|
||||
the result anyway. */
|
||||
DownloadOptions options;
|
||||
options.verifyTLS = false;
|
||||
|
||||
/* Show a progress indicator, even though stderr is not a tty. */
|
||||
options.forceProgress = true;
|
||||
|
||||
auto data = downloadFile(url->second, options);
|
||||
|
||||
auto out = drv.env.find("out");
|
||||
if (out == drv.env.end()) throw Error("attribute ‘url’ missing");
|
||||
|
||||
Path storePath = out->second;
|
||||
assertStorePath(storePath);
|
||||
|
||||
auto unpack = drv.env.find("unpack");
|
||||
if (unpack != drv.env.end() && unpack->second == "1") {
|
||||
if (string(data.data, 0, 6) == string("\xfd" "7zXZ\0", 6))
|
||||
data.data = decompressXZ(data.data);
|
||||
StringSource source(data.data);
|
||||
restorePath(storePath, source);
|
||||
} else
|
||||
writeFile(storePath, data.data);
|
||||
|
||||
auto executable = drv.env.find("executable");
|
||||
if (executable != drv.env.end() && executable->second == "1") {
|
||||
if (chmod(storePath.c_str(), 0755) == -1)
|
||||
throw SysError(format("making ‘%1%’ executable") % storePath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "derivations.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void builtinFetchurl(const BasicDerivation & drv);
|
||||
|
||||
}
|
||||
+61
-20
@@ -3,6 +3,7 @@
|
||||
#include "globals.hh"
|
||||
#include "util.hh"
|
||||
#include "misc.hh"
|
||||
#include "worker-protocol.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -31,8 +32,8 @@ Path writeDerivation(StoreAPI & store,
|
||||
{
|
||||
PathSet references;
|
||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
|
||||
references.insert(i->first);
|
||||
for (auto & i : drv.inputDrvs)
|
||||
references.insert(i.first);
|
||||
/* Note that the outputs of a derivation are *not* references
|
||||
(that can be missing (of course) and should not necessarily be
|
||||
held during a garbage collection). */
|
||||
@@ -155,21 +156,21 @@ string unparseDerivation(const Derivation & drv)
|
||||
s += "Derive([";
|
||||
|
||||
bool first = true;
|
||||
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
|
||||
for (auto & i : drv.outputs) {
|
||||
if (first) first = false; else s += ',';
|
||||
s += '('; printString(s, i->first);
|
||||
s += ','; printString(s, i->second.path);
|
||||
s += ','; printString(s, i->second.hashAlgo);
|
||||
s += ','; printString(s, i->second.hash);
|
||||
s += '('; printString(s, i.first);
|
||||
s += ','; printString(s, i.second.path);
|
||||
s += ','; printString(s, i.second.hashAlgo);
|
||||
s += ','; printString(s, i.second.hash);
|
||||
s += ')';
|
||||
}
|
||||
|
||||
s += "],[";
|
||||
first = true;
|
||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
|
||||
for (auto & i : drv.inputDrvs) {
|
||||
if (first) first = false; else s += ',';
|
||||
s += '('; printString(s, i->first);
|
||||
s += ','; printStrings(s, i->second.begin(), i->second.end());
|
||||
s += '('; printString(s, i.first);
|
||||
s += ','; printStrings(s, i.second.begin(), i.second.end());
|
||||
s += ')';
|
||||
}
|
||||
|
||||
@@ -182,10 +183,10 @@ string unparseDerivation(const Derivation & drv)
|
||||
|
||||
s += ",[";
|
||||
first = true;
|
||||
foreach (StringPairs::const_iterator, i, drv.env) {
|
||||
for (auto & i : drv.env) {
|
||||
if (first) first = false; else s += ',';
|
||||
s += '('; printString(s, i->first);
|
||||
s += ','; printString(s, i->second);
|
||||
s += '('; printString(s, i.first);
|
||||
s += ','; printString(s, i.second);
|
||||
s += ')';
|
||||
}
|
||||
|
||||
@@ -246,15 +247,15 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
|
||||
/* For other derivations, replace the inputs paths with recursive
|
||||
calls to this function.*/
|
||||
DerivationInputs inputs2;
|
||||
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
|
||||
Hash h = drvHashes[i->first];
|
||||
for (auto & i : drv.inputDrvs) {
|
||||
Hash h = drvHashes[i.first];
|
||||
if (h.type == htUnknown) {
|
||||
assert(store.isValidPath(i->first));
|
||||
Derivation drv2 = readDerivation(i->first);
|
||||
assert(store.isValidPath(i.first));
|
||||
Derivation drv2 = readDerivation(i.first);
|
||||
h = hashDerivationModulo(store, drv2);
|
||||
drvHashes[i->first] = h;
|
||||
drvHashes[i.first] = h;
|
||||
}
|
||||
inputs2[printHash(h)] = i->second;
|
||||
inputs2[printHash(h)] = i.second;
|
||||
}
|
||||
drv.inputDrvs = inputs2;
|
||||
|
||||
@@ -285,7 +286,7 @@ bool wantOutput(const string & output, const std::set<string> & wanted)
|
||||
}
|
||||
|
||||
|
||||
PathSet outputPaths(const Derivation & drv)
|
||||
PathSet outputPaths(const BasicDerivation & drv)
|
||||
{
|
||||
PathSet paths;
|
||||
for (auto & i : drv.outputs)
|
||||
@@ -294,4 +295,44 @@ PathSet outputPaths(const Derivation & drv)
|
||||
}
|
||||
|
||||
|
||||
Source & operator >> (Source & in, BasicDerivation & drv)
|
||||
{
|
||||
drv.outputs.clear();
|
||||
auto nr = readInt(in);
|
||||
for (unsigned int n = 0; n < nr; n++) {
|
||||
auto name = readString(in);
|
||||
DerivationOutput o;
|
||||
in >> o.path >> o.hashAlgo >> o.hash;
|
||||
assertStorePath(o.path);
|
||||
drv.outputs[name] = o;
|
||||
}
|
||||
|
||||
drv.inputSrcs = readStorePaths<PathSet>(in);
|
||||
in >> drv.platform >> drv.builder;
|
||||
drv.args = readStrings<Strings>(in);
|
||||
|
||||
nr = readInt(in);
|
||||
for (unsigned int n = 0; n < nr; n++) {
|
||||
auto key = readString(in);
|
||||
auto value = readString(in);
|
||||
drv.env[key] = value;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
Sink & operator << (Sink & out, const BasicDerivation & drv)
|
||||
{
|
||||
out << drv.outputs.size();
|
||||
for (auto & i : drv.outputs)
|
||||
out << i.first << i.second.path << i.second.hashAlgo << i.second.hash;
|
||||
out << drv.inputSrcs << drv.platform << drv.builder << drv.args;
|
||||
out << drv.env.size();
|
||||
for (auto & i : drv.env)
|
||||
out << i.first << i.second;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -40,15 +40,21 @@ typedef std::map<Path, StringSet> DerivationInputs;
|
||||
|
||||
typedef std::map<string, string> StringPairs;
|
||||
|
||||
struct Derivation
|
||||
struct BasicDerivation
|
||||
{
|
||||
DerivationOutputs outputs; /* keyed on symbolic IDs */
|
||||
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
|
||||
PathSet inputSrcs; /* inputs that are sources */
|
||||
string platform;
|
||||
Path builder;
|
||||
Strings args;
|
||||
StringPairs env;
|
||||
|
||||
virtual ~BasicDerivation() { };
|
||||
};
|
||||
|
||||
struct Derivation : BasicDerivation
|
||||
{
|
||||
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
|
||||
};
|
||||
|
||||
|
||||
@@ -89,6 +95,12 @@ Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outpu
|
||||
|
||||
bool wantOutput(const string & output, const std::set<string> & wanted);
|
||||
|
||||
PathSet outputPaths(const Derivation & drv);
|
||||
PathSet outputPaths(const BasicDerivation & drv);
|
||||
|
||||
struct Source;
|
||||
struct Sink;
|
||||
|
||||
Source & operator >> (Source & in, BasicDerivation & drv);
|
||||
Sink & operator << (Sink & out, const BasicDerivation & drv);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,18 @@
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
double getTime()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
return tv.tv_sec + (tv.tv_usec / 1000000.0);
|
||||
}
|
||||
|
||||
struct Curl
|
||||
{
|
||||
CURL * curl;
|
||||
@@ -16,6 +26,10 @@ struct Curl
|
||||
|
||||
struct curl_slist * requestHeaders;
|
||||
|
||||
bool showProgress;
|
||||
double prevProgressTime{0}, startTime{0};
|
||||
unsigned int moveBack{1};
|
||||
|
||||
static size_t writeCallback(void * contents, size_t size, size_t nmemb, void * userp)
|
||||
{
|
||||
Curl & c(* (Curl *) userp);
|
||||
@@ -56,11 +70,30 @@ struct Curl
|
||||
return realSize;
|
||||
}
|
||||
|
||||
static int progressCallback(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
int progressCallback(double dltotal, double dlnow)
|
||||
{
|
||||
if (showProgress) {
|
||||
double now = getTime();
|
||||
if (prevProgressTime <= now - 1) {
|
||||
string s = (format(" [%1$.0f/%2$.0f KiB, %3$.1f KiB/s]")
|
||||
% (dlnow / 1024.0)
|
||||
% (dltotal / 1024.0)
|
||||
% (now == startTime ? 0 : dlnow / 1024.0 / (now - startTime))).str();
|
||||
std::cerr << "\e[" << moveBack << "D" << s;
|
||||
moveBack = s.size();
|
||||
std::cerr.flush();
|
||||
prevProgressTime = now;
|
||||
}
|
||||
}
|
||||
return _isInterrupted;
|
||||
}
|
||||
|
||||
static int progressCallback_(void * userp, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
Curl & c(* (Curl *) userp);
|
||||
return c.progressCallback(dltotal, dlnow);
|
||||
}
|
||||
|
||||
Curl()
|
||||
{
|
||||
requestHeaders = 0;
|
||||
@@ -69,7 +102,6 @@ struct Curl
|
||||
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);
|
||||
|
||||
@@ -79,7 +111,8 @@ struct 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_PROGRESSFUNCTION, progressCallback_);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void *) &curl);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
}
|
||||
|
||||
@@ -89,10 +122,19 @@ struct Curl
|
||||
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
||||
}
|
||||
|
||||
bool fetch(const string & url, const string & expectedETag = "")
|
||||
bool fetch(const string & url, const DownloadOptions & options)
|
||||
{
|
||||
showProgress = options.forceProgress || isatty(STDERR_FILENO);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
|
||||
if (options.verifyTLS)
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
|
||||
else {
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
}
|
||||
|
||||
data.clear();
|
||||
|
||||
if (requestHeaders) {
|
||||
@@ -100,16 +142,25 @@ struct Curl
|
||||
requestHeaders = 0;
|
||||
}
|
||||
|
||||
if (!expectedETag.empty()) {
|
||||
this->expectedETag = expectedETag;
|
||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + expectedETag).c_str());
|
||||
if (!options.expectedETag.empty()) {
|
||||
this->expectedETag = options.expectedETag;
|
||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + options.expectedETag).c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeaders);
|
||||
|
||||
if (showProgress) {
|
||||
std::cerr << (format("downloading ‘%1%’... ") % url);
|
||||
std::cerr.flush();
|
||||
startTime = getTime();
|
||||
}
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
if (showProgress)
|
||||
//std::cerr << "\e[" << moveBack << "D\e[K\n";
|
||||
std::cerr << "\n";
|
||||
checkInterrupt();
|
||||
if (res == CURLE_WRITE_ERROR && etag == expectedETag) return false;
|
||||
if (res == CURLE_WRITE_ERROR && etag == options.expectedETag) return false;
|
||||
if (res != CURLE_OK)
|
||||
throw DownloadError(format("unable to download ‘%1%’: %2% (%3%)")
|
||||
% url % curl_easy_strerror(res) % res);
|
||||
@@ -123,11 +174,11 @@ struct Curl
|
||||
};
|
||||
|
||||
|
||||
DownloadResult downloadFile(string url, string expectedETag)
|
||||
DownloadResult downloadFile(string url, const DownloadOptions & options)
|
||||
{
|
||||
DownloadResult res;
|
||||
Curl curl;
|
||||
if (curl.fetch(url, expectedETag)) {
|
||||
if (curl.fetch(url, options)) {
|
||||
res.cached = false;
|
||||
res.data = curl.data;
|
||||
} else
|
||||
@@ -178,13 +229,10 @@ Path downloadFileCached(const string & url, bool unpack)
|
||||
|
||||
if (!skip) {
|
||||
|
||||
if (storePath.empty())
|
||||
printMsg(lvlInfo, format("downloading ‘%1%’...") % url);
|
||||
else
|
||||
printMsg(lvlInfo, format("checking ‘%1%’...") % url);
|
||||
|
||||
try {
|
||||
auto res = downloadFile(url, expectedETag);
|
||||
DownloadOptions options;
|
||||
options.expectedETag = expectedETag;
|
||||
auto res = downloadFile(url, options);
|
||||
|
||||
if (!res.cached)
|
||||
storePath = store->addTextToStore(name, res.data, PathSet(), false);
|
||||
@@ -192,7 +240,7 @@ Path downloadFileCached(const string & url, bool unpack)
|
||||
assert(!storePath.empty());
|
||||
replaceSymlink(storePath, fileLink);
|
||||
|
||||
writeFile(dataFile, url + "\n" + res.etag + "\n" + int2String(time(0)) + "\n");
|
||||
writeFile(dataFile, url + "\n" + res.etag + "\n" + std::to_string(time(0)) + "\n");
|
||||
} catch (DownloadError & e) {
|
||||
if (storePath.empty()) throw;
|
||||
printMsg(lvlError, format("warning: %1%; using cached result") % e.msg());
|
||||
@@ -5,13 +5,20 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct DownloadOptions
|
||||
{
|
||||
string expectedETag;
|
||||
bool verifyTLS{true};
|
||||
bool forceProgress{false};
|
||||
};
|
||||
|
||||
struct DownloadResult
|
||||
{
|
||||
bool cached;
|
||||
string data, etag;
|
||||
};
|
||||
|
||||
DownloadResult downloadFile(string url, string expectedETag = "");
|
||||
DownloadResult downloadFile(string url, const DownloadOptions & options);
|
||||
|
||||
Path downloadFileCached(const string & url, bool unpack);
|
||||
|
||||
+44
-30
@@ -350,15 +350,14 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
|
||||
|
||||
StringSet paths = tokenizeString<StringSet>(result, "\n");
|
||||
|
||||
foreach (StringSet::iterator, i, paths) {
|
||||
if (isInStore(*i)) {
|
||||
Path path = toStorePath(*i);
|
||||
for (auto & i : paths)
|
||||
if (isInStore(i)) {
|
||||
Path path = toStorePath(i);
|
||||
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
|
||||
debug(format("got additional root ‘%1%’") % path);
|
||||
roots.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -376,6 +375,7 @@ struct LocalStore::GCState
|
||||
bool gcKeepOutputs;
|
||||
bool gcKeepDerivations;
|
||||
unsigned long long bytesInvalidated;
|
||||
bool moveToTrash = true;
|
||||
Path trashDir;
|
||||
bool shouldDelete;
|
||||
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
|
||||
@@ -407,8 +407,8 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
if (isValidPath(path)) {
|
||||
PathSet referrers;
|
||||
queryReferrers(path, referrers);
|
||||
foreach (PathSet::iterator, i, referrers)
|
||||
if (*i != path) deletePathRecursive(state, *i);
|
||||
for (auto & i : referrers)
|
||||
if (i != path) deletePathRecursive(state, i);
|
||||
size = queryPathInfo(path).narSize;
|
||||
invalidatePathChecked(path);
|
||||
}
|
||||
@@ -428,16 +428,23 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
not holding the global GC lock) we can delete the path without
|
||||
being afraid that the path has become alive again. Otherwise
|
||||
delete it right away. */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (state.moveToTrash && S_ISDIR(st.st_mode)) {
|
||||
// Estimate the amount freed using the narSize field. FIXME:
|
||||
// if the path was not valid, need to determine the actual
|
||||
// size.
|
||||
state.bytesInvalidated += size;
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making ‘%1%’ writable") % path);
|
||||
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename ‘%1%’ to ‘%2%’") % path % tmp);
|
||||
try {
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making ‘%1%’ writable") % path);
|
||||
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename ‘%1%’ to ‘%2%’") % path % tmp);
|
||||
state.bytesInvalidated += size;
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOSPC) {
|
||||
printMsg(lvlInfo, format("note: can't create move ‘%1%’: %2%") % path % e.msg());
|
||||
deleteGarbage(state, path);
|
||||
}
|
||||
}
|
||||
} else
|
||||
deleteGarbage(state, path);
|
||||
|
||||
@@ -479,22 +486,22 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p
|
||||
don't delete the derivation if any of the outputs are alive. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (isValidPath(*i) && queryDeriver(*i) == path)
|
||||
incoming.insert(*i);
|
||||
for (auto & i : outputs)
|
||||
if (isValidPath(i) && queryDeriver(i) == path)
|
||||
incoming.insert(i);
|
||||
}
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if there
|
||||
are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, i, derivers)
|
||||
incoming.insert(*i);
|
||||
for (auto & i : derivers)
|
||||
incoming.insert(i);
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, incoming)
|
||||
if (*i != path)
|
||||
if (canReachRoot(state, visited, *i)) {
|
||||
for (auto & i : incoming)
|
||||
if (i != path)
|
||||
if (canReachRoot(state, visited, i)) {
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
@@ -614,7 +621,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
printMsg(lvlError, format("finding garbage collector roots..."));
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
|
||||
|
||||
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
|
||||
for (auto & i : rootMap) state.roots.insert(i.second);
|
||||
|
||||
/* Add additional roots returned by the program specified by the
|
||||
NIX_ROOT_FINDER environment variable. This is typically used
|
||||
@@ -636,7 +643,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
if (state.shouldDelete) {
|
||||
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
|
||||
createDirs(state.trashDir);
|
||||
try {
|
||||
createDirs(state.trashDir);
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOSPC) {
|
||||
printMsg(lvlInfo, format("note: can't create trash directory: %1%") % e.msg());
|
||||
state.moveToTrash = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now either delete all garbage paths, or just the specified
|
||||
@@ -644,11 +658,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
if (options.action == GCOptions::gcDeleteSpecific) {
|
||||
|
||||
foreach (PathSet::iterator, i, options.pathsToDelete) {
|
||||
assertStorePath(*i);
|
||||
tryToDelete(state, *i);
|
||||
if (state.dead.find(*i) == state.dead.end())
|
||||
throw Error(format("cannot delete path ‘%1%’ since it is still alive") % *i);
|
||||
for (auto & i : options.pathsToDelete) {
|
||||
assertStorePath(i);
|
||||
tryToDelete(state, i);
|
||||
if (state.dead.find(i) == state.dead.end())
|
||||
throw Error(format("cannot delete path ‘%1%’ since it is still alive") % i);
|
||||
}
|
||||
|
||||
} else if (options.maxFreed > 0) {
|
||||
@@ -692,8 +706,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
vector<Path> entries_(entries.begin(), entries.end());
|
||||
random_shuffle(entries_.begin(), entries_.end());
|
||||
|
||||
foreach (vector<Path>::iterator, i, entries_)
|
||||
tryToDelete(state, *i);
|
||||
for (auto & i : entries_)
|
||||
tryToDelete(state, i);
|
||||
|
||||
} catch (GCLimitReached & e) {
|
||||
}
|
||||
|
||||
+14
-9
@@ -41,7 +41,7 @@ Settings::Settings()
|
||||
buildTimeout = 0;
|
||||
useBuildHook = true;
|
||||
printBuildTrace = false;
|
||||
reservedSize = 1024 * 1024;
|
||||
reservedSize = 8 * 1024 * 1024;
|
||||
fsyncMetadata = true;
|
||||
useSQLiteWAL = true;
|
||||
syncBeforeRegistering = false;
|
||||
@@ -77,6 +77,11 @@ void Settings::processEnvironment()
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
|
||||
|
||||
// should be set with the other config options, but depends on nixLibexecDir
|
||||
#ifdef __APPLE__
|
||||
preBuildHook = nixLibexecDir + "/nix/resolve-system-dependencies.pl";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -248,12 +253,12 @@ template<class N> void Settings::_get(N & res, const string & name)
|
||||
string Settings::pack()
|
||||
{
|
||||
string s;
|
||||
foreach (SettingsMap::iterator, i, settings) {
|
||||
if (i->first.find('\n') != string::npos ||
|
||||
i->first.find('=') != string::npos ||
|
||||
i->second.find('\n') != string::npos)
|
||||
for (auto & i : settings) {
|
||||
if (i.first.find('\n') != string::npos ||
|
||||
i.first.find('=') != string::npos ||
|
||||
i.second.find('\n') != string::npos)
|
||||
throw Error("illegal option name/value");
|
||||
s += i->first; s += '='; s += i->second; s += '\n';
|
||||
s += i.first; s += '='; s += i.second; s += '\n';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -261,11 +266,11 @@ string Settings::pack()
|
||||
|
||||
void Settings::unpack(const string & pack) {
|
||||
Strings lines = tokenizeString<Strings>(pack, "\n");
|
||||
foreach (Strings::iterator, i, lines) {
|
||||
string::size_type eq = i->find('=');
|
||||
for (auto & i : lines) {
|
||||
string::size_type eq = i.find('=');
|
||||
if (eq == string::npos)
|
||||
throw Error("illegal option name/value");
|
||||
set(i->substr(0, eq), i->substr(eq + 1));
|
||||
set(i.substr(0, eq), i.substr(eq + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+94
-95
@@ -23,16 +23,11 @@
|
||||
#include <time.h>
|
||||
#include <grp.h>
|
||||
|
||||
#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H
|
||||
#if __linux__
|
||||
#include <sched.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <sqlite3.h>
|
||||
@@ -289,7 +284,17 @@ LocalStore::LocalStore(bool reserveSpace)
|
||||
struct stat st;
|
||||
if (stat(reservedPath.c_str(), &st) == -1 ||
|
||||
st.st_size != settings.reservedSize)
|
||||
writeFile(reservedPath, string(settings.reservedSize, 'X'));
|
||||
{
|
||||
AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT, 0600);
|
||||
int res = -1;
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
res = posix_fallocate(fd, 0, settings.reservedSize);
|
||||
#endif
|
||||
if (res == -1) {
|
||||
writeFull(fd, string(settings.reservedSize, 'X'));
|
||||
ftruncate(fd, settings.reservedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
deletePath(reservedPath);
|
||||
@@ -357,13 +362,13 @@ LocalStore::LocalStore(bool reserveSpace)
|
||||
LocalStore::~LocalStore()
|
||||
{
|
||||
try {
|
||||
foreach (RunningSubstituters::iterator, i, runningSubstituters) {
|
||||
if (i->second.disabled) continue;
|
||||
i->second.to.close();
|
||||
i->second.from.close();
|
||||
i->second.error.close();
|
||||
if (i->second.pid != -1)
|
||||
i->second.pid.wait(true);
|
||||
for (auto & i : runningSubstituters) {
|
||||
if (i.second.disabled) continue;
|
||||
i.second.to.close();
|
||||
i.second.from.close();
|
||||
i.second.error.close();
|
||||
if (i.second.pid != -1)
|
||||
i.second.pid.wait(true);
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
@@ -492,7 +497,7 @@ void LocalStore::openDB(bool create)
|
||||
bind mount. So make the Nix store writable for this process. */
|
||||
void LocalStore::makeStoreWritable()
|
||||
{
|
||||
#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
|
||||
#if __linux__
|
||||
if (getuid() != 0) return;
|
||||
/* Check if /nix/store is on a read-only mount. */
|
||||
struct statvfs stat;
|
||||
@@ -597,10 +602,10 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
users group); we check for this case below. */
|
||||
if (st.st_uid != geteuid()) {
|
||||
#if HAVE_LCHOWN
|
||||
if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1)
|
||||
if (lchown(path.c_str(), geteuid(), getegid()) == -1)
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) &&
|
||||
chown(path.c_str(), geteuid(), (gid_t) -1) == -1)
|
||||
chown(path.c_str(), geteuid(), getegid()) == -1)
|
||||
#endif
|
||||
throw SysError(format("changing owner of ‘%1%’ to %2%")
|
||||
% path % geteuid());
|
||||
@@ -661,19 +666,19 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
||||
|
||||
else {
|
||||
Derivation drvCopy(drv);
|
||||
foreach (DerivationOutputs::iterator, i, drvCopy.outputs) {
|
||||
i->second.path = "";
|
||||
drvCopy.env[i->first] = "";
|
||||
for (auto & i : drvCopy.outputs) {
|
||||
i.second.path = "";
|
||||
drvCopy.env[i.first] = "";
|
||||
}
|
||||
|
||||
Hash h = hashDerivationModulo(*this, drvCopy);
|
||||
|
||||
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
|
||||
Path outPath = makeOutputPath(i->first, h, drvName);
|
||||
StringPairs::const_iterator j = drv.env.find(i->first);
|
||||
if (i->second.path != outPath || j == drv.env.end() || j->second != outPath)
|
||||
for (auto & i : drv.outputs) {
|
||||
Path outPath = makeOutputPath(i.first, h, drvName);
|
||||
StringPairs::const_iterator j = drv.env.find(i.first);
|
||||
if (i.second.path != outPath || j == drv.env.end() || j->second != outPath)
|
||||
throw Error(format("derivation ‘%1%’ has incorrect output ‘%2%’, should be ‘%3%’")
|
||||
% drvPath % i->second.path % outPath);
|
||||
% drvPath % i.second.path % outPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -711,11 +716,11 @@ unsigned long long LocalStore::addValidPath(const ValidPathInfo & info, bool che
|
||||
registration above is undone. */
|
||||
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
||||
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
for (auto & i : drv.outputs) {
|
||||
SQLiteStmtUse use(stmtAddDerivationOutput);
|
||||
stmtAddDerivationOutput.bind(id);
|
||||
stmtAddDerivationOutput.bind(i->first);
|
||||
stmtAddDerivationOutput.bind(i->second.path);
|
||||
stmtAddDerivationOutput.bind(i.first);
|
||||
stmtAddDerivationOutput.bind(i.second.path);
|
||||
if (sqlite3_step(stmtAddDerivationOutput) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("adding derivation output for ‘%1%’ in database") % info.path);
|
||||
}
|
||||
@@ -786,11 +791,11 @@ void LocalStore::clearFailedPaths(const PathSet & paths)
|
||||
retry_sqlite {
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
for (auto & i : paths) {
|
||||
SQLiteStmtUse use(stmtClearFailedPath);
|
||||
stmtClearFailedPath.bind(*i);
|
||||
stmtClearFailedPath.bind(i);
|
||||
if (sqlite3_step(stmtClearFailedPath) != SQLITE_DONE)
|
||||
throwSQLiteError(db, format("clearing failed path ‘%1%’ in database") % *i);
|
||||
throwSQLiteError(db, format("clearing failed path ‘%1%’ in database") % i);
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
@@ -913,8 +918,8 @@ PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
||||
{
|
||||
retry_sqlite {
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (isValidPath_(*i)) res.insert(*i);
|
||||
for (auto & i : paths)
|
||||
if (isValidPath_(i)) res.insert(i);
|
||||
return res;
|
||||
} end_retry_sqlite;
|
||||
}
|
||||
@@ -1202,14 +1207,14 @@ template<class T> T LocalStore::getIntLineFromSubstituter(RunningSubstituter & r
|
||||
PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||
{
|
||||
PathSet res;
|
||||
foreach (Paths::iterator, i, settings.substituters) {
|
||||
for (auto & i : settings.substituters) {
|
||||
if (res.size() == paths.size()) break;
|
||||
RunningSubstituter & run(runningSubstituters[*i]);
|
||||
startSubstituter(*i, run);
|
||||
RunningSubstituter & run(runningSubstituters[i]);
|
||||
startSubstituter(i, run);
|
||||
if (run.disabled) continue;
|
||||
string s = "have ";
|
||||
foreach (PathSet::const_iterator, j, paths)
|
||||
if (res.find(*j) == res.end()) { s += *j; s += " "; }
|
||||
for (auto & j : paths)
|
||||
if (res.find(j) == res.end()) { s += j; s += " "; }
|
||||
writeLine(run.to, s);
|
||||
while (true) {
|
||||
/* FIXME: we only read stderr when an error occurs, so
|
||||
@@ -1233,8 +1238,8 @@ void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
||||
if (run.disabled) return;
|
||||
|
||||
string s = "info ";
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (infos.find(*i) == infos.end()) { s += *i; s += " "; }
|
||||
for (auto & i : paths)
|
||||
if (infos.find(i) == infos.end()) { s += i; s += " "; }
|
||||
writeLine(run.to, s);
|
||||
|
||||
while (true) {
|
||||
@@ -1262,9 +1267,9 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos)
|
||||
{
|
||||
PathSet todo = paths;
|
||||
foreach (Paths::iterator, i, settings.substituters) {
|
||||
for (auto & i : settings.substituters) {
|
||||
if (todo.empty()) break;
|
||||
querySubstitutablePathInfos(*i, todo, infos);
|
||||
querySubstitutablePathInfos(i, todo, infos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1294,30 +1299,30 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
SQLiteTxn txn(db);
|
||||
PathSet paths;
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
if (isValidPath_(i->path))
|
||||
updatePathInfo(*i);
|
||||
for (auto & i : infos) {
|
||||
assert(i.hash.type == htSHA256);
|
||||
if (isValidPath_(i.path))
|
||||
updatePathInfo(i);
|
||||
else
|
||||
addValidPath(*i, false);
|
||||
paths.insert(i->path);
|
||||
addValidPath(i, false);
|
||||
paths.insert(i.path);
|
||||
}
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
unsigned long long referrer = queryValidPathId(i->path);
|
||||
foreach (PathSet::iterator, j, i->references)
|
||||
addReference(referrer, queryValidPathId(*j));
|
||||
for (auto & i : infos) {
|
||||
unsigned long long referrer = queryValidPathId(i.path);
|
||||
for (auto & j : i.references)
|
||||
addReference(referrer, queryValidPathId(j));
|
||||
}
|
||||
|
||||
/* Check that the derivation outputs are correct. We can't do
|
||||
this in addValidPath() above, because the references might
|
||||
not be valid yet. */
|
||||
foreach (ValidPathInfos::const_iterator, i, infos)
|
||||
if (isDerivation(i->path)) {
|
||||
for (auto & i : infos)
|
||||
if (isDerivation(i.path)) {
|
||||
// FIXME: inefficient; we already loaded the
|
||||
// derivation in addValidPath().
|
||||
Derivation drv = readDerivation(i->path);
|
||||
checkDerivationOutputs(i->path, drv);
|
||||
Derivation drv = readDerivation(i.path);
|
||||
checkDerivationOutputs(i.path, drv);
|
||||
}
|
||||
|
||||
/* Do a topological sort of the paths. This will throw an
|
||||
@@ -1500,7 +1505,7 @@ void LocalStore::exportPath(const Path & path, bool sign,
|
||||
{
|
||||
assertStorePath(path);
|
||||
|
||||
printMsg(lvlInfo, format("exporting path ‘%1%’") % path);
|
||||
printMsg(lvlTalkative, format("exporting path ‘%1%’") % path);
|
||||
|
||||
if (!isValidPath(path))
|
||||
throw Error(format("path ‘%1%’ is not valid") % path);
|
||||
@@ -1518,22 +1523,14 @@ void LocalStore::exportPath(const Path & path, bool sign,
|
||||
throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path
|
||||
% printHash(storedHash) % printHash(hash));
|
||||
|
||||
writeInt(EXPORT_MAGIC, hashAndWriteSink);
|
||||
|
||||
writeString(path, hashAndWriteSink);
|
||||
|
||||
PathSet references;
|
||||
queryReferences(path, references);
|
||||
writeStrings(references, hashAndWriteSink);
|
||||
|
||||
Path deriver = queryDeriver(path);
|
||||
writeString(deriver, hashAndWriteSink);
|
||||
hashAndWriteSink << EXPORT_MAGIC << path << references << queryDeriver(path);
|
||||
|
||||
if (sign) {
|
||||
Hash hash = hashAndWriteSink.currentHash();
|
||||
|
||||
writeInt(1, hashAndWriteSink);
|
||||
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete delTmp(tmpDir);
|
||||
Path hashFile = tmpDir + "/hash";
|
||||
@@ -1551,10 +1548,10 @@ void LocalStore::exportPath(const Path & path, bool sign,
|
||||
args.push_back(hashFile);
|
||||
string signature = runProgram(OPENSSL_PATH, true, args);
|
||||
|
||||
writeString(signature, hashAndWriteSink);
|
||||
hashAndWriteSink << 1 << signature;
|
||||
|
||||
} else
|
||||
writeInt(0, hashAndWriteSink);
|
||||
hashAndWriteSink << 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1611,6 +1608,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
|
||||
|
||||
Path dstPath = readStorePath(hashAndReadSource);
|
||||
|
||||
printMsg(lvlTalkative, format("importing path ‘%1%’") % dstPath);
|
||||
|
||||
PathSet references = readStorePaths<PathSet>(hashAndReadSource);
|
||||
|
||||
Path deriver = readString(hashAndReadSource);
|
||||
@@ -1751,8 +1750,8 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
||||
|
||||
PathSet validPaths2 = queryAllValidPaths(), validPaths, done;
|
||||
|
||||
foreach (PathSet::iterator, i, validPaths2)
|
||||
verifyPath(*i, store, done, validPaths, repair, errors);
|
||||
for (auto & i : validPaths2)
|
||||
verifyPath(i, store, done, validPaths, repair, errors);
|
||||
|
||||
/* Release the GC lock so that checking content hashes (which can
|
||||
take ages) doesn't block the GC or builds. */
|
||||
@@ -1764,33 +1763,33 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
||||
|
||||
Hash nullHash(htSHA256);
|
||||
|
||||
foreach (PathSet::iterator, i, validPaths) {
|
||||
for (auto & i : validPaths) {
|
||||
try {
|
||||
ValidPathInfo info = queryPathInfo(*i);
|
||||
ValidPathInfo info = queryPathInfo(i);
|
||||
|
||||
/* Check the content hash (optionally - slow). */
|
||||
printMsg(lvlTalkative, format("checking contents of ‘%1%’") % *i);
|
||||
HashResult current = hashPath(info.hash.type, *i);
|
||||
printMsg(lvlTalkative, format("checking contents of ‘%1%’") % i);
|
||||
HashResult current = hashPath(info.hash.type, i);
|
||||
|
||||
if (info.hash != nullHash && info.hash != current.first) {
|
||||
printMsg(lvlError, format("path ‘%1%’ was modified! "
|
||||
"expected hash ‘%2%’, got ‘%3%’")
|
||||
% *i % printHash(info.hash) % printHash(current.first));
|
||||
if (repair) repairPath(*i); else errors = true;
|
||||
% i % printHash(info.hash) % printHash(current.first));
|
||||
if (repair) repairPath(i); else errors = true;
|
||||
} else {
|
||||
|
||||
bool update = false;
|
||||
|
||||
/* Fill in missing hashes. */
|
||||
if (info.hash == nullHash) {
|
||||
printMsg(lvlError, format("fixing missing hash on ‘%1%’") % *i);
|
||||
printMsg(lvlError, format("fixing missing hash on ‘%1%’") % i);
|
||||
info.hash = current.first;
|
||||
update = true;
|
||||
}
|
||||
|
||||
/* Fill in missing narSize fields (from old stores). */
|
||||
if (info.narSize == 0) {
|
||||
printMsg(lvlError, format("updating size field on ‘%1%’ to %2%") % *i % current.second);
|
||||
printMsg(lvlError, format("updating size field on ‘%1%’ to %2%") % i % current.second);
|
||||
info.narSize = current.second;
|
||||
update = true;
|
||||
}
|
||||
@@ -1802,7 +1801,7 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
||||
} catch (Error & e) {
|
||||
/* It's possible that the path got GC'ed, so ignore
|
||||
errors on invalid paths. */
|
||||
if (isValidPath(*i))
|
||||
if (isValidPath(i))
|
||||
printMsg(lvlError, format("error: %1%") % e.msg());
|
||||
else
|
||||
printMsg(lvlError, format("warning: %1%") % e.msg());
|
||||
@@ -1834,10 +1833,10 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||
first, then we can invalidate this path as well. */
|
||||
bool canInvalidate = true;
|
||||
PathSet referrers; queryReferrers(path, referrers);
|
||||
foreach (PathSet::iterator, i, referrers)
|
||||
if (*i != path) {
|
||||
verifyPath(*i, store, done, validPaths, repair, errors);
|
||||
if (validPaths.find(*i) != validPaths.end())
|
||||
for (auto & i : referrers)
|
||||
if (i != path) {
|
||||
verifyPath(i, store, done, validPaths, repair, errors);
|
||||
if (validPaths.find(i) != validPaths.end())
|
||||
canInvalidate = false;
|
||||
}
|
||||
|
||||
@@ -1915,12 +1914,12 @@ ValidPathInfo LocalStore::queryPathInfoOld(const Path & path)
|
||||
/* Parse it. */
|
||||
Strings lines = tokenizeString<Strings>(info, "\n");
|
||||
|
||||
foreach (Strings::iterator, i, lines) {
|
||||
string::size_type p = i->find(':');
|
||||
for (auto & i : lines) {
|
||||
string::size_type p = i.find(':');
|
||||
if (p == string::npos)
|
||||
throw Error(format("corrupt line in ‘%1%’: %2%") % infoFile % *i);
|
||||
string name(*i, 0, p);
|
||||
string value(*i, p + 2);
|
||||
throw Error(format("corrupt line in ‘%1%’: %2%") % infoFile % i);
|
||||
string name(i, 0, p);
|
||||
string value(i, p + 2);
|
||||
if (name == "References") {
|
||||
Strings refs = tokenizeString<Strings>(value, " ");
|
||||
res.references = PathSet(refs.begin(), refs.end());
|
||||
@@ -1950,18 +1949,18 @@ void LocalStore::upgradeStore6()
|
||||
|
||||
SQLiteTxn txn(db);
|
||||
|
||||
foreach (PathSet::iterator, i, validPaths) {
|
||||
addValidPath(queryPathInfoOld(*i), false);
|
||||
for (auto & i : validPaths) {
|
||||
addValidPath(queryPathInfoOld(i), false);
|
||||
std::cerr << ".";
|
||||
}
|
||||
|
||||
std::cerr << "|";
|
||||
|
||||
foreach (PathSet::iterator, i, validPaths) {
|
||||
ValidPathInfo info = queryPathInfoOld(*i);
|
||||
unsigned long long referrer = queryValidPathId(*i);
|
||||
foreach (PathSet::iterator, j, info.references)
|
||||
addReference(referrer, queryValidPathId(*j));
|
||||
for (auto & i : validPaths) {
|
||||
ValidPathInfo info = queryPathInfoOld(i);
|
||||
unsigned long long referrer = queryValidPathId(i);
|
||||
for (auto & j : info.references)
|
||||
addReference(referrer, queryValidPathId(j));
|
||||
std::cerr << ".";
|
||||
}
|
||||
|
||||
|
||||
+32
-29
@@ -98,41 +98,41 @@ public:
|
||||
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
bool isValidPath(const Path & path) override;
|
||||
|
||||
PathSet queryValidPaths(const PathSet & paths);
|
||||
PathSet queryValidPaths(const PathSet & paths) override;
|
||||
|
||||
PathSet queryAllValidPaths();
|
||||
PathSet queryAllValidPaths() override;
|
||||
|
||||
ValidPathInfo queryPathInfo(const Path & path);
|
||||
ValidPathInfo queryPathInfo(const Path & path) override;
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
Hash queryPathHash(const Path & path) override;
|
||||
|
||||
void queryReferences(const Path & path, PathSet & references);
|
||||
void queryReferences(const Path & path, PathSet & references) override;
|
||||
|
||||
void queryReferrers(const Path & path, PathSet & referrers);
|
||||
void queryReferrers(const Path & path, PathSet & referrers) override;
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
Path queryDeriver(const Path & path) override;
|
||||
|
||||
PathSet queryValidDerivers(const Path & path);
|
||||
PathSet queryValidDerivers(const Path & path) override;
|
||||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
PathSet queryDerivationOutputs(const Path & path) override;
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
StringSet queryDerivationOutputNames(const Path & path) override;
|
||||
|
||||
Path queryPathFromHashPart(const string & hashPart);
|
||||
Path queryPathFromHashPart(const string & hashPart) override;
|
||||
|
||||
PathSet querySubstitutablePaths(const PathSet & paths);
|
||||
PathSet querySubstitutablePaths(const PathSet & paths) override;
|
||||
|
||||
void querySubstitutablePathInfos(const Path & substituter,
|
||||
PathSet & paths, SubstitutablePathInfos & infos);
|
||||
|
||||
void querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos);
|
||||
SubstitutablePathInfos & infos) override;
|
||||
|
||||
Path addToStore(const string & name, const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter, bool repair = false);
|
||||
PathFilter & filter = defaultPathFilter, bool repair = false) override;
|
||||
|
||||
/* Like addToStore(), but the contents of the path are contained
|
||||
in `dump', which is either a NAR serialisation (if recursive ==
|
||||
@@ -142,40 +142,43 @@ public:
|
||||
bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references, bool repair = false);
|
||||
const PathSet & references, bool repair = false) override;
|
||||
|
||||
void exportPath(const Path & path, bool sign,
|
||||
Sink & sink);
|
||||
Sink & sink) override;
|
||||
|
||||
Paths importPaths(bool requireSignature, Source & source);
|
||||
Paths importPaths(bool requireSignature, Source & source) override;
|
||||
|
||||
void buildPaths(const PathSet & paths, BuildMode buildMode);
|
||||
void buildPaths(const PathSet & paths, BuildMode buildMode) override;
|
||||
|
||||
void ensurePath(const Path & path);
|
||||
BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
|
||||
BuildMode buildMode) override;
|
||||
|
||||
void addTempRoot(const Path & path);
|
||||
void ensurePath(const Path & path) override;
|
||||
|
||||
void addIndirectRoot(const Path & path);
|
||||
void addTempRoot(const Path & path) override;
|
||||
|
||||
void syncWithGC();
|
||||
void addIndirectRoot(const Path & path) override;
|
||||
|
||||
Roots findRoots();
|
||||
void syncWithGC() override;
|
||||
|
||||
void collectGarbage(const GCOptions & options, GCResults & results);
|
||||
Roots findRoots() override;
|
||||
|
||||
void collectGarbage(const GCOptions & options, GCResults & results) override;
|
||||
|
||||
/* Optimise the disk space usage of the Nix store by hard-linking
|
||||
files with the same contents. */
|
||||
void optimiseStore(OptimiseStats & stats);
|
||||
|
||||
/* Generic variant of the above method. */
|
||||
void optimiseStore();
|
||||
void optimiseStore() override;
|
||||
|
||||
/* Optimise a single store path. */
|
||||
void optimisePath(const Path & path);
|
||||
|
||||
/* Check the integrity of the Nix store. Returns true if errors
|
||||
remain. */
|
||||
bool verifyStore(bool checkContents, bool repair);
|
||||
bool verifyStore(bool checkContents, bool repair) override;
|
||||
|
||||
/* Register the validity of a path, i.e., that `path' exists, that
|
||||
the paths referenced by it exists, and in the case of an output
|
||||
@@ -194,9 +197,9 @@ public:
|
||||
/* Query whether `path' previously failed to build. */
|
||||
bool hasPathFailed(const Path & path);
|
||||
|
||||
PathSet queryFailedPaths();
|
||||
PathSet queryFailedPaths() override;
|
||||
|
||||
void clearFailedPaths(const PathSet & paths);
|
||||
void clearFailedPaths(const PathSet & paths) override;
|
||||
|
||||
void vacuumDB();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
|
||||
|
||||
libstore_LIBS = libutil libformat
|
||||
|
||||
libstore_LDFLAGS = -lsqlite3 -lbz2
|
||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS)
|
||||
|
||||
ifeq ($(OS), SunOS)
|
||||
libstore_LDFLAGS += -lsocket
|
||||
@@ -33,3 +33,4 @@ $(d)/local-store.cc: $(d)/schema.sql.hh
|
||||
clean-files += $(d)/schema.sql.hh
|
||||
|
||||
$(eval $(call install-file-in, $(d)/nix-store.pc, $(prefix)/lib/pkgconfig, 0644))
|
||||
$(eval $(call install-file-in, $(d)/sandbox-defaults.sb, $(datadir)/nix, 0644))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user