Compare commits
146 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
179b896acb | ||
|
|
e8d53bfdc9 | ||
|
|
ddbcd01c83 | ||
|
|
eaa52c34b4 | ||
|
|
ddc58e7896 | ||
|
|
6964131cd7 | ||
|
|
de4c03d201 | ||
|
|
88c90d5e6d | ||
|
|
0d54671b7b | ||
|
|
e2d71bd186 | ||
|
|
a6c497f526 | ||
|
|
fa7fd76c5e | ||
|
|
7c377dc5cc | ||
|
|
4e44025ac5 | ||
|
|
cea4fb3a31 | ||
|
|
546f98dace | ||
|
|
4ea9707591 | ||
|
|
1d0e42879f | ||
|
|
056d28a601 | ||
|
|
d4e93532e2 | ||
|
|
d7fdfe322b | ||
|
|
70eb64147e | ||
|
|
a6c0b773b7 | ||
|
|
623fcb071e | ||
|
|
e1eb63a586 | ||
|
|
e59a8a63e1 | ||
|
|
ed73d40c3b | ||
|
|
75a1d96cfd | ||
|
|
7fe5910bf8 | ||
|
|
690ac7c90b | ||
|
|
37264ed0ad | ||
|
|
66eeff3345 | ||
|
|
7e0360504d | ||
|
|
0845cdf944 | ||
|
|
b8bed7da14 | ||
|
|
f67a7007a2 | ||
|
|
3a5a241b32 | ||
|
|
8f186722a9 | ||
|
|
d26b71fda6 | ||
|
|
ac973a6d3c | ||
|
|
96d48318cb | ||
|
|
b095c06139 | ||
|
|
de4934ab3b | ||
|
|
3fe9767dd3 | ||
|
|
52c777a793 | ||
|
|
7253113fd2 | ||
|
|
b8739f2fb3 | ||
|
|
6eb1040e90 | ||
|
|
f471aacff2 | ||
|
|
7828dca9e8 | ||
|
|
1c10a74c73 | ||
|
|
c5cc57e962 | ||
|
|
9bcb4d2dd9 | ||
|
|
4f09ce7940 | ||
|
|
35fd31770c | ||
|
|
c6209030c4 | ||
|
|
a0bdc96726 | ||
|
|
60eca58533 | ||
|
|
5a082ad15a | ||
|
|
960e9c560e | ||
|
|
aa02cdc33c | ||
|
|
3d2d207aad | ||
|
|
081f14a169 | ||
|
|
88cd2d41ac | ||
|
|
ad97a21834 | ||
|
|
444bae44ef | ||
|
|
0685a6480a | ||
|
|
c7e0be1bfc | ||
|
|
98031b6050 | ||
|
|
3780435a0e | ||
|
|
f201b7733e | ||
|
|
a56637205a | ||
|
|
47ad88099b | ||
|
|
88b5d0c8e8 | ||
|
|
55ecdfe2a8 | ||
|
|
163e39547a | ||
|
|
48c192ca2d | ||
|
|
84989d3af2 | ||
|
|
cfdfad5c34 | ||
|
|
0f3dae1064 | ||
|
|
abe6be578b | ||
|
|
9d1e22f743 | ||
|
|
69d82e5c58 | ||
|
|
bc7e3a4dd6 | ||
|
|
6f6bfc8205 | ||
|
|
694b6d229b | ||
|
|
43f8ef73c6 | ||
|
|
f24e726ba5 | ||
|
|
f539085e65 | ||
|
|
47dc6076af | ||
|
|
55012ec0b9 | ||
|
|
bb1d046f5c | ||
|
|
2175eee9fe | ||
|
|
0ffa615420 | ||
|
|
84722d67d2 | ||
|
|
de96daf54f | ||
|
|
19477e8815 | ||
|
|
e7b23eb5ab | ||
|
|
855699855f | ||
|
|
e5432574e2 | ||
|
|
c129fc6ee8 | ||
|
|
88f4f0231b | ||
|
|
c287d73121 | ||
|
|
6fa690291a | ||
|
|
8af911be5c | ||
|
|
30370f168f | ||
|
|
6270b2e50f | ||
|
|
f8e8dd827e | ||
|
|
478e3e4649 | ||
|
|
9f9393df55 | ||
|
|
f93e890b4d | ||
|
|
0167eac571 | ||
|
|
746f8aed86 | ||
|
|
1d5d277ac7 | ||
|
|
e09161d05c | ||
|
|
98f3c75a0e | ||
|
|
479692a068 | ||
|
|
b76e282da8 | ||
|
|
d43a8b25f0 | ||
|
|
f7c26365eb | ||
|
|
c382866cd2 | ||
|
|
9304fde8de | ||
|
|
89a2a11d9f | ||
|
|
3c4c30eadd | ||
|
|
2896bb6826 | ||
|
|
87e3d142cc | ||
|
|
0c95776c3e | ||
|
|
5647e55f65 | ||
|
|
5cc5c3fb83 | ||
|
|
27b510af5c | ||
|
|
16e0287556 | ||
|
|
cfeff3b273 | ||
|
|
d8b4cfad82 | ||
|
|
6ddfe9a999 | ||
|
|
75b9670df6 | ||
|
|
d4dcffd643 | ||
|
|
23fa7e3606 | ||
|
|
ba75c69e00 | ||
|
|
53b520708a | ||
|
|
ebc42f8b59 | ||
|
|
59086e459c | ||
|
|
6454ca393a | ||
|
|
a65376b01d | ||
|
|
74f75c8558 | ||
|
|
d15826164c | ||
|
|
e4ece83b1a |
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@@ -14,7 +14,7 @@ Examples of _Nix_ issues:
|
||||
|
||||
- Nix segfaults when I run `nix-build -A blahblah`
|
||||
- The Nix language needs a new builtin: `builtins.foobar`
|
||||
- Regression in the behavior of `nix-env` in Nix 1.12
|
||||
- Regression in the behavior of `nix-env` in Nix 2.0
|
||||
|
||||
Examples of _nixpkgs_ issues:
|
||||
|
||||
@@ -24,4 +24,4 @@ Examples of _nixpkgs_ issues:
|
||||
|
||||
Chances are if you're a newcomer to the Nix world, you'll probably want the [nixpkgs tracker](https://github.com/NixOS/nixpkgs/issues). It also gets a lot more eyeball traffic so you'll probably get a response a lot more quickly.
|
||||
|
||||
-->
|
||||
-->
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -38,6 +38,7 @@ perl/Makefile.config
|
||||
/scripts/nix-copy-closure
|
||||
/scripts/nix-reduce-build
|
||||
/scripts/nix-http-export.cgi
|
||||
/scripts/nix-profile-daemon.sh
|
||||
|
||||
# /src/libexpr/
|
||||
/src/libexpr/lexer-tab.cc
|
||||
|
||||
3
Makefile
3
Makefile
@@ -24,7 +24,8 @@ makefiles = \
|
||||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk \
|
||||
doc/manual/local.mk \
|
||||
tests/local.mk
|
||||
tests/local.mk \
|
||||
tests/plugins/local.mk
|
||||
|
||||
GLOBAL_CXXFLAGS += -std=c++14 -g -Wall -include config.h
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ ENABLE_S3 = @ENABLE_S3@
|
||||
HAVE_SODIUM = @HAVE_SODIUM@
|
||||
HAVE_READLINE = @HAVE_READLINE@
|
||||
HAVE_BROTLI = @HAVE_BROTLI@
|
||||
HAVE_SECCOMP = @HAVE_SECCOMP@
|
||||
LIBCURL_LIBS = @LIBCURL_LIBS@
|
||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
|
||||
22
configure.ac
22
configure.ac
@@ -61,6 +61,7 @@ CFLAGS=
|
||||
CXXFLAGS=
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CPP
|
||||
AX_CXX_COMPILE_STDCXX_11
|
||||
|
||||
|
||||
@@ -174,6 +175,8 @@ AC_SUBST(HAVE_SODIUM, [$have_sodium])
|
||||
|
||||
# Look for liblzma, a required dependency.
|
||||
PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"])
|
||||
AC_CHECK_LIB([lzma], [lzma_stream_encoder_mt],
|
||||
[AC_DEFINE([HAVE_LZMA_MT], [1], [xz multithreaded compression support])])
|
||||
|
||||
|
||||
# Look for libbrotli{enc,dec}, optional dependencies
|
||||
@@ -185,9 +188,22 @@ AC_SUBST(HAVE_BROTLI, [$have_brotli])
|
||||
|
||||
# Look for libseccomp, required for Linux sandboxing.
|
||||
if test "$sys_name" = linux; then
|
||||
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
|
||||
[CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"])
|
||||
AC_ARG_ENABLE([seccomp-sandboxing],
|
||||
AC_HELP_STRING([--disable-seccomp-sandboxing],
|
||||
[Don't build support for seccomp sandboxing (only recommended if your arch doesn't support libseccomp yet!)]
|
||||
))
|
||||
if test "x$enable_seccomp_sandboxing" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
|
||||
[CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"])
|
||||
have_seccomp=1
|
||||
AC_DEFINE([HAVE_SECCOMP], [1], [Whether seccomp is available and should be used for sandboxing.])
|
||||
else
|
||||
have_seccomp=
|
||||
fi
|
||||
else
|
||||
have_seccomp=
|
||||
fi
|
||||
AC_SUBST(HAVE_SECCOMP, [$have_seccomp])
|
||||
|
||||
|
||||
# Look for aws-cpp-sdk-s3.
|
||||
@@ -199,7 +215,7 @@ AC_SUBST(ENABLE_S3, [$enable_s3])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
if test -n "$enable_s3"; then
|
||||
declare -a aws_version_tokens=($(printf '#include <aws/core/VersionConfig.h>\nAWS_SDK_VERSION_STRING' | cpp -E | grep -v '^#.*' | sed 's/"//g' | tr '.' ' '))
|
||||
declare -a aws_version_tokens=($(printf '#include <aws/core/VersionConfig.h>\nAWS_SDK_VERSION_STRING' | $CPP - | grep -v '^#.*' | sed 's/"//g' | tr '.' ' '))
|
||||
AC_DEFINE_UNQUOTED([AWS_VERSION_MAJOR], ${aws_version_tokens@<:@0@:>@}, [Major version of aws-sdk-cpp.])
|
||||
AC_DEFINE_UNQUOTED([AWS_VERSION_MINOR], ${aws_version_tokens@<:@1@:>@}, [Minor version of aws-sdk-cpp.])
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{ system ? builtins.currentSystem
|
||||
{ system ? "" # obsolete
|
||||
, url
|
||||
, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""
|
||||
, outputHash ?
|
||||
@@ -17,7 +17,9 @@ derivation {
|
||||
inherit outputHashAlgo outputHash;
|
||||
outputHashMode = if unpack || executable then "recursive" else "flat";
|
||||
|
||||
inherit name system url executable unpack;
|
||||
inherit name url executable unpack;
|
||||
|
||||
system = "builtin";
|
||||
|
||||
# No need to double the amount of network traffic
|
||||
preferLocalBuild = true;
|
||||
|
||||
@@ -4,71 +4,109 @@
|
||||
version="5.0"
|
||||
xml:id='chap-distributed-builds'>
|
||||
|
||||
<title>Distributed Builds</title>
|
||||
<title>Remote Builds</title>
|
||||
|
||||
<para>Nix supports distributed builds, where a local Nix installation can
|
||||
forward Nix builds to other machines over the network. This allows
|
||||
multiple builds to be performed in parallel (thus improving
|
||||
performance) and allows Nix to perform multi-platform builds in a
|
||||
semi-transparent way. For instance, if you perform a build for a
|
||||
<literal>x86_64-darwin</literal> on an <literal>i686-linux</literal>
|
||||
machine, Nix can automatically forward the build to a
|
||||
<literal>x86_64-darwin</literal> machine, if available.</para>
|
||||
<para>Nix supports remote builds, where a local Nix installation can
|
||||
forward Nix builds to other machines. This allows multiple builds to
|
||||
be performed in parallel and allows Nix to perform multi-platform
|
||||
builds in a semi-transparent way. For instance, if you perform a
|
||||
build for a <literal>x86_64-darwin</literal> on an
|
||||
<literal>i686-linux</literal> machine, Nix can automatically forward
|
||||
the build to a <literal>x86_64-darwin</literal> machine, if
|
||||
available.</para>
|
||||
|
||||
<para>You can enable distributed builds by setting the environment
|
||||
variable <envar>NIX_BUILD_HOOK</envar> to point to a program that Nix
|
||||
will call whenever it wants to build a derivation. The build hook
|
||||
(typically a shell or Perl script) can decline the build, in which Nix
|
||||
will perform it in the usual way if possible, or it can accept it, in
|
||||
which case it is responsible for somehow getting the inputs of the
|
||||
build to another machine, doing the build there, and getting the
|
||||
results back.</para>
|
||||
<para>To forward a build to a remote machine, it’s required that the
|
||||
remote machine is accessible via SSH and that it has Nix
|
||||
installed. You can test whether connecting to the remote Nix instance
|
||||
works, e.g.
|
||||
|
||||
<example xml:id='ex-remote-systems'><title>Remote machine configuration:
|
||||
<filename>remote-systems.conf</filename></title>
|
||||
<programlisting>
|
||||
nix@mcflurry.labs.cs.uu.nl x86_64-darwin /home/nix/.ssh/id_quarterpounder_auto 2
|
||||
nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 1 kvm
|
||||
nix@itchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 2
|
||||
nix@poochie.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 2 kvm perf
|
||||
</programlisting>
|
||||
</example>
|
||||
<screen>
|
||||
$ nix ping-store --store ssh://mac
|
||||
</screen>
|
||||
|
||||
<para>Nix ships with a build hook that should be suitable for most
|
||||
purposes. It uses <command>ssh</command> and
|
||||
<command>nix-copy-closure</command> to copy the build inputs and
|
||||
outputs and perform the remote build. To use it, you should set
|
||||
<envar>NIX_BUILD_HOOK</envar> to
|
||||
<filename><replaceable>prefix</replaceable>/libexec/nix/build-remote</filename>.
|
||||
You should also define a list of available build machines and point
|
||||
the environment variable <envar>NIX_REMOTE_SYSTEMS</envar> to
|
||||
it. <envar>NIX_REMOTE_SYSTEMS</envar> must be an absolute path. An
|
||||
example configuration is shown in <xref linkend='ex-remote-systems'
|
||||
/>. Each line in the file specifies a machine, with the following
|
||||
bits of information:
|
||||
will try to connect to the machine named <literal>mac</literal>. It is
|
||||
possible to specify an SSH identity file as part of the remote store
|
||||
URI, e.g.
|
||||
|
||||
<screen>
|
||||
$ nix ping-store --store ssh://mac?ssh-key=/home/alice/my-key
|
||||
</screen>
|
||||
|
||||
Since builds should be non-interactive, the key should not have a
|
||||
passphrase. Alternatively, you can load identities ahead of time into
|
||||
<command>ssh-agent</command> or <command>gpg-agent</command>.</para>
|
||||
|
||||
<para>If you get the error
|
||||
|
||||
<screen>
|
||||
bash: nix-store: command not found
|
||||
error: cannot connect to 'mac'
|
||||
</screen>
|
||||
|
||||
then you need to ensure that the <envar>PATH</envar> of
|
||||
non-interactive login shells contains Nix.</para>
|
||||
|
||||
<warning><para>If you are building via the Nix daemon, it is the Nix
|
||||
daemon user account (that is, <literal>root</literal>) that should
|
||||
have SSH access to the remote machine. If you can’t or don’t want to
|
||||
configure <literal>root</literal> to be able to access to remote
|
||||
machine, you can use a private Nix store instead by passing
|
||||
e.g. <literal>--store ~/my-nix</literal>.</para></warning>
|
||||
|
||||
<para>The list of remote machines can be specified on the command line
|
||||
or in the Nix configuration file. The former is convenient for
|
||||
testing. For example, the following command allows you to build a
|
||||
derivation for <literal>x86_64-darwin</literal> on a Linux machine:
|
||||
|
||||
<screen>
|
||||
$ uname
|
||||
Linux
|
||||
|
||||
$ nix build \
|
||||
'(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
|
||||
--builders 'ssh://mac x86_64-darwin'
|
||||
[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac
|
||||
|
||||
$ cat ./result
|
||||
Darwin
|
||||
</screen>
|
||||
|
||||
It is possible to specify multiple builders separated by a semicolon
|
||||
or a newline, e.g.
|
||||
|
||||
<screen>
|
||||
--builders 'ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd'
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>Each machine specification consists of the following elements,
|
||||
separated by spaces. Only the first element is required.
|
||||
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>The name of the remote machine, with optionally the
|
||||
user under which the remote build should be performed. This is
|
||||
actually passed as an argument to <command>ssh</command>, so it can
|
||||
be an alias defined in your
|
||||
<listitem><para>The URI of the remote store in the format
|
||||
<literal>ssh://[<replaceable>username</replaceable>@]<replaceable>hostname</replaceable></literal>,
|
||||
e.g. <literal>ssh://nix@mac</literal> or
|
||||
<literal>ssh://mac</literal>. For backward compatibility,
|
||||
<literal>ssh://</literal> may be omitted. The hostname may be an
|
||||
alias defined in your
|
||||
<filename>~/.ssh/config</filename>.</para></listitem>
|
||||
|
||||
<listitem><para>A comma-separated list of Nix platform type
|
||||
identifiers, such as <literal>x86_64-darwin</literal>. It is
|
||||
possible for a machine to support multiple platform types, e.g.,
|
||||
<literal>i686-linux,x86_64-linux</literal>.</para></listitem>
|
||||
<literal>i686-linux,x86_64-linux</literal>. If omitted, this
|
||||
defaults to the local platform type.</para></listitem>
|
||||
|
||||
<listitem><para>The SSH private key to be used to log in to the
|
||||
remote machine. Since builds should be non-interactive, this key
|
||||
should not have a passphrase!</para></listitem>
|
||||
<listitem><para>The SSH identity file to be used to log in to the
|
||||
remote machine. If omitted, SSH will use its regular
|
||||
identities.</para></listitem>
|
||||
|
||||
<listitem><para>The maximum number of builds that
|
||||
<filename>build-remote</filename> will execute in parallel on the
|
||||
machine. Typically this should be equal to the number of CPU cores.
|
||||
For instance, the machine <literal>itchy</literal> in the example
|
||||
will execute up to 8 builds in parallel.</para></listitem>
|
||||
<listitem><para>The maximum number of builds that Nix will execute
|
||||
in parallel on the machine. Typically this should be equal to the
|
||||
number of CPU cores. For instance, the machine
|
||||
<literal>itchy</literal> in the example will execute up to 8 builds
|
||||
in parallel.</para></listitem>
|
||||
|
||||
<listitem><para>The “speed factor”, indicating the relative speed of
|
||||
the machine. If there are multiple machines of the right type, Nix
|
||||
@@ -76,30 +114,69 @@ bits of information:
|
||||
|
||||
<listitem><para>A comma-separated list of <emphasis>supported
|
||||
features</emphasis>. If a derivation has the
|
||||
<varname>requiredSystemFeatures</varname> attribute, then
|
||||
<filename>build-remote</filename> will only perform the
|
||||
derivation on a machine that has the specified features. For
|
||||
instance, the attribute
|
||||
<varname>requiredSystemFeatures</varname> attribute, then Nix will
|
||||
only perform the derivation on a machine that has the specified
|
||||
features. For instance, the attribute
|
||||
|
||||
<programlisting>
|
||||
requiredSystemFeatures = [ "kvm" ];
|
||||
</programlisting>
|
||||
|
||||
will cause the build to be performed on a machine that has the
|
||||
<literal>kvm</literal> feature (i.e., <literal>scratchy</literal> in
|
||||
the example above).</para></listitem>
|
||||
<literal>kvm</literal> feature.</para></listitem>
|
||||
|
||||
<listitem><para>A comma-separated list of <emphasis>mandatory
|
||||
features</emphasis>. A machine will only be used to build a
|
||||
derivation if all of the machine’s mandatory features appear in the
|
||||
derivation’s <varname>requiredSystemFeatures</varname> attribute.
|
||||
Thus, in the example, the machine <literal>poochie</literal> will
|
||||
only do derivations that have
|
||||
<varname>requiredSystemFeatures</varname> set to <literal>["kvm"
|
||||
"perf"]</literal> or <literal>["perf"]</literal>.</para></listitem>
|
||||
derivation’s <varname>requiredSystemFeatures</varname>
|
||||
attribute..</para></listitem>
|
||||
|
||||
</orderedlist>
|
||||
|
||||
</para>
|
||||
For example, the machine specification
|
||||
|
||||
<programlisting>
|
||||
nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 1 kvm
|
||||
nix@itchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 2
|
||||
nix@poochie.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 1 2 kvm benchmark
|
||||
</programlisting>
|
||||
|
||||
specifies several machines that can perform
|
||||
<literal>i686-linux</literal> builds. However,
|
||||
<literal>poochie</literal> will only do builds that have the attribute
|
||||
|
||||
<programlisting>
|
||||
requiredSystemFeatures = [ "benchmark" ];
|
||||
</programlisting>
|
||||
|
||||
or
|
||||
|
||||
<programlisting>
|
||||
requiredSystemFeatures = [ "benchmark" "kvm" ];
|
||||
</programlisting>
|
||||
|
||||
<literal>itchy</literal> cannot do builds that require
|
||||
<literal>kvm</literal>, but <literal>scratchy</literal> does support
|
||||
such builds. For regular builds, <literal>itchy</literal> will be
|
||||
preferred over <literal>scratchy</literal> because it has a higher
|
||||
speed factor.</para>
|
||||
|
||||
<para>Remote builders can also be configured in
|
||||
<filename>nix.conf</filename>, e.g.
|
||||
|
||||
<programlisting>
|
||||
builders = ssh://mac x86_64-darwin ; ssh://beastie x86_64-freebsd
|
||||
</programlisting>
|
||||
|
||||
Finally, remote builders can be configured in a separate configuration
|
||||
file included in <option>builders</option> via the syntax
|
||||
<literal>@<replaceable>file</replaceable></literal>. For example,
|
||||
|
||||
<programlisting>
|
||||
builders = @/etc/nix/machines
|
||||
</programlisting>
|
||||
|
||||
causes the list of machines in <filename>/etc/nix/machines</filename>
|
||||
to be included. (This is the default.)</para>
|
||||
|
||||
</chapter>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -154,6 +154,8 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<literal>daemon</literal> if you want to use the Nix daemon to
|
||||
execute Nix operations. This is necessary in <link
|
||||
linkend="ssec-multi-user">multi-user Nix installations</link>.
|
||||
If the Nix daemon's Unix socket is at some non-standard path,
|
||||
this variable should be set to <literal>unix://path/to/socket</literal>.
|
||||
Otherwise, it should be left unset.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
</group>
|
||||
<replaceable>attrPath</replaceable>
|
||||
</arg>
|
||||
<arg><option>--drv-link</option> <replaceable>drvlink</replaceable></arg>
|
||||
<arg><option>--add-drv-link</option></arg>
|
||||
<arg><option>--no-out-link</option></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
@@ -91,25 +89,6 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--drv-link</option> <replaceable>drvlink</replaceable></term>
|
||||
|
||||
<listitem><para>Add a symlink named
|
||||
<replaceable>drvlink</replaceable> to the store derivation
|
||||
produced by <command>nix-instantiate</command>. The derivation is
|
||||
a root of the garbage collector until the symlink is deleted or
|
||||
renamed. If there are multiple derivations, numbers are suffixed
|
||||
to <replaceable>drvlink</replaceable> to distinguish between
|
||||
them.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--add-drv-link</option></term>
|
||||
|
||||
<listitem><para>Shorthand for <option>--drv-link</option>
|
||||
<filename>./derivation</filename>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--no-out-link</option></term>
|
||||
|
||||
<listitem><para>Do not create a symlink to the output path. Note
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
</arg>
|
||||
<arg><option>--fallback</option></arg>
|
||||
<arg><option>--readonly-mode</option></arg>
|
||||
<arg><option>--show-trace</option></arg>
|
||||
<arg>
|
||||
<option>-I</option>
|
||||
<replaceable>path</replaceable>
|
||||
|
||||
@@ -301,13 +301,6 @@
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--show-trace</option></term>
|
||||
|
||||
<listitem><para>Causes Nix to print out a stack trace in case of Nix
|
||||
expression evaluation errors.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-I"><term><option>-I</option> <replaceable>path</replaceable></term>
|
||||
|
||||
|
||||
@@ -126,6 +126,17 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.splitVersion</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Split a string representing a version into its
|
||||
components, by the same version splitting logic underlying the
|
||||
version comparison in <link linkend="ssec-version-comparisons">
|
||||
<command>nix-env -u</command></link>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.concatLists</function>
|
||||
<replaceable>lists</replaceable></term>
|
||||
|
||||
@@ -308,8 +319,9 @@ stdenv.mkDerivation { … }
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filterSource</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
<varlistentry xml:id='builtin-filterSource'>
|
||||
<term><function>builtins.filterSource</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
@@ -768,6 +780,75 @@ Evaluates to <literal>[ "foo" ]</literal>.
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<function>builtins.path</function>
|
||||
<replaceable>args</replaceable>
|
||||
</term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
An enrichment of the built-in path type, based on the attributes
|
||||
present in <replaceable>args</replaceable>. All are optional
|
||||
except <varname>path</varname>:
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>path</term>
|
||||
<listitem>
|
||||
<para>The underlying path.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>name</term>
|
||||
<listitem>
|
||||
<para>
|
||||
The name of the path when added to the store. This can
|
||||
used to reference paths that have nix-illegal characters
|
||||
in their names, like <literal>@</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>filter</term>
|
||||
<listitem>
|
||||
<para>
|
||||
A function of the type expected by
|
||||
<link linkend="builtin-filterSource">builtins.filterSource</link>,
|
||||
with the same semantics.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>recursive</term>
|
||||
<listitem>
|
||||
<para>
|
||||
When <literal>false</literal>, when
|
||||
<varname>path</varname> is added to the store it is with a
|
||||
flat hash, rather than a hash of the NAR serialization of
|
||||
the file. Thus, <varname>path</varname> must refer to a
|
||||
regular file, not a directory. This allows similar
|
||||
behavior to <literal>fetchurl</literal>. Defaults to
|
||||
<literal>true</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>sha256</term>
|
||||
<listitem>
|
||||
<para>
|
||||
When provided, this is the expected hash of the file at
|
||||
the path. Evaluation will fail if the hash is incorrect,
|
||||
and providing a hash allows
|
||||
<literal>builtins.path</literal> to be used even when the
|
||||
<literal>pure-eval</literal> nix config option is on.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.pathExists</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<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="sec-debug-build">
|
||||
|
||||
<title>Debugging Build Failures</title>
|
||||
|
||||
<para>At the beginning of each phase of the build (such as unpacking,
|
||||
building or installing), the set of all shell variables is written to
|
||||
the file <filename>env-vars</filename> at the top-level build
|
||||
directory. This is useful for debugging: it allows you to recreate
|
||||
the environment in which a build was performed. For instance, if a
|
||||
build fails, then assuming you used the <option>-K</option> flag, you
|
||||
can go to the output directory and <quote>switch</quote> to the
|
||||
environment of the builder:
|
||||
|
||||
<screen>
|
||||
$ nix-build -K ./foo.nix
|
||||
... fails, keeping build directory `/tmp/nix-1234-0'
|
||||
|
||||
$ cd /tmp/nix-1234-0
|
||||
|
||||
$ source env-vars
|
||||
|
||||
<lineannotation>(edit some files...)</lineannotation>
|
||||
|
||||
$ make
|
||||
|
||||
<lineannotation>(execution continues with the same GCC, make, etc.)</lineannotation></screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
@@ -61,7 +61,7 @@ evaluates to <literal>"foobar"</literal>.
|
||||
|
||||
<simplesect><title>Inheriting attributes</title>
|
||||
|
||||
<para>When defining a set it is often convenient to copy variables
|
||||
<para>When defining a set or in a let-expression it is often convenient to copy variables
|
||||
from the surrounding lexical scope (e.g., when you want to propagate
|
||||
attributes). This can be shortened using the
|
||||
<literal>inherit</literal> keyword. For instance,
|
||||
@@ -72,7 +72,15 @@ let x = 123; in
|
||||
y = 456;
|
||||
}</programlisting>
|
||||
|
||||
evaluates to <literal>{ x = 123; y = 456; }</literal>. (Note that
|
||||
is equivalent to
|
||||
|
||||
<programlisting>
|
||||
let x = 123; in
|
||||
{ x = x;
|
||||
y = 456;
|
||||
}</programlisting>
|
||||
|
||||
and both evaluate to <literal>{ x = 123; y = 456; }</literal>. (Note that
|
||||
this works because <varname>x</varname> is added to the lexical scope
|
||||
by the <literal>let</literal> construct.) It is also possible to
|
||||
inherit attributes from another set. For instance, in this fragment
|
||||
@@ -101,6 +109,26 @@ variables from the surrounding scope (<varname>fetchurl</varname>
|
||||
<varname>libXaw</varname> (the X Athena Widgets) from the
|
||||
<varname>xlibs</varname> (X11 client-side libraries) set.</para>
|
||||
|
||||
<para>
|
||||
Summarizing the fragment
|
||||
|
||||
<programlisting>
|
||||
...
|
||||
inherit x y z;
|
||||
inherit (src-set) a b c;
|
||||
...</programlisting>
|
||||
|
||||
is equivalent to
|
||||
|
||||
<programlisting>
|
||||
...
|
||||
x = x; y = y; z = z;
|
||||
a = src-set.a; b = src-set.b; c = src-set.c;
|
||||
...</programlisting>
|
||||
|
||||
when used while defining local variables in a let-expression or
|
||||
while defining a set.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
|
||||
@@ -81,6 +81,4 @@ Just pass the option <link linkend='opt-max-jobs'><option>-j
|
||||
in parallel, or set. Typically this should be the number of
|
||||
CPUs.</para>
|
||||
|
||||
<xi:include href="debug-build.xml" />
|
||||
|
||||
</section>
|
||||
|
||||
@@ -79,16 +79,6 @@ alice$ ./install
|
||||
|
||||
</para>
|
||||
|
||||
<para>Nix can be uninstalled using <command>rpm -e nix</command> or
|
||||
<command>dpkg -r nix</command> on RPM- and Dpkg-based systems,
|
||||
respectively. After this you should manually remove the Nix store and
|
||||
other auxiliary data, if desired:
|
||||
|
||||
<screen>
|
||||
$ rm -rf /nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>You can uninstall Nix simply by running:
|
||||
|
||||
<screen>
|
||||
|
||||
@@ -52,34 +52,6 @@ This creates 10 build users. There can never be more concurrent builds
|
||||
than the number of build users, so you may want to increase this if
|
||||
you expect to do many builds at the same time.</para>
|
||||
|
||||
<para>On macOS, you can create the required group and users by
|
||||
running the following script:
|
||||
|
||||
<programlisting>
|
||||
#! /bin/bash -e
|
||||
|
||||
dseditgroup -o create nixbld -q
|
||||
|
||||
gid=$(dscl . -read /Groups/nixbld | awk '($1 == "PrimaryGroupID:") {print $2 }')
|
||||
|
||||
echo "created nixbld group with gid $gid"
|
||||
|
||||
for i in $(seq 1 10); do
|
||||
user=/Users/nixbld$i
|
||||
uid="$((30000 + $i))"
|
||||
dscl . create $user
|
||||
dscl . create $user RealName "Nix build user $i"
|
||||
dscl . create $user PrimaryGroupID "$gid"
|
||||
dscl . create $user UserShell /usr/bin/false
|
||||
dscl . create $user NFSHomeDirectory /var/empty
|
||||
dscl . create $user UniqueID "$uid"
|
||||
dseditgroup -o edit -a nixbld$i -t user nixbld
|
||||
echo "created nixbld$i user with uid $uid"
|
||||
done
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,4 @@
|
||||
|
||||
</para>
|
||||
|
||||
<para>Nix is fairly portable, so it should work on most platforms that
|
||||
support POSIX threads and have a C++11 compiler.</para>
|
||||
|
||||
</chapter>
|
||||
|
||||
@@ -15,7 +15,7 @@ to subsequent chapters.</para>
|
||||
<step><para>Install single-user Nix by running the following:
|
||||
|
||||
<screen>
|
||||
$ curl https://nixos.org/nix/install | sh
|
||||
$ bash <(curl https://nixos.org/nix/install)
|
||||
</screen>
|
||||
|
||||
This will install Nix in <filename>/nix</filename>. The install script
|
||||
|
||||
@@ -12,19 +12,14 @@
|
||||
<firstname>Eelco</firstname>
|
||||
<surname>Dolstra</surname>
|
||||
</personname>
|
||||
<affiliation>
|
||||
<orgname>LogicBlox</orgname>
|
||||
</affiliation>
|
||||
<contrib>Author</contrib>
|
||||
</author>
|
||||
|
||||
<copyright>
|
||||
<year>2004-2014</year>
|
||||
<year>2004-2018</year>
|
||||
<holder>Eelco Dolstra</holder>
|
||||
</copyright>
|
||||
|
||||
<date>November 2014</date>
|
||||
|
||||
</info>
|
||||
|
||||
<!--
|
||||
@@ -41,7 +36,6 @@
|
||||
<xi:include href="expressions/writing-nix-expressions.xml" />
|
||||
<xi:include href="advanced-topics/advanced-topics.xml" />
|
||||
<xi:include href="command-ref/command-ref.xml" />
|
||||
<xi:include href="troubleshooting/troubleshooting.xml" />
|
||||
<xi:include href="glossary/glossary.xml" />
|
||||
<xi:include href="hacking.xml" />
|
||||
<xi:include href="release-notes/release-notes.xml" />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</partintro>
|
||||
-->
|
||||
|
||||
<xi:include href="rl-1.12.xml" />
|
||||
<xi:include href="rl-2.0.xml" />
|
||||
<xi:include href="rl-1.11.10.xml" />
|
||||
<xi:include href="rl-1.11.xml" />
|
||||
<xi:include href="rl-1.10.xml" />
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
<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.12">
|
||||
|
||||
<title>Release 1.12 (TBA)</title>
|
||||
|
||||
<para>This release has the following new features:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Start of new <command>nix</command> command line
|
||||
interface. This is a work in progress and the interface is subject
|
||||
to change.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Self-documenting: <option>--help</option> shows
|
||||
all available command-line arguments.</para></listitem>
|
||||
|
||||
<listitem><para><option>--help-config</option> shows all
|
||||
configuration options.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix build</command>: Replacement for
|
||||
<command>nix-build</command>.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix ls-store</command> and <command>nix
|
||||
ls-nar</command> allow listing the contents of a store path or
|
||||
NAR file.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix cat-store</command> and
|
||||
<command>nix cat-nar</command> allow extracting a file from a
|
||||
store path or NAR file.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix verify</command> checks whether a
|
||||
store path is unmodified and/or is trusted.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix copy-sigs</command> copies
|
||||
signatures from one store to another.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix sign-paths</command> signs store
|
||||
paths.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix copy</command> copies paths between
|
||||
arbitrary Nix stores, generalising
|
||||
<command>nix-copy-closure</command> and
|
||||
<command>nix-push</command>.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix path-info</command> shows
|
||||
information about store paths.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix run</command> starts a shell in
|
||||
which the specified packages are available.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix log</command> shows the build log
|
||||
of a package or path. If the build log is not available locally,
|
||||
it will try to obtain it from a binary cache.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix eval</command> replaces
|
||||
<command>nix-instantiate --eval</command>.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix dump-path</command> to get a NAR
|
||||
from a store path.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix edit</command> opens the source
|
||||
code of a package in an editor.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix search</command> replaces
|
||||
<command>nix-env -qa</command>. It searches the available
|
||||
packages for occurences of a search string in the attribute
|
||||
name, package name or description. It caches available packages
|
||||
to speed up searches.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix why-depends</command> (d41c5eb13f4f3a37d80dbc6d3888644170c3b44a).</para></listitem>
|
||||
|
||||
<listitem><para><command>nix show-derivation</command> (e8d6ee7c1b90a2fe6d824f1a875acc56799ae6e2).</para></listitem>
|
||||
|
||||
<listitem><para><command>nix add-to-store</command> (970366266b8df712f5f9cedb45af183ef5a8357f).</para></listitem>
|
||||
|
||||
<listitem><para>Progress indicator.</para></listitem>
|
||||
|
||||
<listitem><para>All options are available as flags now
|
||||
(b8283773bd64d7da6859ed520ee19867742a03ba).</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The external program <command>nix-repl</command> has been
|
||||
integrated into Nix as <command>nix repl</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New build mode <command>nix-build --hash</command> that
|
||||
builds a derivation, computes the hash of the output, and moves
|
||||
the output to the store path corresponding to what a fixed-output
|
||||
derivation with that hash would produce.
|
||||
(Add docs and examples; see d367b8e7875161e655deaa96bf8a5dd0bcf8229e)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>It is no longer necessary to set the
|
||||
<envar>NIX_REMOTE</envar> environment variable if you need to use
|
||||
the Nix daemon. Nix will use the daemon automatically if you don’t
|
||||
have write access to the Nix database.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The Nix language now supports floating point numbers. They are
|
||||
based on regular C++ <literal>float</literal> and compatible with
|
||||
existing integers and number-related operations. Export and import to and
|
||||
from JSON and XML works, too.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> now sets the
|
||||
<varname>IN_NIX_SHELL</varname> environment variable during
|
||||
evaluation and in the shell itself. This can be used to perform
|
||||
different actions depending on whether you’re in a Nix shell or in
|
||||
a regular build. Nixpkgs provides
|
||||
<varname>lib.inNixShell</varname> to check this variable during
|
||||
evaluation. (bb36a1a3cf3fbe6bc9d0afcc5fa0f928bed03170)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Internal: all <classname>Store</classname> classes are now
|
||||
thread-safe. <classname>RemoteStore</classname> supports multiple
|
||||
concurrent connections to the daemon. This is primarily useful in
|
||||
multi-threaded programs such as
|
||||
<command>hydra-queue-runner</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The dependency on Perl has been removed. As a result, some
|
||||
(obsolete) programs have been removed: <command>nix-push</command>
|
||||
(replaced by <command>nix copy</command>),
|
||||
<command>nix-pull</command> (obsoleted by binary caches),
|
||||
<command>nix-generate-patches</command>,
|
||||
<command>bsdiff</command>, <command>bspatch</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Improved store abstraction. Substituters
|
||||
eliminated. BinaryCacheStore, LocalBinaryCacheStore,
|
||||
HttpBinaryCacheStore, S3BinaryCacheStore (compile-time
|
||||
optional), SSHStore. Add docs + examples?
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now stores signatures for local store
|
||||
paths. Locally-built paths are now signed automatically using the
|
||||
secret keys specified by the <option>secret-key-files</option>
|
||||
store option.</para>
|
||||
|
||||
<para>In addition, store paths that have been built locally are
|
||||
marked as “ultimately trusted”, and content-addressable store
|
||||
paths carry a “content-addressability assertion” that allow them
|
||||
to be trusted without any signatures.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><envar>NIX_PATH</envar> is now lazy, so URIs in the path are
|
||||
only downloaded if they are needed for evaluation.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>You can now use
|
||||
<uri>channel:<replaceable>channel-name</replaceable></uri> as a
|
||||
short-hand for
|
||||
<uri>https://nixos.org/channels/<replaceable>channel-name</replaceable>/nixexprs.tar.xz</uri>. For
|
||||
example, <literal>nix-build channel:nixos-15.09 -A hello</literal>
|
||||
will build the GNU Hello package from the
|
||||
<literal>nixos-15.09</literal> channel.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>When <option>--no-build-output</option> is given, the last
|
||||
10 lines of the build log will be shown if a build
|
||||
fails.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>builtins.fetchGit</function>.
|
||||
(38539b943a060d9cdfc24d6e5d997c0885b8aa2f)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal><nix/fetchurl.nix></literal> now uses the
|
||||
content-addressable tarball cache at
|
||||
<uri>http://tarballs.nixos.org/</uri>, just like
|
||||
<function>fetchurl</function> in
|
||||
Nixpkgs. (f2682e6e18a76ecbfb8a12c17e3a0ca15c084197)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Chroot Nix stores: allow the “physical” location of the Nix
|
||||
store (e.g. <filename>/home/alice/nix/store</filename>) to differ
|
||||
from its “logical” location (typically
|
||||
<filename>/nix/store</filename>). This allows non-root users to
|
||||
use Nix while still getting the benefits from prebuilt binaries
|
||||
from
|
||||
<uri>cache.nixos.org</uri>. (4494000e04122f24558e1436e66d20d89028b4bd,
|
||||
3eb621750848e0e6b30e5a79f76afbb096bb6c8a)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>On Linux, builds are now executed in a user
|
||||
namespace with uid 1000 and gid 100.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><function>builtins.fetchurl</function> and
|
||||
<function>builtins.fetchTarball</function> now support
|
||||
<varname>sha256</varname> and <varname>name</varname>
|
||||
attributes.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>HttpBinaryCacheStore</literal> (the replacement of
|
||||
<command>download-from-binary-cache</command>) now retries
|
||||
automatically on certain HTTP error codes.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Derivation attributes can now reference the outputs of the
|
||||
derivation using the <function>placeholder</function> builtin
|
||||
function. For example, the attribute
|
||||
|
||||
<programlisting>
|
||||
configureFlags = "--prefix=${placeholder "out"} --includedir=${placeholder "dev"}";
|
||||
</programlisting>
|
||||
|
||||
will cause the <envar>configureFlags</envar> environment variable
|
||||
to contain the actual store paths corresponding to the
|
||||
<literal>out</literal> and <literal>dev</literal> outputs. TODO:
|
||||
add docs.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for HTTP/2. This makes binary cache lookups much
|
||||
more efficient. (90ad02bf626b885a5dd8967894e2eafc953bdf92)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The <option>build-sandbox-paths</option> configuration
|
||||
option can now specify optional paths by appending a
|
||||
<literal>?</literal>, e.g. <literal>/dev/nvidiactl?</literal> will
|
||||
bind-mount <varname>/dev/nvidiactl</varname> only if it
|
||||
exists.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>More support for testing build reproducibility: when
|
||||
<option>enforce-determinism</option> is set to
|
||||
<literal>false</literal>, it’s no longer a fatal error build
|
||||
rounds produce different output
|
||||
(8bdf83f936adae6f2c907a6d2541e80d4120f051); add a hook to run
|
||||
diffoscope when build rounds produce different output
|
||||
(9a313469a4bdea2d1e8df24d16289dc2a172a169w).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Kill builds as soon as stdout/stderr is closed. This fixes a
|
||||
bug that allowed builds to hang Nix indefinitely (regardless of
|
||||
timeouts). (21948deed99a3295e4d5666e027a6ca42dc00b40)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Add support for passing structured data to builders. TODO:
|
||||
document. (6de33a9c675b187437a2e1abbcb290981a89ecb1)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>exportReferencesGraph</varname>: Export more
|
||||
complete info in JSON
|
||||
format. (c2b0d8749f7e77afc1c4b3e8dd36b7ee9720af4a)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for
|
||||
netrc. (e6e74f987f0fa284d220432d426eb965269a97d6,
|
||||
302386f775eea309679654e5ea7c972fb6e7b9af)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support <uri>s3://</uri> URIs in all places where Nix allows
|
||||
URIs. (9ff9c3f2f80ba4108e9c945bbfda2c64735f987b)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The <option>build-max-jobs</option> option can be set to
|
||||
<literal>auto</literal> to use the number of CPUs in the
|
||||
system. (7251d048fa812d2551b7003bc9f13a8f5d4c95a5)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Add support for Brotli compression.
|
||||
<uri>cache.nixos.org</uri> compresses build logs using
|
||||
Brotli.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Substitutions from binary caches now require signatures by
|
||||
default. This was already the case on
|
||||
NixOS. (ecbc3fedd3d5bdc5a0e1a0a51b29062f2874ac8b)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-env</command> now ignores packages with bad
|
||||
derivation names (in particular those starting with a digit or
|
||||
containing a
|
||||
dot). (b0cb11722626e906a73f10dd9a0c9eea29faf43a)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Renamed various configuration options. (TODO: in progress)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Remote machines can now be specified on the command
|
||||
line. TODO:
|
||||
document. (1a68710d4dff609bbaf61db3e17a2573f0aadf17)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>In Linux sandbox builds, we now use
|
||||
<filename>/build</filename> instead of <filename>/tmp</filename>
|
||||
as the temporary build directory. This fixes potential security
|
||||
problems when a build accidentally stores its
|
||||
<envar>TMPDIR</envar> in some critical place, such as an
|
||||
RPATH. (eba840c8a13b465ace90172ff76a0db2899ab11b)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>In Linux sandbox builds, we now provide a default
|
||||
<filename>/bin/sh</filename> (namely <filename>ash</filename> from
|
||||
BusyBox). (a2d92bb20e82a0957067ede60e91fab256948b41)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Make all configuration options available as command line
|
||||
flags (b8283773bd64d7da6859ed520ee19867742a03ba).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support base-64
|
||||
hashes. (c0015e87af70f539f24d2aa2bc224a9d8b84276b)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> now uses
|
||||
<varname>bashInteractive</varname> from Nixpkgs, rather than the
|
||||
<command>bash</command> command that happens to be in the caller’s
|
||||
<envar>PATH</envar>. This is especially important on macOS where
|
||||
the <command>bash</command> provided by the system is seriously
|
||||
outdated and cannot execute <literal>stdenv</literal>’s setup
|
||||
script.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New builtin functions: <function>builtins.split</function>
|
||||
(b8867a0239b1930a16f9ef3f7f3e864b01416dff),
|
||||
<function>builtins.partition</function>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Automatic garbage collection.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-store -q --roots</command> and
|
||||
<command>nix-store --gc --print-roots</command> now show temporary
|
||||
and in-memory roots.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Builders can now communicate what build phase they are in by
|
||||
writing messages to the file descriptor specified in
|
||||
<envar>NIX_LOG_FD</envar>. (88e6bb76de5564b3217be9688677d1c89101b2a3)
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>Some features were removed:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>“Nested” log output. As a result,
|
||||
<command>nix-log2xml</command> was also removed.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>OpenSSL-based signing. (f435f8247553656774dd1b2c88e9de5d59cab203)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Caching of failed
|
||||
builds. (8cffec84859cec8b610a2a22ab0c4d462a9351ff)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><filename>nix-mode.el</filename> has been removed from
|
||||
Nix. It is now a separate repository in
|
||||
<uri>https://github.com/NixOS/nix-mode</uri> and can be installed
|
||||
through the MELPA package repository.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>In restricted evaluation mode
|
||||
(<option>--restrict-eval</option>), builtin functions that
|
||||
download from the network (such as <function>fetchGit</function>)
|
||||
are permitted to fetch underneath the list of URI prefixes
|
||||
specified in the option <option>allowed-uris</option>.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>This release has contributions from TBD.</para>
|
||||
|
||||
</section>
|
||||
1014
doc/manual/release-notes/rl-2.0.xml
Normal file
1014
doc/manual/release-notes/rl-2.0.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -96,7 +96,6 @@ div.example
|
||||
margin-right: 1.5em;
|
||||
background: #f4f4f8;
|
||||
border-radius: 0.4em;
|
||||
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
|
||||
}
|
||||
|
||||
div.example p.title
|
||||
@@ -106,7 +105,6 @@ div.example p.title
|
||||
|
||||
div.example pre
|
||||
{
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,15 +114,12 @@ div.example pre
|
||||
|
||||
pre.screen, pre.programlisting
|
||||
{
|
||||
border: 1px solid #b0b0b0;
|
||||
padding: 3px 3px;
|
||||
padding: 6px 6px;
|
||||
margin-left: 1.5em;
|
||||
margin-right: 1.5em;
|
||||
color: #600000;
|
||||
background: #f4f4f8;
|
||||
font-family: monospace;
|
||||
border-radius: 0.4em;
|
||||
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
|
||||
}
|
||||
|
||||
div.example pre.programlisting
|
||||
@@ -149,7 +144,6 @@ div.example pre.programlisting
|
||||
padding: 0.3em 0.3em 0.3em 0.3em;
|
||||
background: #fffff5;
|
||||
border-radius: 0.4em;
|
||||
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
|
||||
}
|
||||
|
||||
div.note, div.warning
|
||||
@@ -256,16 +250,14 @@ span.command strong
|
||||
|
||||
div.calloutlist table
|
||||
{
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-collapse: collapse;
|
||||
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
|
||||
}
|
||||
|
||||
div.affiliation
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<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="sec-collisions-nixenv">
|
||||
|
||||
<title>Collisions in <command>nix-env</command></title>
|
||||
|
||||
<para>Symptom: when installing or upgrading, you get an error message such as
|
||||
|
||||
<screen>
|
||||
$ nix-env -i docbook-xml
|
||||
...
|
||||
adding /nix/store/s5hyxgm62gk2...-docbook-xml-4.2
|
||||
collision between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd'
|
||||
and `/nix/store/06h377hr4b33...-docbook-xml-4.3/xml/dtd/docbook/calstblx.dtd'
|
||||
at /nix/store/...-builder.pl line 62.</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The cause is that two installed packages in the user environment
|
||||
have overlapping filenames (e.g.,
|
||||
<filename>xml/dtd/docbook/calstblx.dtd</filename>. This usually
|
||||
happens when you accidentally try to install two versions of the same
|
||||
package. For instance, in the example above, the Nix Packages
|
||||
collection contains two versions of <literal>docbook-xml</literal>, so
|
||||
<command>nix-env -i</command> will try to install both. The default
|
||||
user environment builder has no way to way to resolve such conflicts,
|
||||
so it just gives up.</para>
|
||||
|
||||
<para>Solution: remove one of the offending packages from the user
|
||||
environment (if already installed) using <command>nix-env
|
||||
-e</command>, or specify exactly which version should be installed
|
||||
(e.g., <literal>nix-env -i docbook-xml-4.2</literal>).</para>
|
||||
|
||||
<!-- FIXME: describe priorities -->
|
||||
|
||||
</section>
|
||||
@@ -1,43 +0,0 @@
|
||||
<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="sec-links-nix-store">
|
||||
|
||||
<title><quote>Too many links</quote> Error in the Nix store</title>
|
||||
|
||||
|
||||
<para>Symptom: when building something, you get an error message such as
|
||||
|
||||
<screen>
|
||||
...
|
||||
<literal>mkdir: cannot create directory `/nix/store/<replaceable>name</replaceable>': Too many links</literal></screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>This is usually because you have more than 32,000 subdirectories
|
||||
in <filename>/nix/store</filename>, as can be seen using <command>ls
|
||||
-l</command>:
|
||||
|
||||
<screen>
|
||||
$ ls -ld /nix/store
|
||||
drwxrwxrwt 32000 nix nix 4620288 Sep 8 15:08 store</screen>
|
||||
|
||||
The <literal>ext2</literal> file system is limited to an inode link
|
||||
count of 32,000 (each subdirectory increasing the count by one).
|
||||
Furthermore, the <literal>st_nlink</literal> field of the
|
||||
<function>stat</function> system call is a 16-bit value.</para>
|
||||
|
||||
<para>This only happens on very large Nix installations (such as build
|
||||
machines).</para>
|
||||
|
||||
<para>Quick solution: run the garbage collector. You may want to use
|
||||
the <option>--max-links</option> option.</para>
|
||||
|
||||
<para>Real solution: put the Nix store on a file system that supports
|
||||
more than 32,000 subdirectories per directory, such as ext4. (This
|
||||
doesn’t solve the <literal>st_nlink</literal> limit, but ext4 lies to
|
||||
the kernel by reporting a link count of 1 if it exceeds the
|
||||
limit.)</para>
|
||||
|
||||
</section>
|
||||
@@ -1,16 +0,0 @@
|
||||
<appendix 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="ch-troubleshooting">
|
||||
|
||||
<title>Troubleshooting</title>
|
||||
|
||||
<para>This section provides solutions for some common problems. See
|
||||
the <link xlink:href="https://github.com/NixOS/nix/issues">Nix bug
|
||||
tracker</link> for a list of currently known issues.</para>
|
||||
|
||||
<xi:include href="collisions-nixenv.xml" />
|
||||
<xi:include href="links-nix-store.xml" />
|
||||
|
||||
</appendix>
|
||||
@@ -45,6 +45,11 @@ endif
|
||||
# - $(1)_INSTALL_DIR: the directory where the library will be
|
||||
# installed. Defaults to $(libdir).
|
||||
#
|
||||
# - $(1)_EXCLUDE_FROM_LIBRARY_LIST: if defined, the library will not
|
||||
# be automatically marked as a dependency of the top-level all
|
||||
# target andwill not be listed in the make help output. This is
|
||||
# useful for libraries built solely for testing, for example.
|
||||
#
|
||||
# - BUILD_SHARED_LIBS: if equal to ‘1’, a dynamic library will be
|
||||
# built, otherwise a static library.
|
||||
define build-library
|
||||
@@ -149,7 +154,9 @@ define build-library
|
||||
$(1)_DEPS := $$(foreach fn, $$($(1)_OBJS), $$(call filename-to-dep, $$(fn)))
|
||||
-include $$($(1)_DEPS)
|
||||
|
||||
ifndef $(1)_EXCLUDE_FROM_LIBRARY_LIST
|
||||
libs-list += $$($(1)_PATH)
|
||||
endif
|
||||
clean-files += $$(_d)/*.a $$(_d)/*.$(SO_EXT) $$(_d)/*.o $$(_d)/.*.dep $$($(1)_DEPS) $$($(1)_OBJS)
|
||||
dist-files += $$(_srcs)
|
||||
endef
|
||||
|
||||
@@ -39,7 +39,7 @@ installcheck:
|
||||
echo "$${red}$$failed out of $$total tests failed $$normal"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "$${green}All tests succeeded"; \
|
||||
echo "$${green}All tests succeeded$$normal"; \
|
||||
fi
|
||||
|
||||
.PHONY: check installcheck
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
{ pkgs }:
|
||||
|
||||
rec {
|
||||
sh = pkgs.busybox.override {
|
||||
# Use "busybox-sandbox-shell" if present,
|
||||
# if not (legacy) fallback and hope it's sufficient.
|
||||
sh = pkgs.busybox-sandbox-shell or (pkgs.busybox.override {
|
||||
useMusl = true;
|
||||
enableStatic = true;
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_FEATURE_FANCY_ECHO y
|
||||
CONFIG_FEATURE_SH_MATH y
|
||||
CONFIG_FEATURE_SH_MATH_64 y
|
||||
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_TEST y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
|
||||
CONFIG_ASH_ALIAS y
|
||||
CONFIG_ASH_BASH_COMPAT y
|
||||
CONFIG_ASH_CMDCMD y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_GETOPTS y
|
||||
CONFIG_ASH_INTERNAL_GLOB y
|
||||
CONFIG_ASH_JOB_CONTROL y
|
||||
CONFIG_ASH_PRINTF y
|
||||
CONFIG_ASH_TEST y
|
||||
'';
|
||||
};
|
||||
});
|
||||
|
||||
configureFlags =
|
||||
[ "--disable-init-state"
|
||||
|
||||
25
release.nix
25
release.nix
@@ -1,12 +1,12 @@
|
||||
{ nix ? builtins.fetchGit ./.
|
||||
, nixpkgs ? fetchTarball channel:nixos-17.09
|
||||
, nixpkgs ? builtins.fetchGit { url = https://github.com/NixOS/nixpkgs.git; ref = "nix-2.0"; }
|
||||
, officialRelease ? false
|
||||
, systems ? [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ]
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
pkgs = import nixpkgs {};
|
||||
pkgs = import nixpkgs { system = builtins.currentSystem or "x86_64-linux"; };
|
||||
|
||||
jobs = rec {
|
||||
|
||||
@@ -127,7 +127,6 @@ let
|
||||
|
||||
binaryTarball = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
# FIXME: temporarily use a different branch for the Darwin build.
|
||||
with import nixpkgs { inherit system; };
|
||||
|
||||
let
|
||||
@@ -137,7 +136,7 @@ let
|
||||
|
||||
runCommand "nix-binary-tarball-${version}"
|
||||
{ exportReferencesGraph = [ "closure1" toplevel "closure2" cacert ];
|
||||
buildInputs = [ perl shellcheck ];
|
||||
buildInputs = [ perl ] ++ lib.optional (system != "aarch64-linux") shellcheck;
|
||||
meta.description = "Distribution-independent Nix bootstrap binaries for ${system}";
|
||||
}
|
||||
''
|
||||
@@ -150,8 +149,10 @@ let
|
||||
--subst-var-by nix ${toplevel} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
shellcheck -e SC1090 $TMPDIR/install
|
||||
shellcheck -e SC1091,SC2002 $TMPDIR/install-darwin-multi-user
|
||||
if type -p shellcheck; then
|
||||
shellcheck -e SC1090 $TMPDIR/install
|
||||
shellcheck -e SC1091,SC2002 $TMPDIR/install-darwin-multi-user
|
||||
fi
|
||||
|
||||
chmod +x $TMPDIR/install
|
||||
chmod +x $TMPDIR/install-darwin-multi-user
|
||||
@@ -224,11 +225,13 @@ let
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
});
|
||||
|
||||
tests.setuid = pkgs.lib.genAttrs (pkgs.lib.filter (pkgs.lib.hasSuffix "-linux") systems) (system:
|
||||
import ./tests/setuid.nix rec {
|
||||
inherit nixpkgs;
|
||||
nix = build.${system}; inherit system;
|
||||
});
|
||||
tests.setuid = pkgs.lib.genAttrs
|
||||
["i686-linux" "x86_64-linux"]
|
||||
(system:
|
||||
import ./tests/setuid.nix rec {
|
||||
inherit nixpkgs;
|
||||
nix = build.${system}; inherit system;
|
||||
});
|
||||
|
||||
tests.binaryTarball =
|
||||
with import nixpkgs { system = "x86_64-linux"; };
|
||||
|
||||
@@ -24,7 +24,7 @@ readonly YELLOW='\033[38;33m'
|
||||
readonly YELLOW_UL='\033[38;4;33m'
|
||||
|
||||
readonly CORES=$(sysctl -n hw.ncpu)
|
||||
readonly NIX_USER_COUNT="$CORES"
|
||||
readonly NIX_USER_COUNT="32"
|
||||
readonly NIX_BUILD_GROUP_ID="30000"
|
||||
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
||||
readonly NIX_FIRST_BUILD_UID="30001"
|
||||
@@ -647,7 +647,7 @@ chat_about_sudo() {
|
||||
cat <<EOF
|
||||
This script is going to call sudo a lot. Normally, it would show you
|
||||
exactly what commands it is running and why. However, the script is
|
||||
run in a headless fashion, like this:
|
||||
run in a headless fashion, like this:
|
||||
|
||||
$ curl https://nixos.org/nix/install | sh
|
||||
|
||||
@@ -695,7 +695,7 @@ install_from_extracted_nix() {
|
||||
cd "$EXTRACTED_NIX_PATH"
|
||||
|
||||
_sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
|
||||
rsync -rlpt "$(pwd)/store/" "$NIX_ROOT/store/"
|
||||
rsync -rlpt ./store/* "$NIX_ROOT/store/"
|
||||
|
||||
if [ -d "$NIX_INSTALLED_NIX" ]; then
|
||||
echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"
|
||||
|
||||
@@ -75,7 +75,7 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ca-bundle.crt"
|
||||
fi
|
||||
|
||||
if [ -n ${MANPATH} ]; then
|
||||
if [ -n "${MANPATH}" ]; then
|
||||
export MANPATH="$NIX_LINK/share/man:$MANPATH"
|
||||
fi
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ int main (int argc, char * * argv)
|
||||
|
||||
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
||||
|
||||
initPlugins();
|
||||
|
||||
auto store = openStore().cast<LocalStore>();
|
||||
|
||||
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
|
||||
@@ -241,7 +243,7 @@ connected:
|
||||
|
||||
if (!missing.empty()) {
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
|
||||
setenv("NIX_HELD_LOCKS", concatStringsSep(" ", missing).c_str(), 1); /* FIXME: ugly */
|
||||
store->locksHeld.insert(missing.begin(), missing.end()); /* FIXME: ugly */
|
||||
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, substitute);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||
static void * allocBytes(size_t n)
|
||||
{
|
||||
void * p;
|
||||
#if HAVE_BOEHMGC
|
||||
p = GC_malloc(n);
|
||||
#else
|
||||
p = malloc(n);
|
||||
p = calloc(n, 1);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
return p;
|
||||
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
for (size_t n = 0; n < size_; n++)
|
||||
res.emplace_back(&attrs[n]);
|
||||
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
|
||||
return (string) a->name < (string) b->name;
|
||||
return (const string &) a->name < (const string &) b->name;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -43,13 +43,14 @@ static char * dupString(const char * s)
|
||||
}
|
||||
|
||||
|
||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||
static void * allocBytes(size_t n)
|
||||
{
|
||||
void * p;
|
||||
#if HAVE_BOEHMGC
|
||||
p = GC_malloc(n);
|
||||
#else
|
||||
p = malloc(n);
|
||||
p = calloc(n, 1);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
return p;
|
||||
@@ -293,6 +294,10 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||
, sWrong(symbols.create("wrong"))
|
||||
, sStructuredAttrs(symbols.create("__structuredAttrs"))
|
||||
, sBuilder(symbols.create("builder"))
|
||||
, sArgs(symbols.create("args"))
|
||||
, sOutputHash(symbols.create("outputHash"))
|
||||
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
|
||||
, sOutputHashMode(symbols.create("outputHashMode"))
|
||||
, repair(NoRepair)
|
||||
, store(store)
|
||||
, baseEnv(allocEnv(128))
|
||||
@@ -300,15 +305,24 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||
{
|
||||
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
||||
|
||||
restricted = settings.restrictEval;
|
||||
|
||||
assert(gcInitialised);
|
||||
|
||||
/* Initialise the Nix expression search path. */
|
||||
Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
|
||||
for (auto & i : _searchPath) addToSearchPath(i);
|
||||
for (auto & i : paths) addToSearchPath(i);
|
||||
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
||||
if (!settings.pureEval) {
|
||||
Strings paths = parseNixPath(getEnv("NIX_PATH", ""));
|
||||
for (auto & i : _searchPath) addToSearchPath(i);
|
||||
for (auto & i : paths) addToSearchPath(i);
|
||||
}
|
||||
addToSearchPath("nix=" + canonPath(settings.nixDataDir + "/nix/corepkgs"));
|
||||
|
||||
if (settings.restrictEval || settings.pureEval) {
|
||||
allowedPaths = PathSet();
|
||||
for (auto & i : searchPath) {
|
||||
auto r = resolveSearchPathElem(i);
|
||||
if (!r.first) continue;
|
||||
allowedPaths->insert(r.second);
|
||||
}
|
||||
}
|
||||
|
||||
clearValue(vEmptySet);
|
||||
vEmptySet.type = tAttrs;
|
||||
@@ -326,38 +340,36 @@ EvalState::~EvalState()
|
||||
|
||||
Path EvalState::checkSourcePath(const Path & path_)
|
||||
{
|
||||
if (!restricted) return path_;
|
||||
if (!allowedPaths) return path_;
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (auto & i : *allowedPaths) {
|
||||
if (isDirOrInDir(path_, i)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path_);
|
||||
|
||||
/* Resolve symlinks. */
|
||||
debug(format("checking access to '%s'") % path_);
|
||||
Path path = canonPath(path_, true);
|
||||
|
||||
for (auto & i : searchPath) {
|
||||
auto r = resolveSearchPathElem(i);
|
||||
if (!r.first) continue;
|
||||
if (path == r.second || isInDir(path, r.second))
|
||||
for (auto & i : *allowedPaths) {
|
||||
if (isDirOrInDir(path, i))
|
||||
return path;
|
||||
}
|
||||
|
||||
/* To support import-from-derivation, allow access to anything in
|
||||
the store. FIXME: only allow access to paths that have been
|
||||
constructed by this evaluation. */
|
||||
if (store->isInStore(path)) return path;
|
||||
|
||||
#if 0
|
||||
/* Hack to support the chroot dependencies of corepkgs (see
|
||||
corepkgs/config.nix.in). */
|
||||
if (path == settings.nixPrefix && isStorePath(settings.nixPrefix))
|
||||
return path;
|
||||
#endif
|
||||
|
||||
throw RestrictedPathError(format("access to path '%1%' is forbidden in restricted mode") % path_);
|
||||
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::checkURI(const std::string & uri)
|
||||
{
|
||||
if (!restricted) return;
|
||||
if (!settings.restrictEval) return;
|
||||
|
||||
/* 'uri' should be equal to a prefix, or in a subdirectory of a
|
||||
prefix. Thus, the prefix https://github.co does not permit
|
||||
@@ -371,11 +383,33 @@ void EvalState::checkURI(const std::string & uri)
|
||||
&& (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
|
||||
return;
|
||||
|
||||
/* If the URI is a path, then check it against allowedPaths as
|
||||
well. */
|
||||
if (hasPrefix(uri, "/")) {
|
||||
checkSourcePath(uri);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPrefix(uri, "file://")) {
|
||||
checkSourcePath(std::string(uri, 7));
|
||||
return;
|
||||
}
|
||||
|
||||
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::addConstant(const string & name, Value & v)
|
||||
Path EvalState::toRealPath(const Path & path, const PathSet & context)
|
||||
{
|
||||
// FIXME: check whether 'path' is in 'context'.
|
||||
return
|
||||
!context.empty() && store->isInStore(path)
|
||||
? store->toRealPath(path)
|
||||
: path;
|
||||
};
|
||||
|
||||
|
||||
Value * EvalState::addConstant(const string & name, Value & v)
|
||||
{
|
||||
Value * v2 = allocValue();
|
||||
*v2 = v;
|
||||
@@ -383,12 +417,18 @@ void EvalState::addConstant(const string & name, Value & v)
|
||||
baseEnv.values[baseEnvDispl++] = v2;
|
||||
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2));
|
||||
return v2;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::addPrimOp(const string & name,
|
||||
Value * EvalState::addPrimOp(const string & name,
|
||||
unsigned int arity, PrimOpFun primOp)
|
||||
{
|
||||
if (arity == 0) {
|
||||
Value v;
|
||||
primOp(*this, noPos, nullptr, v);
|
||||
return addConstant(name, v);
|
||||
}
|
||||
Value * v = allocValue();
|
||||
string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name;
|
||||
Symbol sym = symbols.create(name2);
|
||||
@@ -397,6 +437,7 @@ void EvalState::addPrimOp(const string & name,
|
||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
@@ -546,9 +587,7 @@ Env & EvalState::allocEnv(unsigned int size)
|
||||
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||
env->size = size;
|
||||
|
||||
/* Clear the values because maybeThunk() and lookupVar fromWith expect this. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
env->values[i] = 0;
|
||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||
|
||||
return *env;
|
||||
}
|
||||
@@ -649,8 +688,10 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||
}
|
||||
|
||||
|
||||
void EvalState::evalFile(const Path & path, Value & v)
|
||||
void EvalState::evalFile(const Path & path_, Value & v)
|
||||
{
|
||||
auto path = checkSourcePath(path_);
|
||||
|
||||
FileEvalCache::iterator i;
|
||||
if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) {
|
||||
v = i->second;
|
||||
@@ -1546,7 +1587,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||
dstPath = srcToStore[path];
|
||||
else {
|
||||
dstPath = settings.readOnlyMode
|
||||
? store->computeStorePathForPath(checkSourcePath(path)).first
|
||||
? store->computeStorePathForPath(baseNameOf(path), checkSourcePath(path)).first
|
||||
: store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
|
||||
srcToStore[path] = dstPath;
|
||||
printMsg(lvlChatty, format("copied source '%1%' -> '%2%'")
|
||||
@@ -1668,10 +1709,13 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" time elapsed: %1%") % cpuTime);
|
||||
printMsg(v, format(" size of a value: %1%") % sizeof(Value));
|
||||
printMsg(v, format(" size of an attr: %1%") % sizeof(Attr));
|
||||
printMsg(v, format(" environments allocated: %1% (%2% bytes)") % nrEnvs % bEnvs);
|
||||
printMsg(v, format(" list elements: %1% (%2% bytes)") % nrListElems % bLists);
|
||||
printMsg(v, format(" environments allocated count: %1%") % nrEnvs);
|
||||
printMsg(v, format(" environments allocated bytes: %1%") % bEnvs);
|
||||
printMsg(v, format(" list elements count: %1%") % nrListElems);
|
||||
printMsg(v, format(" list elements bytes: %1%") % bLists);
|
||||
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
|
||||
printMsg(v, format(" values allocated: %1% (%2% bytes)") % nrValues % bValues);
|
||||
printMsg(v, format(" values allocated count: %1%") % nrValues);
|
||||
printMsg(v, format(" values allocated bytes: %1%") % bValues);
|
||||
printMsg(v, format(" sets allocated: %1% (%2% bytes)") % nrAttrsets % bAttrsets);
|
||||
printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates);
|
||||
printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied);
|
||||
@@ -1683,8 +1727,6 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" number of primop calls: %1%") % nrPrimOpCalls);
|
||||
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
|
||||
printMsg(v, format(" total allocations: %1% bytes") % (bEnvs + bLists + bValues + bAttrsets));
|
||||
printMsg(v, format(" memoisation hits: %d") % nrMemoiseHits);
|
||||
printMsg(v, format(" memoisation misses: %d") % nrMemoiseMisses);
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
GC_word heapSize, totalBytes;
|
||||
|
||||
@@ -69,16 +69,17 @@ public:
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||
sFile, sLine, sColumn, sFunctor, sToString,
|
||||
sRight, sWrong, sStructuredAttrs, sBuilder;
|
||||
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
||||
sOutputHash, sOutputHashAlgo, sOutputHashMode;
|
||||
Symbol sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
RepairFlag repair;
|
||||
|
||||
/* If set, don't allow access to files outside of the Nix search
|
||||
path or to environment variables. */
|
||||
bool restricted;
|
||||
/* The allowed filesystem paths in restricted or pure evaluation
|
||||
mode. */
|
||||
std::experimental::optional<PathSet> allowedPaths;
|
||||
|
||||
Value vEmptySet;
|
||||
|
||||
@@ -89,7 +90,7 @@ private:
|
||||
|
||||
/* A cache from path names to values. */
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value>>> FileEvalCache;
|
||||
typedef std::map<Path, Value, std::less<Path>, traceable_allocator<std::pair<const Path, Value> > > FileEvalCache;
|
||||
#else
|
||||
typedef std::map<Path, Value> FileEvalCache;
|
||||
#endif
|
||||
@@ -112,6 +113,15 @@ public:
|
||||
|
||||
void checkURI(const std::string & uri);
|
||||
|
||||
/* When using a diverted store and 'path' is in the Nix store, map
|
||||
'path' to the diverted location (e.g. /nix/store/foo is mapped
|
||||
to /home/alice/my-nix/nix/store/foo). However, this is only
|
||||
done if the context is not empty, since otherwise we're
|
||||
probably trying to read from the actual /nix/store. This is
|
||||
intended to distinguish between import-from-derivation and
|
||||
sources stored in the actual /nix/store. */
|
||||
Path toRealPath(const Path & path, const PathSet & context);
|
||||
|
||||
/* Parse a Nix expression from the specified file. */
|
||||
Expr * parseExprFromFile(const Path & path);
|
||||
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
||||
@@ -201,9 +211,9 @@ private:
|
||||
|
||||
void createBaseEnv();
|
||||
|
||||
void addConstant(const string & name, Value & v);
|
||||
Value * addConstant(const string & name, Value & v);
|
||||
|
||||
void addPrimOp(const string & name,
|
||||
Value * addPrimOp(const string & name,
|
||||
unsigned int arity, PrimOpFun primOp);
|
||||
|
||||
public:
|
||||
@@ -269,8 +279,6 @@ private:
|
||||
unsigned long nrListConcats = 0;
|
||||
unsigned long nrPrimOpCalls = 0;
|
||||
unsigned long nrFunctionCalls = 0;
|
||||
unsigned long nrMemoiseHits = 0;
|
||||
unsigned long nrMemoiseMisses = 0;
|
||||
|
||||
bool countCalls;
|
||||
|
||||
@@ -289,22 +297,6 @@ private:
|
||||
friend struct ExprOpConcatLists;
|
||||
friend struct ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
friend void prim_memoise(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
|
||||
/* State for builtins.memoise. */
|
||||
struct MemoArgComparator
|
||||
{
|
||||
EvalState & state;
|
||||
MemoArgComparator(EvalState & state) : state(state) { }
|
||||
bool operator()(Value * a, Value * b);
|
||||
};
|
||||
|
||||
typedef std::map<Value *, Value, MemoArgComparator, traceable_allocator<std::pair<const Value *, Value>>> PerLambdaMemo;
|
||||
|
||||
typedef std::pair<Env *, ExprLambda *> LambdaKey;
|
||||
|
||||
// FIXME: use std::unordered_map
|
||||
std::map<LambdaKey, PerLambdaMemo, std::less<LambdaKey>, traceable_allocator<std::pair<const LambdaKey, PerLambdaMemo>>> memos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -49,9 +49,10 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
}
|
||||
|
||||
|
||||
static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
|
||||
{
|
||||
string t;
|
||||
t.reserve(length);
|
||||
char c;
|
||||
while ((c = *s++)) {
|
||||
if (c == '\\') {
|
||||
@@ -150,7 +151,7 @@ or { return OR_KW; }
|
||||
/* 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);
|
||||
yylval->e = unescapeStr(data->symbols, yytext, yyleng);
|
||||
return STR;
|
||||
}
|
||||
<STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
|
||||
@@ -178,7 +179,7 @@ or { return OR_KW; }
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\\. {
|
||||
yylval->e = unescapeStr(data->symbols, yytext + 2);
|
||||
yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\$\{ { PUSH_STATE(INSIDE_DOLLAR_CURLY); return DOLLAR_CURLY; }
|
||||
|
||||
@@ -41,7 +41,7 @@ bool DrvName::matches(DrvName & n)
|
||||
}
|
||||
|
||||
|
||||
static string nextComponent(string::const_iterator & p,
|
||||
string nextComponent(string::const_iterator & p,
|
||||
const string::const_iterator end)
|
||||
{
|
||||
/* Skip any dots and dashes (component separators). */
|
||||
|
||||
@@ -24,6 +24,8 @@ private:
|
||||
|
||||
typedef list<DrvName> DrvNames;
|
||||
|
||||
string nextComponent(string::const_iterator & p,
|
||||
const string::const_iterator end);
|
||||
int compareVersions(const string & v1, const string & v2);
|
||||
DrvNames drvNamesFromArgs(const Strings & opArgs);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ std::pair<string, string> decodeContext(const string & s)
|
||||
size_t index = s.find("!", 1);
|
||||
return std::pair<string, string>(string(s, index + 1), string(s, 1, index - 1));
|
||||
} else
|
||||
return std::pair<string, string>(s.at(0) == '/' ? s: string(s, 1), "");
|
||||
return std::pair<string, string>(s.at(0) == '/' ? s : string(s, 1), "");
|
||||
}
|
||||
|
||||
|
||||
@@ -49,24 +49,38 @@ InvalidPathError::InvalidPathError(const Path & path) :
|
||||
void EvalState::realiseContext(const PathSet & context)
|
||||
{
|
||||
PathSet drvs;
|
||||
|
||||
for (auto & i : context) {
|
||||
std::pair<string, string> decoded = decodeContext(i);
|
||||
Path ctx = decoded.first;
|
||||
assert(store->isStorePath(ctx));
|
||||
if (!store->isValidPath(ctx))
|
||||
throw InvalidPathError(ctx);
|
||||
if (!decoded.second.empty() && nix::isDerivation(ctx))
|
||||
if (!decoded.second.empty() && nix::isDerivation(ctx)) {
|
||||
drvs.insert(decoded.first + "!" + decoded.second);
|
||||
|
||||
/* Add the output of this derivation to the allowed
|
||||
paths. */
|
||||
if (allowedPaths) {
|
||||
auto drv = store->derivationFromPath(decoded.first);
|
||||
DerivationOutputs::iterator i = drv.outputs.find(decoded.second);
|
||||
if (i == drv.outputs.end())
|
||||
throw Error("derivation '%s' does not have an output named '%s'", decoded.first, decoded.second);
|
||||
allowedPaths->insert(i->second.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!drvs.empty()) {
|
||||
if (!settings.enableImportFromDerivation)
|
||||
throw EvalError(format("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false") % *(drvs.begin()));
|
||||
/* For performance, prefetch all substitute info. */
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
unsigned long long downloadSize, narSize;
|
||||
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
store->buildPaths(drvs);
|
||||
}
|
||||
|
||||
if (drvs.empty()) return;
|
||||
|
||||
if (!settings.enableImportFromDerivation)
|
||||
throw EvalError(format("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false") % *(drvs.begin()));
|
||||
|
||||
/* For performance, prefetch all substitute info. */
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
unsigned long long downloadSize, narSize;
|
||||
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
store->buildPaths(drvs);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +98,10 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
path = state.checkSourcePath(path);
|
||||
Path realPath = state.checkSourcePath(state.toRealPath(path, context));
|
||||
|
||||
if (state.store->isStorePath(path) && state.store->isValidPath(path) && isDerivation(path)) {
|
||||
Derivation drv = readDerivation(path);
|
||||
Derivation drv = readDerivation(realPath);
|
||||
Value & w = *state.allocValue();
|
||||
state.mkAttrs(w, 3 + drv.outputs.size());
|
||||
Value * v2 = state.allocAttr(w, state.sDrvPath);
|
||||
@@ -114,7 +128,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
} else {
|
||||
state.forceAttrs(*args[0]);
|
||||
if (args[0]->attrs->empty())
|
||||
state.evalFile(path, v);
|
||||
state.evalFile(realPath, v);
|
||||
else {
|
||||
Env * env = &state.allocEnv(args[0]->attrs->size());
|
||||
env->up = &state.baseEnv;
|
||||
@@ -127,8 +141,8 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
env->values[displ++] = attr.value;
|
||||
}
|
||||
|
||||
printTalkative("evaluating file '%1%'", path);
|
||||
Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
|
||||
printTalkative("evaluating file '%1%'", realPath);
|
||||
Expr * e = state.parseExprFromFile(resolveExprPath(realPath), staticEnv);
|
||||
|
||||
e->eval(state, *env, v);
|
||||
}
|
||||
@@ -439,7 +453,7 @@ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Val
|
||||
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
string name = state.forceStringNoCtx(*args[0], pos);
|
||||
mkString(v, state.restricted ? "" : getEnv(name));
|
||||
mkString(v, settings.restrictEval || settings.pureEval ? "" : getEnv(name));
|
||||
}
|
||||
|
||||
|
||||
@@ -539,7 +553,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
for (auto & i : args[0]->attrs->lexicographicOrder()) {
|
||||
if (i->name == state.sIgnoreNulls) continue;
|
||||
string key = i->name;
|
||||
const string & key = i->name;
|
||||
vomit("processing attribute '%1%'", key);
|
||||
|
||||
auto handleHashMode = [&](const std::string & s) {
|
||||
@@ -575,7 +589,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
/* The `args' attribute is special: it supplies the
|
||||
command-line arguments to the builder. */
|
||||
if (key == "args") {
|
||||
if (i->name == state.sArgs) {
|
||||
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);
|
||||
@@ -598,15 +612,13 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
drv.builder = state.forceString(*i->value, context, posDrvName);
|
||||
else if (i->name == state.sSystem)
|
||||
drv.platform = state.forceStringNoCtx(*i->value, posDrvName);
|
||||
else if (i->name == state.sName)
|
||||
drvName = state.forceStringNoCtx(*i->value, posDrvName);
|
||||
else if (key == "outputHash")
|
||||
else if (i->name == state.sOutputHash)
|
||||
outputHash = state.forceStringNoCtx(*i->value, posDrvName);
|
||||
else if (key == "outputHashAlgo")
|
||||
else if (i->name == state.sOutputHashAlgo)
|
||||
outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName);
|
||||
else if (key == "outputHashMode")
|
||||
else if (i->name == state.sOutputHashMode)
|
||||
handleHashMode(state.forceStringNoCtx(*i->value, posDrvName));
|
||||
else if (key == "outputs") {
|
||||
else if (i->name == state.sOutputs) {
|
||||
/* Require ‘outputs’ to be a list of strings. */
|
||||
state.forceList(*i->value, posDrvName);
|
||||
Strings ss;
|
||||
@@ -620,14 +632,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
drv.env.emplace(key, s);
|
||||
if (i->name == state.sBuilder) drv.builder = s;
|
||||
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);
|
||||
}
|
||||
else if (key == "outputHash") outputHash = s;
|
||||
else if (key == "outputHashAlgo") outputHashAlgo = s;
|
||||
else if (key == "outputHashMode") handleHashMode(s);
|
||||
else if (key == "outputs")
|
||||
else if (i->name == state.sOutputHash) outputHash = s;
|
||||
else if (i->name == state.sOutputHashAlgo) outputHashAlgo = s;
|
||||
else if (i->name == state.sOutputHashMode) handleHashMode(s);
|
||||
else if (i->name == state.sOutputs)
|
||||
handleOutputs(tokenizeString<Strings>(s));
|
||||
}
|
||||
|
||||
@@ -863,7 +871,7 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
string s = readFile(state.checkSourcePath(path));
|
||||
string s = readFile(state.checkSourcePath(state.toRealPath(path, context)));
|
||||
if (s.find((char) 0) != string::npos)
|
||||
throw Error(format("the contents of the file '%1%' cannot be represented as a Nix string") % path);
|
||||
mkString(v, s.c_str());
|
||||
@@ -1009,20 +1017,13 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
}
|
||||
|
||||
|
||||
static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
static void addPath(EvalState & state, const Pos & pos, const string & name, const Path & path_,
|
||||
Value * filterFun, bool recursive, const Hash & expectedHash, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
Path path = state.coerceToPath(pos, *args[1], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
|
||||
|
||||
state.forceValue(*args[0]);
|
||||
if (args[0]->type != tLambda)
|
||||
throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
|
||||
|
||||
path = state.checkSourcePath(path);
|
||||
|
||||
PathFilter filter = [&](const Path & path) {
|
||||
const auto path = settings.pureEval && expectedHash ?
|
||||
path_ :
|
||||
state.checkSourcePath(path_);
|
||||
PathFilter filter = filterFun ? ([&](const Path & path) {
|
||||
auto st = lstat(path);
|
||||
|
||||
/* Call the filter function. The first argument is the path,
|
||||
@@ -1031,7 +1032,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||
mkString(arg1, path);
|
||||
|
||||
Value fun2;
|
||||
state.callFunction(*args[0], arg1, fun2, noPos);
|
||||
state.callFunction(*filterFun, arg1, fun2, noPos);
|
||||
|
||||
Value arg2;
|
||||
mkString(arg2,
|
||||
@@ -1044,16 +1045,79 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||
state.callFunction(fun2, arg2, res, noPos);
|
||||
|
||||
return state.forceBool(res, pos);
|
||||
};
|
||||
}) : defaultPathFilter;
|
||||
|
||||
Path dstPath = settings.readOnlyMode
|
||||
? state.store->computeStorePathForPath(path, true, htSHA256, filter).first
|
||||
: state.store->addToStore(baseNameOf(path), path, true, htSHA256, filter, state.repair);
|
||||
Path expectedStorePath;
|
||||
if (expectedHash) {
|
||||
expectedStorePath =
|
||||
state.store->makeFixedOutputPath(recursive, expectedHash, name);
|
||||
}
|
||||
Path dstPath;
|
||||
if (!expectedHash || !state.store->isValidPath(expectedStorePath)) {
|
||||
dstPath = settings.readOnlyMode
|
||||
? state.store->computeStorePathForPath(name, path, recursive, htSHA256, filter).first
|
||||
: state.store->addToStore(name, path, recursive, htSHA256, filter, state.repair);
|
||||
if (expectedHash && expectedStorePath != dstPath) {
|
||||
throw Error(format("store path mismatch in (possibly filtered) path added from '%1%'") % path);
|
||||
}
|
||||
} else
|
||||
dstPath = expectedStorePath;
|
||||
|
||||
mkString(v, dstPath, {dstPath});
|
||||
}
|
||||
|
||||
|
||||
static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
Path path = state.coerceToPath(pos, *args[1], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
|
||||
|
||||
state.forceValue(*args[0]);
|
||||
if (args[0]->type != tLambda)
|
||||
throw TypeError(format("first argument in call to 'filterSource' is not a function but %1%, at %2%") % showType(*args[0]) % pos);
|
||||
|
||||
addPath(state, pos, baseNameOf(path), path, args[0], true, Hash(), v);
|
||||
}
|
||||
|
||||
static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0], pos);
|
||||
Path path;
|
||||
string name;
|
||||
Value * filterFun = nullptr;
|
||||
auto recursive = true;
|
||||
Hash expectedHash;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
const string & n(attr.name);
|
||||
if (n == "path") {
|
||||
PathSet context;
|
||||
path = state.coerceToPath(*attr.pos, *attr.value, context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % *attr.pos);
|
||||
} else if (attr.name == state.sName)
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "filter") {
|
||||
state.forceValue(*attr.value);
|
||||
filterFun = attr.value;
|
||||
} else if (n == "recursive")
|
||||
recursive = state.forceBool(*attr.value, *attr.pos);
|
||||
else if (n == "sha256")
|
||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||
else
|
||||
throw EvalError(format("unsupported argument '%1%' to 'addPath', at %2%") % attr.name % *attr.pos);
|
||||
}
|
||||
if (path.empty())
|
||||
throw EvalError(format("'path' required, at %1%") % pos);
|
||||
if (name.empty())
|
||||
name = baseNameOf(path);
|
||||
|
||||
addPath(state, pos, name, path, filterFun, recursive, expectedHash, v);
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Sets
|
||||
*************************************************************/
|
||||
@@ -1068,8 +1132,11 @@ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, V
|
||||
state.mkList(v, args[0]->attrs->size());
|
||||
|
||||
size_t n = 0;
|
||||
for (auto & i : args[0]->attrs->lexicographicOrder())
|
||||
mkString(*(v.listElems()[n++] = state.allocValue()), i->name);
|
||||
for (auto & i : *args[0]->attrs)
|
||||
mkString(*(v.listElems()[n++] = state.allocValue()), i.name);
|
||||
|
||||
std::sort(v.listElems(), v.listElems() + n,
|
||||
[](Value * v1, Value * v2) { return strcmp(v1->string.s, v2->string.s) < 0; });
|
||||
}
|
||||
|
||||
|
||||
@@ -1891,6 +1958,26 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a
|
||||
}
|
||||
|
||||
|
||||
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
string version = state.forceStringNoCtx(*args[0], pos);
|
||||
auto iter = version.cbegin();
|
||||
Strings components;
|
||||
while (iter != version.cend()) {
|
||||
auto component = nextComponent(iter, version.cend());
|
||||
if (component.empty())
|
||||
break;
|
||||
components.emplace_back(std::move(component));
|
||||
}
|
||||
state.mkList(v, components.size());
|
||||
unsigned int n = 0;
|
||||
for (auto & component : components) {
|
||||
auto listElem = v.listElems()[n++] = state.allocValue();
|
||||
mkString(*listElem, std::move(component));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Networking
|
||||
*************************************************************/
|
||||
@@ -1929,7 +2016,14 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
|
||||
state.checkURI(url);
|
||||
|
||||
if (settings.pureEval && !expectedHash)
|
||||
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
|
||||
|
||||
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
|
||||
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(res);
|
||||
|
||||
mkString(v, res, PathSet({res}));
|
||||
}
|
||||
|
||||
@@ -1981,11 +2075,24 @@ void EvalState::createBaseEnv()
|
||||
mkNull(v);
|
||||
addConstant("null", v);
|
||||
|
||||
mkInt(v, time(0));
|
||||
addConstant("__currentTime", v);
|
||||
auto vThrow = addPrimOp("throw", 1, prim_throw);
|
||||
|
||||
mkString(v, settings.thisSystem);
|
||||
addConstant("__currentSystem", v);
|
||||
auto addPurityError = [&](const std::string & name) {
|
||||
Value * v2 = allocValue();
|
||||
mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name));
|
||||
mkApp(v, *vThrow, *v2);
|
||||
addConstant(name, v);
|
||||
};
|
||||
|
||||
if (!settings.pureEval) {
|
||||
mkInt(v, time(0));
|
||||
addConstant("__currentTime", v);
|
||||
}
|
||||
|
||||
if (!settings.pureEval) {
|
||||
mkString(v, settings.thisSystem);
|
||||
addConstant("__currentSystem", v);
|
||||
}
|
||||
|
||||
mkString(v, nixVersion);
|
||||
addConstant("__nixVersion", v);
|
||||
@@ -2001,10 +2108,10 @@ void EvalState::createBaseEnv()
|
||||
addConstant("__langVersion", v);
|
||||
|
||||
// Miscellaneous
|
||||
addPrimOp("scopedImport", 2, prim_scopedImport);
|
||||
auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport);
|
||||
Value * v2 = allocValue();
|
||||
mkAttrs(*v2, 0);
|
||||
mkApp(v, *baseEnv.values[baseEnvDispl - 1], *v2);
|
||||
mkApp(v, *vScopedImport, *v2);
|
||||
forceValue(v);
|
||||
addConstant("import", v);
|
||||
if (settings.enableNativeCode) {
|
||||
@@ -2020,7 +2127,6 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__isBool", 1, prim_isBool);
|
||||
addPrimOp("__genericClosure", 1, prim_genericClosure);
|
||||
addPrimOp("abort", 1, prim_abort);
|
||||
addPrimOp("throw", 1, prim_throw);
|
||||
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
|
||||
addPrimOp("__tryEval", 1, prim_tryEval);
|
||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||
@@ -2035,7 +2141,10 @@ void EvalState::createBaseEnv()
|
||||
|
||||
// Paths
|
||||
addPrimOp("__toPath", 1, prim_toPath);
|
||||
addPrimOp("__storePath", 1, prim_storePath);
|
||||
if (settings.pureEval)
|
||||
addPurityError("__storePath");
|
||||
else
|
||||
addPrimOp("__storePath", 1, prim_storePath);
|
||||
addPrimOp("__pathExists", 1, prim_pathExists);
|
||||
addPrimOp("baseNameOf", 1, prim_baseNameOf);
|
||||
addPrimOp("dirOf", 1, prim_dirOf);
|
||||
@@ -2049,6 +2158,7 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__fromJSON", 1, prim_fromJSON);
|
||||
addPrimOp("__toFile", 2, prim_toFile);
|
||||
addPrimOp("__filterSource", 2, prim_filterSource);
|
||||
addPrimOp("__path", 1, prim_path);
|
||||
|
||||
// Sets
|
||||
addPrimOp("__attrNames", 1, prim_attrNames);
|
||||
@@ -2103,6 +2213,7 @@ void EvalState::createBaseEnv()
|
||||
// Versions
|
||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||
addPrimOp("__compareVersions", 2, prim_compareVersions);
|
||||
addPrimOp("__splitVersion", 1, prim_splitVersion);
|
||||
|
||||
// Derivations
|
||||
addPrimOp("derivationStrict", 1, prim_derivationStrict);
|
||||
|
||||
@@ -9,6 +9,9 @@ struct RegisterPrimOp
|
||||
{
|
||||
typedef std::vector<std::tuple<std::string, size_t, PrimOpFun>> PrimOps;
|
||||
static PrimOps * primOps;
|
||||
/* You can register a constant by passing an arity of 0. fun
|
||||
will get called during EvalState initialization, so there
|
||||
may be primops not yet added and builtins is not yet sorted. */
|
||||
RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,10 +22,15 @@ struct GitInfo
|
||||
uint64_t revCount = 0;
|
||||
};
|
||||
|
||||
std::regex revRegex("^[0-9a-fA-F]{40}$");
|
||||
|
||||
GitInfo exportGit(ref<Store> store, const std::string & uri,
|
||||
std::experimental::optional<std::string> ref, std::string rev,
|
||||
const std::string & name)
|
||||
{
|
||||
if (settings.pureEval && rev == "")
|
||||
throw Error("in pure evaluation mode, 'fetchGit' requires a Git revision");
|
||||
|
||||
if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) {
|
||||
|
||||
bool clean = true;
|
||||
@@ -76,11 +81,8 @@ GitInfo exportGit(ref<Store> store, const std::string & uri,
|
||||
|
||||
if (!ref) ref = "master"s;
|
||||
|
||||
if (rev != "") {
|
||||
std::regex revRegex("^[0-9a-fA-F]{40}$");
|
||||
if (!std::regex_match(rev, revRegex))
|
||||
throw Error("invalid Git revision '%s'", rev);
|
||||
}
|
||||
if (rev != "" && !std::regex_match(rev, revRegex))
|
||||
throw Error("invalid Git revision '%s'", rev);
|
||||
|
||||
Path cacheDir = getCacheDir() + "/nix/git";
|
||||
|
||||
@@ -231,6 +233,9 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
|
||||
v.attrs->sort();
|
||||
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(gitInfo.storePath);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
|
||||
|
||||
@@ -27,6 +27,9 @@ std::regex commitHashRegex("^[0-9a-fA-F]{40}$");
|
||||
HgInfo exportMercurial(ref<Store> store, const std::string & uri,
|
||||
std::string rev, const std::string & name)
|
||||
{
|
||||
if (settings.pureEval && rev == "")
|
||||
throw Error("in pure evaluation mode, 'fetchMercurial' requires a Mercurial revision");
|
||||
|
||||
if (rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.hg")) {
|
||||
|
||||
bool clean = runProgram("hg", true, { "status", "-R", uri, "--modified", "--added", "--removed" }) == "";
|
||||
@@ -196,6 +199,9 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(hgInfo.rev, 0, 12));
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), hgInfo.revCount);
|
||||
v.attrs->sort();
|
||||
|
||||
if (state.allowedPaths)
|
||||
state.allowedPaths->insert(hgInfo.storePath);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace nix {
|
||||
|
||||
bool EvalState::MemoArgComparator::operator()(Value * v1, Value * v2)
|
||||
{
|
||||
if (v1 == v2) return false;
|
||||
|
||||
state.forceValue(*v1);
|
||||
state.forceValue(*v2);
|
||||
|
||||
if (v1->type == v2->type) {
|
||||
switch (v1->type) {
|
||||
|
||||
case tInt:
|
||||
return v1->integer < v2->integer;
|
||||
|
||||
case tBool:
|
||||
return v1->boolean < v2->boolean;
|
||||
|
||||
case tString:
|
||||
return strcmp(v1->string.s, v2->string.s);
|
||||
|
||||
case tPath:
|
||||
return strcmp(v1->path, v2->path);
|
||||
|
||||
case tNull:
|
||||
return false;
|
||||
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
unsigned int n;
|
||||
for (n = 0; n < v1->listSize() && n < v2->listSize(); ++n) {
|
||||
if ((*this)(v1->listElems()[n], v2->listElems()[n])) return true;
|
||||
if ((*this)(v2->listElems()[n], v1->listElems()[n])) return false;
|
||||
}
|
||||
|
||||
return n == v1->listSize() && n < v2->listSize();
|
||||
|
||||
case tAttrs:
|
||||
Bindings::iterator i, j;
|
||||
for (i = v1->attrs->begin(), j = v2->attrs->begin(); i != v1->attrs->end() && j != v2->attrs->end(); ++i, ++j) {
|
||||
if (i->name < j->name) return true;
|
||||
if (j->name < i->name) return false;
|
||||
if ((*this)(i->value, j->value)) return true;
|
||||
if ((*this)(j->value, i->value)) return false;
|
||||
}
|
||||
return i == v1->attrs->end() && j != v2->attrs->end();
|
||||
|
||||
case tLambda:
|
||||
return std::make_pair(v1->lambda.env, v1->lambda.fun) < std::make_pair(v2->lambda.env, v2->lambda.fun);
|
||||
|
||||
case tFloat:
|
||||
return v1->fpoint < v2->fpoint;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// As a fallback, use pointer equality.
|
||||
return v1 < v2;
|
||||
}
|
||||
|
||||
void prim_memoise(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceFunction(*args[0], pos);
|
||||
|
||||
EvalState::PerLambdaMemo foo(state);
|
||||
|
||||
auto & memo = state.memos.emplace(std::make_pair(args[0]->lambda.env, args[0]->lambda.fun), state).first->second;
|
||||
|
||||
auto result = memo.find(args[1]);
|
||||
|
||||
if (result != memo.end()) {
|
||||
state.nrMemoiseHits++;
|
||||
v = result->second;
|
||||
return;
|
||||
}
|
||||
|
||||
state.nrMemoiseMisses++;
|
||||
|
||||
state.callFunction(*args[0], *args[1], v, pos);
|
||||
|
||||
memo[args[1]] = v;
|
||||
|
||||
}
|
||||
|
||||
static RegisterPrimOp r("memoise", 2, prim_memoise);
|
||||
|
||||
}
|
||||
@@ -37,6 +37,10 @@ MixCommonArgs::MixCommonArgs(const string & programName)
|
||||
|
||||
std::string cat = "config";
|
||||
settings.convertToArgs(*this, cat);
|
||||
|
||||
// Backward compatibility hack: nix-env already had a --system flag.
|
||||
if (programName == "nix-env") longFlags.erase("system");
|
||||
|
||||
hiddenCategories.insert(cat);
|
||||
}
|
||||
|
||||
|
||||
@@ -193,9 +193,6 @@ LegacyArgs::LegacyArgs(const std::string & programName,
|
||||
mkFlag(0, "readonly-mode", "do not write to the Nix store",
|
||||
&settings.readOnlyMode);
|
||||
|
||||
mkFlag(0, "show-trace", "show Nix expression stack trace in evaluation errors",
|
||||
&settings.showTrace);
|
||||
|
||||
mkFlag(0, "no-gc-warning", "disable warning about not using '--add-root'",
|
||||
&gcWarning, false);
|
||||
|
||||
@@ -265,6 +262,7 @@ void printVersion(const string & programName)
|
||||
void showManPage(const string & name)
|
||||
{
|
||||
restoreSignals();
|
||||
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
||||
execlp("man", "man", name.c_str(), NULL);
|
||||
throw SysError(format("command 'man %1%' failed") % name.c_str());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
|
||||
int handleExceptions(const string & programName, std::function<void()> fun);
|
||||
|
||||
/* Don't forget to call initPlugins() after settings are initialized! */
|
||||
void initNix();
|
||||
|
||||
void parseCmdLine(int argc, char * * argv,
|
||||
|
||||
@@ -149,7 +149,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
/* Compress the NAR. */
|
||||
narInfo->compression = compression;
|
||||
auto now1 = std::chrono::steady_clock::now();
|
||||
auto narCompressed = compress(compression, *nar);
|
||||
auto narCompressed = compress(compression, *nar, parallelCompression);
|
||||
auto now2 = std::chrono::steady_clock::now();
|
||||
narInfo->fileHash = hashString(htSHA256, *narCompressed);
|
||||
narInfo->fileSize = narCompressed->size();
|
||||
|
||||
@@ -19,6 +19,8 @@ public:
|
||||
const Setting<bool> writeNARListing{this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"};
|
||||
const Setting<Path> secretKeyFile{this, "", "secret-key", "path to secret key used to sign the binary cache"};
|
||||
const Setting<Path> localNarCache{this, "", "local-nar-cache", "path to a local cache of NARs"};
|
||||
const Setting<bool> parallelCompression{this, false, "parallel-compression",
|
||||
"enable multi-threading compression, available for xz only currently"};
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "archive.hh"
|
||||
#include "affinity.hh"
|
||||
#include "builtins.hh"
|
||||
#include "download.hh"
|
||||
#include "finally.hh"
|
||||
#include "compression.hh"
|
||||
#include "json.hh"
|
||||
@@ -48,7 +49,9 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/syscall.h>
|
||||
#if HAVE_SECCOMP
|
||||
#include <seccomp.h>
|
||||
#endif
|
||||
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
|
||||
#endif
|
||||
|
||||
@@ -1123,11 +1126,6 @@ void DerivationGoal::haveDerivation()
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reject doing a hash build of anything other than a fixed-output
|
||||
derivation. */
|
||||
if (buildMode == bmHash && !drv->isFixedOutput())
|
||||
throw Error("cannot do a hash build of non-fixed-output derivation '%1%'", drvPath);
|
||||
|
||||
/* We are first going to try to create the invalid output paths
|
||||
through substitutes. If that doesn't work, we'll build
|
||||
them. */
|
||||
@@ -1319,9 +1317,7 @@ void DerivationGoal::inputsRealised()
|
||||
allPaths.insert(inputPaths.begin(), inputPaths.end());
|
||||
|
||||
/* Is this a fixed-output derivation? */
|
||||
fixedOutput = true;
|
||||
for (auto & i : drv->outputs)
|
||||
if (i.second.hash == "") fixedOutput = false;
|
||||
fixedOutput = drv->isFixedOutput();
|
||||
|
||||
/* Don't repeat fixed-output derivations since they're already
|
||||
verified by their output hash.*/
|
||||
@@ -1341,19 +1337,6 @@ void DerivationGoal::tryToBuild()
|
||||
{
|
||||
trace("trying to build");
|
||||
|
||||
/* Check for the possibility that some other goal in this process
|
||||
has locked the output since we checked in haveDerivation().
|
||||
(It can't happen between here and the lockPaths() call below
|
||||
because we're not allowing multi-threading.) If so, put this
|
||||
goal to sleep until another goal finishes, then try again. */
|
||||
for (auto & i : drv->outputs)
|
||||
if (pathIsLockedByMe(worker.store.toRealPath(i.second.path))) {
|
||||
debug(format("putting derivation '%1%' to sleep because '%2%' is locked by another goal")
|
||||
% drvPath % i.second.path);
|
||||
worker.waitForAnyGoal(shared_from_this());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Obtain locks on all output paths. The locks are automatically
|
||||
released when we exit this function or Nix crashes. If we
|
||||
can't acquire the lock, then continue; hopefully some other
|
||||
@@ -1777,6 +1760,19 @@ PathSet exportReferences(Store & store, PathSet storePaths)
|
||||
return paths;
|
||||
}
|
||||
|
||||
static std::once_flag dns_resolve_flag;
|
||||
|
||||
static void preloadNSS() {
|
||||
/* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of
|
||||
one of the glibc NSS libraries in a sandboxed child, which will fail unless the library's already
|
||||
been loaded in the parent. So we force a download of an invalid URL to force the NSS machinery to
|
||||
load its lookup libraries in the parent before any child gets a chance to. */
|
||||
std::call_once(dns_resolve_flag, []() {
|
||||
DownloadRequest request("http://this.pre-initializes.the.dns.resolvers.invalid");
|
||||
request.tries = 1; // We only need to do it once, and this also suppresses an annoying warning
|
||||
try { getDownloader()->download(request); } catch (...) {}
|
||||
});
|
||||
}
|
||||
|
||||
void DerivationGoal::startBuilder()
|
||||
{
|
||||
@@ -1787,6 +1783,9 @@ void DerivationGoal::startBuilder()
|
||||
% drv->platform % settings.thisSystem % drvPath);
|
||||
}
|
||||
|
||||
if (drv->isBuiltin())
|
||||
preloadNSS();
|
||||
|
||||
#if __APPLE__
|
||||
additionalSandboxProfile = get(drv->env, "__sandboxProfile");
|
||||
#endif
|
||||
@@ -1810,8 +1809,13 @@ void DerivationGoal::startBuilder()
|
||||
useChroot = !fixedOutput && get(drv->env, "__noChroot") != "1";
|
||||
}
|
||||
|
||||
if (worker.store.storeDir != worker.store.realStoreDir)
|
||||
useChroot = true;
|
||||
if (worker.store.storeDir != worker.store.realStoreDir) {
|
||||
#if __linux__
|
||||
useChroot = true;
|
||||
#else
|
||||
throw Error("building using a diverted store is not supported on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If `build-users-group' is not empty, then we have to build as
|
||||
one of the members of that group. */
|
||||
@@ -2469,7 +2473,7 @@ void setupSeccomp()
|
||||
{
|
||||
#if __linux__
|
||||
if (!settings.filterSyscalls) return;
|
||||
|
||||
#if HAVE_SECCOMP
|
||||
scmp_filter_ctx ctx;
|
||||
|
||||
if (!(ctx = seccomp_init(SCMP_ACT_ALLOW)))
|
||||
@@ -2515,6 +2519,11 @@ void setupSeccomp()
|
||||
|
||||
if (seccomp_load(ctx) != 0)
|
||||
throw SysError("unable to load seccomp BPF program");
|
||||
#else
|
||||
throw Error(
|
||||
"seccomp is not supported on this platform; "
|
||||
"you can bypass this error by setting the option 'filter-syscalls' to false, but note that untrusted builds can then create setuid binaries!");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2928,8 +2937,13 @@ void DerivationGoal::runChild()
|
||||
if (drv->isBuiltin()) {
|
||||
try {
|
||||
logger = makeJSONLogger(*logger);
|
||||
|
||||
BasicDerivation drv2(*drv);
|
||||
for (auto & e : drv2.env)
|
||||
e.second = rewriteStrings(e.second, inputRewrites);
|
||||
|
||||
if (drv->builder == "builtin:fetchurl")
|
||||
builtinFetchurl(*drv, netrcData);
|
||||
builtinFetchurl(drv2, netrcData);
|
||||
else
|
||||
throw Error(format("unsupported builtin function '%1%'") % string(drv->builder, 8));
|
||||
_exit(0);
|
||||
@@ -2992,6 +3006,8 @@ void DerivationGoal::registerOutputs()
|
||||
bool runDiffHook = settings.runDiffHook;
|
||||
bool keepPreviousRound = settings.keepFailed || runDiffHook;
|
||||
|
||||
std::exception_ptr delayedException;
|
||||
|
||||
/* Check whether the output paths were created, and grep each
|
||||
output path to determine what other paths it references. Also make all
|
||||
output paths read-only. */
|
||||
@@ -3066,7 +3082,7 @@ void DerivationGoal::registerOutputs()
|
||||
/* Check that fixed-output derivations produced the right
|
||||
outputs (i.e., the content hash should match the specified
|
||||
hash). */
|
||||
if (i.second.hash != "") {
|
||||
if (fixedOutput) {
|
||||
|
||||
bool recursive; Hash h;
|
||||
i.second.parseHashInfo(recursive, h);
|
||||
@@ -3082,27 +3098,34 @@ void DerivationGoal::registerOutputs()
|
||||
/* Check the hash. In hash mode, move the path produced by
|
||||
the derivation to its content-addressed location. */
|
||||
Hash h2 = recursive ? hashPath(h.type, actualPath).first : hashFile(h.type, actualPath);
|
||||
if (buildMode == bmHash) {
|
||||
Path dest = worker.store.makeFixedOutputPath(recursive, h2, drv->env["name"]);
|
||||
printError(format("build produced path '%1%' with %2% hash '%3%'")
|
||||
% dest % printHashType(h.type) % printHash16or32(h2));
|
||||
if (worker.store.isValidPath(dest))
|
||||
return;
|
||||
|
||||
Path dest = worker.store.makeFixedOutputPath(recursive, h2, drv->env["name"]);
|
||||
|
||||
if (h != h2) {
|
||||
|
||||
/* Throw an error after registering the path as
|
||||
valid. */
|
||||
delayedException = std::make_exception_ptr(
|
||||
BuildError("fixed-output derivation produced path '%s' with %s hash '%s' instead of the expected hash '%s'",
|
||||
dest, printHashType(h.type), printHash16or32(h2), printHash16or32(h)));
|
||||
|
||||
Path actualDest = worker.store.toRealPath(dest);
|
||||
|
||||
if (worker.store.isValidPath(dest))
|
||||
std::rethrow_exception(delayedException);
|
||||
|
||||
if (actualPath != actualDest) {
|
||||
PathLocks outputLocks({actualDest});
|
||||
deletePath(actualDest);
|
||||
if (rename(actualPath.c_str(), actualDest.c_str()) == -1)
|
||||
throw SysError(format("moving '%1%' to '%2%'") % actualPath % dest);
|
||||
}
|
||||
|
||||
path = dest;
|
||||
actualPath = actualDest;
|
||||
} else {
|
||||
if (h != h2)
|
||||
throw BuildError(
|
||||
format("output path '%1%' has %2% hash '%3%' when '%4%' was expected")
|
||||
% path % i.second.hashAlgo % printHash16or32(h2) % printHash16or32(h));
|
||||
}
|
||||
else
|
||||
assert(path == dest);
|
||||
|
||||
info.ca = makeFixedOutputCA(recursive, h2);
|
||||
}
|
||||
@@ -3279,6 +3302,11 @@ void DerivationGoal::registerOutputs()
|
||||
paths referenced by each of them. If there are cycles in the
|
||||
outputs, this will fail. */
|
||||
worker.store.registerValidPaths(infos);
|
||||
|
||||
/* In case of a fixed-output derivation hash mismatch, throw an
|
||||
exception now that we have registered the output as valid. */
|
||||
if (delayedException)
|
||||
std::rethrow_exception(delayedException);
|
||||
}
|
||||
|
||||
|
||||
@@ -3394,7 +3422,7 @@ void DerivationGoal::flushLine()
|
||||
else {
|
||||
if (settings.verboseBuild &&
|
||||
(settings.printRepeatedBuilds || curRound == 1))
|
||||
printError(filterANSIEscapes(currentLogLine, true));
|
||||
printError(currentLogLine);
|
||||
else {
|
||||
logTail.push_back(currentLogLine);
|
||||
if (logTail.size() > settings.logLines) logTail.pop_front();
|
||||
@@ -3636,7 +3664,7 @@ void SubstitutionGoal::tryNext()
|
||||
/* Update the total expected download size. */
|
||||
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(info);
|
||||
|
||||
maintainExpectedNar = std::make_unique<MaintainCount<uint64_t>>(worker.expectedNarSize, narInfo->narSize);
|
||||
maintainExpectedNar = std::make_unique<MaintainCount<uint64_t>>(worker.expectedNarSize, info->narSize);
|
||||
|
||||
maintainExpectedDownload =
|
||||
narInfo && narInfo->fileSize
|
||||
@@ -3650,9 +3678,12 @@ void SubstitutionGoal::tryNext()
|
||||
/* Bail out early if this substituter lacks a valid
|
||||
signature. LocalStore::addToStore() also checks for this, but
|
||||
only after we've downloaded the path. */
|
||||
if (worker.store.requireSigs && !info->checkSignatures(worker.store, worker.store.publicKeys)) {
|
||||
printInfo(format("warning: substituter '%s' does not have a valid signature for path '%s'")
|
||||
% sub->getUri() % storePath);
|
||||
if (worker.store.requireSigs
|
||||
&& !sub->isTrusted
|
||||
&& !info->checkSignatures(worker.store, worker.store.publicKeys))
|
||||
{
|
||||
printError("warning: substituter '%s' does not have a valid signature for path '%s'",
|
||||
sub->getUri(), storePath);
|
||||
tryNext();
|
||||
return;
|
||||
}
|
||||
@@ -3702,6 +3733,17 @@ void SubstitutionGoal::tryToRun()
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the store path is already locked (probably by a
|
||||
DerivationGoal), then put this goal to sleep. Note: we don't
|
||||
acquire a lock here since that breaks addToStore(), so below we
|
||||
handle an AlreadyLocked exception from addToStore(). The check
|
||||
here is just an optimisation to prevent having to redo a
|
||||
download due to a locked path. */
|
||||
if (pathIsLockedByMe(worker.store.toRealPath(storePath))) {
|
||||
worker.waitForAWhile(shared_from_this());
|
||||
return;
|
||||
}
|
||||
|
||||
maintainRunningSubstitutions = std::make_unique<MaintainCount<uint64_t>>(worker.runningSubstitutions);
|
||||
worker.updateProgress();
|
||||
|
||||
@@ -3718,7 +3760,7 @@ void SubstitutionGoal::tryToRun()
|
||||
PushActivity pact(act.id);
|
||||
|
||||
copyStorePath(ref<Store>(sub), ref<Store>(worker.store.shared_from_this()),
|
||||
storePath, repair);
|
||||
storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs);
|
||||
|
||||
promise.set_value();
|
||||
} catch (...) {
|
||||
@@ -3741,8 +3783,14 @@ void SubstitutionGoal::finished()
|
||||
|
||||
try {
|
||||
promise.get_future().get();
|
||||
} catch (AlreadyLocked & e) {
|
||||
/* Probably a DerivationGoal is already building this store
|
||||
path. Sleep for a while and try again. */
|
||||
state = &SubstitutionGoal::init;
|
||||
worker.waitForAWhile(shared_from_this());
|
||||
return;
|
||||
} catch (Error & e) {
|
||||
printInfo(e.msg());
|
||||
printError(e.msg());
|
||||
|
||||
/* Try the next substitute. */
|
||||
state = &SubstitutionGoal::tryNext;
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <queue>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
@@ -91,6 +93,8 @@ struct CurlDownloader : public Downloader
|
||||
{
|
||||
if (!request.expectedETag.empty())
|
||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
||||
if (!request.mimeType.empty())
|
||||
requestHeaders = curl_slist_append(requestHeaders, ("Content-Type: " + request.mimeType).c_str());
|
||||
}
|
||||
|
||||
~DownloadItem()
|
||||
@@ -185,6 +189,22 @@ struct CurlDownloader : public Downloader
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t readOffset = 0;
|
||||
int readCallback(char *buffer, size_t size, size_t nitems)
|
||||
{
|
||||
if (readOffset == request.data->length())
|
||||
return 0;
|
||||
auto count = std::min(size * nitems, request.data->length() - readOffset);
|
||||
memcpy(buffer, request.data->data() + readOffset, count);
|
||||
readOffset += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static int readCallbackWrapper(char *buffer, size_t size, size_t nitems, void * userp)
|
||||
{
|
||||
return ((DownloadItem *) userp)->readCallback(buffer, size, nitems);
|
||||
}
|
||||
|
||||
long lowSpeedTimeout = 300;
|
||||
|
||||
void init()
|
||||
@@ -225,6 +245,13 @@ struct CurlDownloader : public Downloader
|
||||
if (request.head)
|
||||
curl_easy_setopt(req, CURLOPT_NOBODY, 1);
|
||||
|
||||
if (request.data) {
|
||||
curl_easy_setopt(req, CURLOPT_UPLOAD, 1L);
|
||||
curl_easy_setopt(req, CURLOPT_READFUNCTION, readCallbackWrapper);
|
||||
curl_easy_setopt(req, CURLOPT_READDATA, this);
|
||||
curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->length());
|
||||
}
|
||||
|
||||
if (request.verifyTLS) {
|
||||
if (settings.caFile != "")
|
||||
curl_easy_setopt(req, CURLOPT_CAINFO, settings.caFile.c_str());
|
||||
@@ -265,7 +292,7 @@ struct CurlDownloader : public Downloader
|
||||
}
|
||||
|
||||
if (code == CURLE_OK &&
|
||||
(httpStatus == 200 || httpStatus == 304 || httpStatus == 226 /* FTP */ || httpStatus == 0 /* other protocol */))
|
||||
(httpStatus == 200 || httpStatus == 201 || httpStatus == 204 || httpStatus == 304 || httpStatus == 226 /* FTP */ || httpStatus == 0 /* other protocol */))
|
||||
{
|
||||
result.cached = httpStatus == 304;
|
||||
done = true;
|
||||
@@ -303,6 +330,7 @@ struct CurlDownloader : public Downloader
|
||||
// Don't bother retrying on certain cURL errors either
|
||||
switch (code) {
|
||||
case CURLE_FAILED_INIT:
|
||||
case CURLE_URL_MALFORMAT:
|
||||
case CURLE_NOT_BUILT_IN:
|
||||
case CURLE_REMOTE_ACCESS_DENIED:
|
||||
case CURLE_FILE_COULDNT_READ_FILE:
|
||||
@@ -311,10 +339,10 @@ struct CurlDownloader : public Downloader
|
||||
case CURLE_BAD_FUNCTION_ARGUMENT:
|
||||
case CURLE_INTERFACE_FAILED:
|
||||
case CURLE_UNKNOWN_OPTION:
|
||||
err = Misc;
|
||||
break;
|
||||
err = Misc;
|
||||
break;
|
||||
default: // Shut up warnings
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ struct DownloadRequest
|
||||
unsigned int baseRetryTimeMs = 250;
|
||||
ActivityId parentAct;
|
||||
bool decompress = true;
|
||||
std::shared_ptr<std::string> data;
|
||||
std::string mimeType;
|
||||
|
||||
DownloadRequest(const std::string & uri)
|
||||
: uri(uri), parentAct(curActivity) { }
|
||||
|
||||
@@ -324,10 +324,8 @@ Roots LocalStore::findRootsNoTemp()
|
||||
{
|
||||
Roots roots;
|
||||
|
||||
/* Process direct roots in {gcroots,manifests,profiles}. */
|
||||
/* Process direct roots in {gcroots,profiles}. */
|
||||
findRoots(stateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
|
||||
if (pathExists(stateDir + "/manifests"))
|
||||
findRoots(stateDir + "/manifests", DT_UNKNOWN, roots);
|
||||
findRoots(stateDir + "/profiles", DT_UNKNOWN, roots);
|
||||
|
||||
/* Add additional roots returned by the program specified by the
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <dlfcn.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -37,6 +38,7 @@ Settings::Settings()
|
||||
, nixConfDir(canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR)))
|
||||
, nixLibexecDir(canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)))
|
||||
, nixBinDir(canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)))
|
||||
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||
, nixDaemonSocketFile(canonPath(nixStateDir + DEFAULT_SOCKET_PATH))
|
||||
{
|
||||
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
|
||||
@@ -137,4 +139,46 @@ void MaxBuildJobsSetting::set(const std::string & str)
|
||||
throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
|
||||
}
|
||||
|
||||
|
||||
void initPlugins()
|
||||
{
|
||||
for (const auto & pluginFile : settings.pluginFiles.get()) {
|
||||
Paths pluginFiles;
|
||||
try {
|
||||
auto ents = readDirectory(pluginFile);
|
||||
for (const auto & ent : ents)
|
||||
pluginFiles.emplace_back(pluginFile + "/" + ent.name);
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo != ENOTDIR)
|
||||
throw;
|
||||
pluginFiles.emplace_back(pluginFile);
|
||||
}
|
||||
for (const auto & file : pluginFiles) {
|
||||
/* handle is purposefully leaked as there may be state in the
|
||||
DSO needed by the action of the plugin. */
|
||||
void *handle =
|
||||
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
throw Error("could not dynamically open plugin file '%s%': %s%", file, dlerror());
|
||||
}
|
||||
}
|
||||
/* We handle settings registrations here, since plugins can add settings */
|
||||
if (RegisterSetting::settingRegistrations) {
|
||||
for (auto & registration : *RegisterSetting::settingRegistrations)
|
||||
settings.addSetting(registration);
|
||||
delete RegisterSetting::settingRegistrations;
|
||||
}
|
||||
settings.handleUnknownSettings();
|
||||
}
|
||||
|
||||
RegisterSetting::SettingRegistrations * RegisterSetting::settingRegistrations;
|
||||
|
||||
RegisterSetting::RegisterSetting(AbstractSetting * s)
|
||||
{
|
||||
if (!settingRegistrations)
|
||||
settingRegistrations = new SettingRegistrations;
|
||||
settingRegistrations->emplace_back(s);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ public:
|
||||
/* The directory where the main programs are stored. */
|
||||
Path nixBinDir;
|
||||
|
||||
/* The directory where the man pages are stored. */
|
||||
Path nixManDir;
|
||||
|
||||
/* File name of the socket the daemon listens to. */
|
||||
Path nixDaemonSocketFile;
|
||||
|
||||
@@ -211,7 +214,8 @@ public:
|
||||
bool lockCPU;
|
||||
|
||||
/* Whether to show a stack trace if Nix evaluation fails. */
|
||||
bool showTrace = false;
|
||||
Setting<bool> showTrace{this, false, "show-trace",
|
||||
"Whether to show a stack trace on evaluation errors."};
|
||||
|
||||
Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation",
|
||||
"Whether builtin functions that allow executing native code should be enabled."};
|
||||
@@ -232,6 +236,9 @@ public:
|
||||
"Whether to restrict file system access to paths in $NIX_PATH, "
|
||||
"and network access to the URI prefixes listed in 'allowed-uris'."};
|
||||
|
||||
Setting<bool> pureEval{this, false, "pure-eval",
|
||||
"Whether to restrict file system and network access to files specified by cryptographic hash."};
|
||||
|
||||
Setting<size_t> buildRepeat{this, 0, "repeat",
|
||||
"The number of times to repeat a build in order to verify determinism.",
|
||||
{"build-repeat"}};
|
||||
@@ -283,10 +290,7 @@ public:
|
||||
Setting<unsigned int> tarballTtl{this, 60 * 60, "tarball-ttl",
|
||||
"How soon to expire files fetched by builtins.fetchTarball and builtins.fetchurl."};
|
||||
|
||||
Setting<std::string> signedBinaryCaches{this, "*", "signed-binary-caches",
|
||||
"Obsolete."};
|
||||
|
||||
Setting<bool> requireSigs{this, signedBinaryCaches == "*", "require-sigs",
|
||||
Setting<bool> requireSigs{this, true, "require-sigs",
|
||||
"Whether to check that any non-content-addressed path added to the "
|
||||
"Nix store has a valid signature (that is, one signed using a key "
|
||||
"listed in 'trusted-public-keys'."};
|
||||
@@ -366,14 +370,28 @@ public:
|
||||
|
||||
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
||||
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
|
||||
|
||||
Setting<Paths> pluginFiles{this, {}, "plugin-files",
|
||||
"Plugins to dynamically load at nix initialization time."};
|
||||
};
|
||||
|
||||
|
||||
// FIXME: don't use a global variable.
|
||||
extern Settings settings;
|
||||
|
||||
/* This should be called after settings are initialized, but before
|
||||
anything else */
|
||||
void initPlugins();
|
||||
|
||||
|
||||
extern const string nixVersion;
|
||||
|
||||
struct RegisterSetting
|
||||
{
|
||||
typedef std::vector<AbstractSetting *> SettingRegistrations;
|
||||
static SettingRegistrations * settingRegistrations;
|
||||
RegisterSetting(AbstractSetting * s);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
try {
|
||||
BinaryCacheStore::init();
|
||||
} catch (UploadToHTTP &) {
|
||||
throw Error(format("'%s' does not appear to be a binary cache") % cacheUri);
|
||||
throw Error("'%s' does not appear to be a binary cache", cacheUri);
|
||||
}
|
||||
diskCache->createCache(cacheUri, storeDir, wantMassQuery_, priority);
|
||||
}
|
||||
@@ -67,7 +67,14 @@ protected:
|
||||
const std::string & data,
|
||||
const std::string & mimeType) override
|
||||
{
|
||||
throw UploadToHTTP("uploading to an HTTP binary cache is not supported");
|
||||
auto req = DownloadRequest(cacheUri + "/" + path);
|
||||
req.data = std::make_shared<string>(data); // FIXME: inefficient
|
||||
req.mimeType = mimeType;
|
||||
try {
|
||||
getDownloader()->download(req);
|
||||
} catch (DownloadError & e) {
|
||||
throw UploadToHTTP(format("uploading to HTTP binary cache at %1% not supported: %2%") % cacheUri % e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
void getFile(const std::string & path,
|
||||
|
||||
@@ -992,8 +992,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> &
|
||||
/* Lock the output path. But don't lock if we're being called
|
||||
from a build hook (whose parent process already acquired a
|
||||
lock on this path). */
|
||||
Strings locksHeld = tokenizeString<Strings>(getEnv("NIX_HELD_LOCKS"));
|
||||
if (find(locksHeld.begin(), locksHeld.end(), info.path) == locksHeld.end())
|
||||
if (!locksHeld.count(info.path))
|
||||
outputLock.lockPaths({realPath});
|
||||
|
||||
if (repair || !isValidPath(info.path)) {
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace nix {
|
||||
/* Nix store and database schema version. Version 1 (or 0) was Nix <=
|
||||
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
|
||||
Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is
|
||||
Nix 1.0. Version 7 is Nix 1.3. Version 10 is 1.12. */
|
||||
Nix 1.0. Version 7 is Nix 1.3. Version 10 is 2.0. */
|
||||
const int nixSchemaVersion = 10;
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
// Hack for build-remote.cc.
|
||||
PathSet locksHeld = tokenizeString<PathSet>(getEnv("NIX_HELD_LOCKS"));
|
||||
|
||||
/* Initialise the local store, upgrading the schema if
|
||||
necessary. */
|
||||
LocalStore(const Params & params);
|
||||
|
||||
@@ -9,6 +9,9 @@ libstore_SOURCES := $(wildcard $(d)/*.cc)
|
||||
libstore_LIBS = libutil libformat
|
||||
|
||||
libstore_LDFLAGS = $(SQLITE3_LIBS) -lbz2 $(LIBCURL_LIBS) $(SODIUM_LIBS) -pthread
|
||||
ifneq ($(OS), FreeBSD)
|
||||
libstore_LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
libstore_FILES = sandbox-defaults.sb sandbox-minimal.sb sandbox-network.sb
|
||||
|
||||
@@ -22,7 +25,7 @@ ifeq ($(OS), SunOS)
|
||||
libstore_LDFLAGS += -lsocket
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Linux)
|
||||
ifeq ($(HAVE_SECCOMP), 1)
|
||||
libstore_LDFLAGS += -lseccomp
|
||||
endif
|
||||
|
||||
@@ -35,6 +38,7 @@ libstore_CXXFLAGS = \
|
||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||
-DNIX_MAN_DIR=\"$(mandir)\" \
|
||||
-DSANDBOX_SHELL="\"$(sandbox_shell)\"" \
|
||||
-DLSOF=\"$(lsof)\"
|
||||
|
||||
|
||||
@@ -113,8 +113,10 @@ bool PathLocks::lockPaths(const PathSet & _paths,
|
||||
|
||||
{
|
||||
auto lockedPaths(lockedPaths_.lock());
|
||||
if (lockedPaths->count(lockPath))
|
||||
throw Error("deadlock: trying to re-acquire self-held lock '%s'", lockPath);
|
||||
if (lockedPaths->count(lockPath)) {
|
||||
if (!wait) return false;
|
||||
throw AlreadyLocked("deadlock: trying to re-acquire self-held lock '%s'", lockPath);
|
||||
}
|
||||
lockedPaths->insert(lockPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Open (possibly create) a lock file and return the file descriptor.
|
||||
-1 is returned if create is false and the lock could not be opened
|
||||
because it doesn't exist. Any other error throws an exception. */
|
||||
@@ -18,6 +16,7 @@ enum LockType { ltRead, ltWrite, ltNone };
|
||||
|
||||
bool lockFile(int fd, LockType lockType, bool wait);
|
||||
|
||||
MakeError(AlreadyLocked, Error);
|
||||
|
||||
class PathLocks
|
||||
{
|
||||
@@ -38,9 +37,6 @@ public:
|
||||
void setDeletion(bool deletePaths);
|
||||
};
|
||||
|
||||
|
||||
// FIXME: not thread-safe!
|
||||
bool pathIsLockedByMe(const Path & path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -78,9 +78,22 @@ UDSRemoteStore::UDSRemoteStore(const Params & params)
|
||||
}
|
||||
|
||||
|
||||
UDSRemoteStore::UDSRemoteStore(std::string socket_path, const Params & params)
|
||||
: Store(params)
|
||||
, LocalFSStore(params)
|
||||
, RemoteStore(params)
|
||||
, path(socket_path)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
std::string UDSRemoteStore::getUri()
|
||||
{
|
||||
return "daemon";
|
||||
if (path) {
|
||||
return std::string("unix://") + *path;
|
||||
} else {
|
||||
return "daemon";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +111,7 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
|
||||
throw SysError("cannot create Unix domain socket");
|
||||
closeOnExec(conn->fd.get());
|
||||
|
||||
string socketPath = settings.nixDaemonSocketFile;
|
||||
string socketPath = path ? *path : settings.nixDaemonSocketFile;
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
@@ -721,5 +734,14 @@ void RemoteStore::Connection::processStderr(Sink * sink, Source * source)
|
||||
}
|
||||
}
|
||||
|
||||
static std::string uriScheme = "unix://";
|
||||
|
||||
static RegisterStoreImplementation regStore([](
|
||||
const std::string & uri, const Store::Params & params)
|
||||
-> std::shared_ptr<Store>
|
||||
{
|
||||
if (std::string(uri, 0, uriScheme.size()) != uriScheme) return 0;
|
||||
return std::make_shared<UDSRemoteStore>(std::string(uri, uriScheme.size()), params);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ class UDSRemoteStore : public LocalFSStore, public RemoteStore
|
||||
public:
|
||||
|
||||
UDSRemoteStore(const Params & params);
|
||||
UDSRemoteStore(std::string path, const Params & params);
|
||||
|
||||
std::string getUri() override;
|
||||
|
||||
@@ -145,6 +146,7 @@ private:
|
||||
};
|
||||
|
||||
ref<RemoteStore::Connection> openConnection() override;
|
||||
std::experimental::optional<std::string> path;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -222,11 +222,10 @@ Path Store::makeTextPath(const string & name, const Hash & hash,
|
||||
}
|
||||
|
||||
|
||||
std::pair<Path, Hash> Store::computeStorePathForPath(const Path & srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter) const
|
||||
std::pair<Path, Hash> Store::computeStorePathForPath(const string & name,
|
||||
const Path & srcPath, bool recursive, HashType hashAlgo, PathFilter & filter) const
|
||||
{
|
||||
Hash h = recursive ? hashPath(hashAlgo, srcPath, filter).first : hashFile(hashAlgo, srcPath);
|
||||
string name = baseNameOf(srcPath);
|
||||
Path dstPath = makeFixedOutputPath(recursive, h, name);
|
||||
return std::pair<Path, Hash>(dstPath, h);
|
||||
}
|
||||
@@ -840,7 +839,7 @@ ref<Store> openStore(const std::string & uri_,
|
||||
for (auto fun : *RegisterStoreImplementation::implementations) {
|
||||
auto store = fun(uri, params);
|
||||
if (store) {
|
||||
store->warnUnknownSettings();
|
||||
store->handleUnknownSettings();
|
||||
return ref<Store>(store);
|
||||
}
|
||||
}
|
||||
@@ -897,7 +896,11 @@ std::list<ref<Store>> getDefaultSubstituters()
|
||||
auto addStore = [&](const std::string & uri) {
|
||||
if (done.count(uri)) return;
|
||||
done.insert(uri);
|
||||
stores.push_back(openStore(uri));
|
||||
try {
|
||||
stores.push_back(openStore(uri));
|
||||
} catch (Error & e) {
|
||||
printError("warning: %s", e.what());
|
||||
}
|
||||
};
|
||||
|
||||
for (auto uri : settings.substituters.get())
|
||||
|
||||
@@ -192,7 +192,7 @@ struct ValidPathInfo
|
||||
typedef list<ValidPathInfo> ValidPathInfos;
|
||||
|
||||
|
||||
enum BuildMode { bmNormal, bmRepair, bmCheck, bmHash };
|
||||
enum BuildMode { bmNormal, bmRepair, bmCheck };
|
||||
|
||||
|
||||
struct BuildResult
|
||||
@@ -248,6 +248,8 @@ public:
|
||||
|
||||
const Setting<int> pathInfoCacheSize{this, 65536, "path-info-cache-size", "size of the in-memory store path information cache"};
|
||||
|
||||
const Setting<bool> isTrusted{this, false, "trusted", "whether paths from this store can be used as substitutes even when they lack trusted signatures"};
|
||||
|
||||
protected:
|
||||
|
||||
struct State
|
||||
@@ -305,9 +307,9 @@ public:
|
||||
/* This is the preparatory part of addToStore(); it computes the
|
||||
store path to which srcPath is to be copied. Returns the store
|
||||
path and the cryptographic hash of the contents of srcPath. */
|
||||
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter) const;
|
||||
std::pair<Path, Hash> computeStorePathForPath(const string & name,
|
||||
const Path & srcPath, bool recursive = true,
|
||||
HashType hashAlgo = htSHA256, PathFilter & filter = defaultPathFilter) const;
|
||||
|
||||
/* Preparatory part of addTextToStore().
|
||||
|
||||
@@ -597,6 +599,11 @@ public:
|
||||
"nix-cache-info" file. Lower value means higher priority. */
|
||||
virtual int getPriority() { return 0; }
|
||||
|
||||
virtual Path toRealPath(const Path & storePath)
|
||||
{
|
||||
return storePath;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Stats stats;
|
||||
@@ -639,9 +646,10 @@ public:
|
||||
|
||||
virtual Path getRealStoreDir() { return storeDir; }
|
||||
|
||||
Path toRealPath(const Path & storePath)
|
||||
Path toRealPath(const Path & storePath) override
|
||||
{
|
||||
return getRealStoreDir() + "/" + baseNameOf(storePath);
|
||||
assert(isInStore(storePath));
|
||||
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
|
||||
}
|
||||
|
||||
std::shared_ptr<std::string> getBuildLog(const Path & path) override;
|
||||
@@ -699,6 +707,9 @@ void removeTempRoots();
|
||||
* ‘daemon’: The Nix store accessed via a Unix domain socket
|
||||
connection to nix-daemon.
|
||||
|
||||
* ‘unix://<path>’: The Nix store accessed via a Unix domain socket
|
||||
connection to nix-daemon, with the socket located at <path>.
|
||||
|
||||
* ‘auto’ or ‘’: Equivalent to ‘local’ or ‘daemon’ depending on
|
||||
whether the user has write access to the local Nix
|
||||
store/database.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "compression.hh"
|
||||
#include "util.hh"
|
||||
#include "finally.hh"
|
||||
#include "logging.hh"
|
||||
|
||||
#include <lzma.h>
|
||||
#include <bzlib.h>
|
||||
@@ -151,10 +152,10 @@ static ref<std::string> decompressBrotli(const std::string & in)
|
||||
#endif // HAVE_BROTLI
|
||||
}
|
||||
|
||||
ref<std::string> compress(const std::string & method, const std::string & in)
|
||||
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel)
|
||||
{
|
||||
StringSink ssink;
|
||||
auto sink = makeCompressionSink(method, ssink);
|
||||
auto sink = makeCompressionSink(method, ssink, parallel);
|
||||
(*sink)(in);
|
||||
sink->finish();
|
||||
return ssink.s;
|
||||
@@ -189,10 +190,9 @@ struct XzSink : CompressionSink
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
bool finished = false;
|
||||
|
||||
XzSink(Sink & nextSink) : nextSink(nextSink)
|
||||
{
|
||||
lzma_ret ret = lzma_easy_encoder(
|
||||
&strm, 6, LZMA_CHECK_CRC64);
|
||||
template <typename F>
|
||||
XzSink(Sink & nextSink, F&& initEncoder) : nextSink(nextSink) {
|
||||
lzma_ret ret = initEncoder();
|
||||
if (ret != LZMA_OK)
|
||||
throw CompressionError("unable to initialise lzma encoder");
|
||||
// FIXME: apply the x86 BCJ filter?
|
||||
@@ -200,6 +200,9 @@ struct XzSink : CompressionSink
|
||||
strm.next_out = outbuf;
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
}
|
||||
XzSink(Sink & nextSink) : XzSink(nextSink, [this]() {
|
||||
return lzma_easy_encoder(&strm, 6, LZMA_CHECK_CRC64);
|
||||
}) {}
|
||||
|
||||
~XzSink()
|
||||
{
|
||||
@@ -253,6 +256,27 @@ struct XzSink : CompressionSink
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAVE_LZMA_MT
|
||||
struct ParallelXzSink : public XzSink
|
||||
{
|
||||
ParallelXzSink(Sink &nextSink) : XzSink(nextSink, [this]() {
|
||||
lzma_mt mt_options = {};
|
||||
mt_options.flags = 0;
|
||||
mt_options.timeout = 300; // Using the same setting as the xz cmd line
|
||||
mt_options.preset = LZMA_PRESET_DEFAULT;
|
||||
mt_options.filters = NULL;
|
||||
mt_options.check = LZMA_CHECK_CRC64;
|
||||
mt_options.threads = lzma_cputhreads();
|
||||
mt_options.block_size = 0;
|
||||
if (mt_options.threads == 0)
|
||||
mt_options.threads = 1;
|
||||
// FIXME: maybe use lzma_stream_encoder_mt_memusage() to control the
|
||||
// number of threads.
|
||||
return lzma_stream_encoder_mt(&strm, &mt_options);
|
||||
}) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
struct BzipSink : CompressionSink
|
||||
{
|
||||
Sink & nextSink;
|
||||
@@ -449,8 +473,16 @@ struct BrotliSink : CompressionSink
|
||||
};
|
||||
#endif // HAVE_BROTLI
|
||||
|
||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink)
|
||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel)
|
||||
{
|
||||
if (parallel) {
|
||||
#ifdef HAVE_LZMA_MT
|
||||
if (method == "xz")
|
||||
return make_ref<ParallelXzSink>(nextSink);
|
||||
#endif
|
||||
printMsg(lvlError, format("Warning: parallel compression requested but not supported for method '%1%', falling back to single-threaded compression") % method);
|
||||
}
|
||||
|
||||
if (method == "none")
|
||||
return make_ref<NoneSink>(nextSink);
|
||||
else if (method == "xz")
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
ref<std::string> compress(const std::string & method, const std::string & in);
|
||||
ref<std::string> compress(const std::string & method, const std::string & in, const bool parallel = false);
|
||||
|
||||
ref<std::string> decompress(const std::string & method, const std::string & in);
|
||||
|
||||
@@ -17,7 +17,7 @@ struct CompressionSink : BufferedSink
|
||||
virtual void finish() = 0;
|
||||
};
|
||||
|
||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink);
|
||||
ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false);
|
||||
|
||||
MakeError(UnknownCompressionMethod, Error);
|
||||
|
||||
|
||||
@@ -7,10 +7,12 @@ namespace nix {
|
||||
void Config::set(const std::string & name, const std::string & value)
|
||||
{
|
||||
auto i = _settings.find(name);
|
||||
if (i == _settings.end())
|
||||
throw UsageError("unknown setting '%s'", name);
|
||||
i->second.setting->set(value);
|
||||
i->second.setting->overriden = true;
|
||||
if (i == _settings.end()) {
|
||||
extras.emplace(name, value);
|
||||
} else {
|
||||
i->second.setting->set(value);
|
||||
i->second.setting->overriden = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Config::addSetting(AbstractSetting * setting)
|
||||
@@ -21,34 +23,34 @@ void Config::addSetting(AbstractSetting * setting)
|
||||
|
||||
bool set = false;
|
||||
|
||||
auto i = initials.find(setting->name);
|
||||
if (i != initials.end()) {
|
||||
auto i = extras.find(setting->name);
|
||||
if (i != extras.end()) {
|
||||
setting->set(i->second);
|
||||
setting->overriden = true;
|
||||
initials.erase(i);
|
||||
extras.erase(i);
|
||||
set = true;
|
||||
}
|
||||
|
||||
for (auto & alias : setting->aliases) {
|
||||
auto i = initials.find(alias);
|
||||
if (i != initials.end()) {
|
||||
auto i = extras.find(alias);
|
||||
if (i != extras.end()) {
|
||||
if (set)
|
||||
warn("setting '%s' is set, but it's an alias of '%s' which is also set",
|
||||
alias, setting->name);
|
||||
else {
|
||||
setting->set(i->second);
|
||||
setting->overriden = true;
|
||||
initials.erase(i);
|
||||
extras.erase(i);
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::warnUnknownSettings()
|
||||
void Config::handleUnknownSettings()
|
||||
{
|
||||
for (auto & i : initials)
|
||||
warn("unknown setting '%s'", i.first);
|
||||
for (auto & s : extras)
|
||||
warn("unknown setting '%s'", s.first);
|
||||
}
|
||||
|
||||
StringMap Config::getSettings(bool overridenOnly)
|
||||
@@ -60,7 +62,7 @@ StringMap Config::getSettings(bool overridenOnly)
|
||||
return res;
|
||||
}
|
||||
|
||||
void Config::applyConfigFile(const Path & path, bool fatal)
|
||||
void Config::applyConfigFile(const Path & path)
|
||||
{
|
||||
try {
|
||||
string contents = readFile(path);
|
||||
@@ -80,7 +82,31 @@ void Config::applyConfigFile(const Path & path, bool fatal)
|
||||
vector<string> tokens = tokenizeString<vector<string> >(line);
|
||||
if (tokens.empty()) continue;
|
||||
|
||||
if (tokens.size() < 2 || tokens[1] != "=")
|
||||
if (tokens.size() < 2)
|
||||
throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
|
||||
|
||||
auto include = false;
|
||||
auto ignoreMissing = false;
|
||||
if (tokens[0] == "include")
|
||||
include = true;
|
||||
else if (tokens[0] == "!include") {
|
||||
include = true;
|
||||
ignoreMissing = true;
|
||||
}
|
||||
|
||||
if (include) {
|
||||
if (tokens.size() != 2)
|
||||
throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
|
||||
auto p = absPath(tokens[1], dirOf(path));
|
||||
if (pathExists(p)) {
|
||||
applyConfigFile(p);
|
||||
} else if (!ignoreMissing) {
|
||||
throw Error("file '%1%' included from '%2%' not found", p, path);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokens[1] != "=")
|
||||
throw UsageError("illegal configuration line '%1%' in '%2%'", line, path);
|
||||
|
||||
string name = tokens[0];
|
||||
@@ -88,12 +114,7 @@ void Config::applyConfigFile(const Path & path, bool fatal)
|
||||
vector<string>::iterator i = tokens.begin();
|
||||
advance(i, 2);
|
||||
|
||||
try {
|
||||
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
|
||||
} catch (UsageError & e) {
|
||||
if (fatal) throw;
|
||||
warn("in configuration file '%s': %s", path, e.what());
|
||||
}
|
||||
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
|
||||
};
|
||||
} catch (SysError &) { }
|
||||
}
|
||||
|
||||
@@ -48,25 +48,25 @@ private:
|
||||
|
||||
Settings _settings;
|
||||
|
||||
StringMap initials;
|
||||
StringMap extras;
|
||||
|
||||
public:
|
||||
|
||||
Config(const StringMap & initials)
|
||||
: initials(initials)
|
||||
: extras(initials)
|
||||
{ }
|
||||
|
||||
void set(const std::string & name, const std::string & value);
|
||||
|
||||
void addSetting(AbstractSetting * setting);
|
||||
|
||||
void warnUnknownSettings();
|
||||
void handleUnknownSettings();
|
||||
|
||||
StringMap getSettings(bool overridenOnly = false);
|
||||
|
||||
const Settings & _getSettings() { return _settings; }
|
||||
|
||||
void applyConfigFile(const Path & path, bool fatal = false);
|
||||
void applyConfigFile(const Path & path);
|
||||
|
||||
void resetOverriden();
|
||||
|
||||
|
||||
@@ -189,7 +189,8 @@ Hash::Hash(const std::string & s, HashType type)
|
||||
|
||||
else if (size == base64Len()) {
|
||||
auto d = base64Decode(std::string(s, pos));
|
||||
assert(d.size() == hashSize);
|
||||
if (d.size() != hashSize)
|
||||
throw BadHash("invalid base-64 hash '%s'", s);
|
||||
memcpy(hash, d.data(), hashSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
prefix = std::string("<") + c + ">";
|
||||
}
|
||||
|
||||
writeToStderr(prefix + (tty ? fs.s : filterANSIEscapes(fs.s)) + "\n");
|
||||
writeToStderr(prefix + filterANSIEscapes(fs.s) + "\n");
|
||||
}
|
||||
|
||||
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
||||
|
||||
@@ -21,13 +21,29 @@ public:
|
||||
MonitorFdHup(int fd)
|
||||
{
|
||||
thread = std::thread([fd]() {
|
||||
/* Wait indefinitely until a POLLHUP occurs. */
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = 0;
|
||||
if (poll(fds, 1, -1) == -1) abort(); // can't happen
|
||||
assert(fds[0].revents & POLLHUP);
|
||||
triggerInterrupt();
|
||||
while (true) {
|
||||
/* Wait indefinitely until a POLLHUP occurs. */
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = fd;
|
||||
/* This shouldn't be necessary, but macOS doesn't seem to
|
||||
like a zeroed out events field.
|
||||
See rdar://37537852.
|
||||
*/
|
||||
fds[0].events = POLLHUP;
|
||||
auto count = poll(fds, 1, -1);
|
||||
if (count == -1) abort(); // can't happen
|
||||
/* This shouldn't happen, but can on macOS due to a bug.
|
||||
See rdar://37550628.
|
||||
|
||||
This may eventually need a delay or further
|
||||
coordination with the main thread if spinning proves
|
||||
too harmful.
|
||||
*/
|
||||
if (count == 0) continue;
|
||||
assert(fds[0].revents & POLLHUP);
|
||||
triggerInterrupt();
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -192,6 +192,12 @@ bool isInDir(const Path & path, const Path & dir)
|
||||
}
|
||||
|
||||
|
||||
bool isDirOrInDir(const Path & path, const Path & dir)
|
||||
{
|
||||
return path == dir or isInDir(path, dir);
|
||||
}
|
||||
|
||||
|
||||
struct stat lstat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
@@ -1172,36 +1178,51 @@ void ignoreException()
|
||||
}
|
||||
|
||||
|
||||
string filterANSIEscapes(const string & s, bool nixOnly)
|
||||
std::string filterANSIEscapes(const std::string & s, unsigned int width)
|
||||
{
|
||||
string t, r;
|
||||
enum { stTop, stEscape, stCSI } state = stTop;
|
||||
for (auto c : s) {
|
||||
if (state == stTop) {
|
||||
if (c == '\e') {
|
||||
state = stEscape;
|
||||
r = c;
|
||||
} else
|
||||
t += c;
|
||||
} else if (state == stEscape) {
|
||||
r += c;
|
||||
if (c == '[')
|
||||
state = stCSI;
|
||||
else {
|
||||
t += r;
|
||||
state = stTop;
|
||||
std::string t, e;
|
||||
size_t w = 0;
|
||||
auto i = s.begin();
|
||||
|
||||
while (w < (size_t) width && i != s.end()) {
|
||||
|
||||
if (*i == '\e') {
|
||||
std::string e;
|
||||
e += *i++;
|
||||
char last = 0;
|
||||
|
||||
if (i != s.end() && *i == '[') {
|
||||
e += *i++;
|
||||
// eat parameter bytes
|
||||
while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++;
|
||||
// eat intermediate bytes
|
||||
while (i != s.end() && *i >= 0x20 && *i <= 0x2f) e += *i++;
|
||||
// eat final byte
|
||||
if (i != s.end() && *i >= 0x40 && *i <= 0x7e) e += last = *i++;
|
||||
} else {
|
||||
if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++;
|
||||
}
|
||||
} else {
|
||||
r += c;
|
||||
if (c >= 0x40 && c <= 0x7e) {
|
||||
if (nixOnly && (c != 'p' && c != 'q' && c != 's' && c != 'a' && c != 'b'))
|
||||
t += r;
|
||||
state = stTop;
|
||||
r.clear();
|
||||
|
||||
if (last == 'm')
|
||||
t += e;
|
||||
}
|
||||
|
||||
else if (*i == '\t') {
|
||||
i++; t += ' '; w++;
|
||||
while (w < (size_t) width && w % 8) {
|
||||
t += ' '; w++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (*i == '\r')
|
||||
// do nothing for now
|
||||
i++;
|
||||
|
||||
else {
|
||||
t += *i++; w++;
|
||||
}
|
||||
}
|
||||
t += r;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,10 +53,12 @@ Path dirOf(const Path & path);
|
||||
following the final `/'. */
|
||||
string baseNameOf(const Path & path);
|
||||
|
||||
/* Check whether a given path is a descendant of the given
|
||||
directory. */
|
||||
/* Check whether 'path' is a descendant of 'dir'. */
|
||||
bool isInDir(const Path & path, const Path & dir);
|
||||
|
||||
/* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */
|
||||
bool isDirOrInDir(const Path & path, const Path & dir);
|
||||
|
||||
/* Get status of `path'. */
|
||||
struct stat lstat(const Path & path);
|
||||
|
||||
@@ -386,10 +388,12 @@ void ignoreException();
|
||||
#define ANSI_BLUE "\e[34;1m"
|
||||
|
||||
|
||||
/* Filter out ANSI escape codes from the given string. If ‘nixOnly’ is
|
||||
set, only filter escape codes generated by Nixpkgs' stdenv (used to
|
||||
denote nesting etc.). */
|
||||
string filterANSIEscapes(const string & s, bool nixOnly = false);
|
||||
/* Truncate a string to 'width' printable characters. Certain ANSI
|
||||
escape sequences (such as colour setting) are copied but not
|
||||
included in the character count. Other ANSI escape sequences are
|
||||
filtered. Also, tabs are expanded to spaces. */
|
||||
std::string filterANSIEscapes(const std::string & s,
|
||||
unsigned int width = std::numeric_limits<unsigned int>::max());
|
||||
|
||||
|
||||
/* Base64 encoding/decoding. */
|
||||
|
||||
@@ -141,7 +141,7 @@ void mainWrapped(int argc, char * * argv)
|
||||
else if (*arg == "--version")
|
||||
printVersion(myName);
|
||||
|
||||
else if (*arg == "--add-drv-link")
|
||||
else if (*arg == "--add-drv-link" || *arg == "--indirect")
|
||||
; // obsolete
|
||||
|
||||
else if (*arg == "--no-out-link" || *arg == "--no-link")
|
||||
@@ -167,9 +167,6 @@ void mainWrapped(int argc, char * * argv)
|
||||
buildMode = bmRepair;
|
||||
}
|
||||
|
||||
else if (*arg == "--hash")
|
||||
buildMode = bmHash;
|
||||
|
||||
else if (*arg == "--run-env") // obsolete
|
||||
runEnv = true;
|
||||
|
||||
@@ -235,6 +232,8 @@ void mainWrapped(int argc, char * * argv)
|
||||
|
||||
myArgs.parseCmdline(args);
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (packages && fromArgs)
|
||||
throw UsageError("'-p' and '-E' are mutually exclusive");
|
||||
|
||||
@@ -279,8 +278,8 @@ void mainWrapped(int argc, char * * argv)
|
||||
else
|
||||
/* If we're in a #! script, interpret filenames
|
||||
relative to the script. */
|
||||
exprs.push_back(state.parseExprFromFile(resolveExprPath(lookupFileArg(state,
|
||||
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))));
|
||||
exprs.push_back(state.parseExprFromFile(resolveExprPath(state.checkSourcePath(lookupFileArg(state,
|
||||
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))));
|
||||
}
|
||||
|
||||
/* Evaluate them into derivations. */
|
||||
|
||||
@@ -213,6 +213,9 @@ int main(int argc, char ** argv)
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
initPlugins();
|
||||
|
||||
switch (cmd) {
|
||||
case cNone:
|
||||
throw UsageError("no command specified");
|
||||
|
||||
@@ -77,6 +77,8 @@ int main(int argc, char * * argv)
|
||||
return true;
|
||||
});
|
||||
|
||||
initPlugins();
|
||||
|
||||
auto profilesDir = settings.nixStateDir + "/profiles";
|
||||
if (removeOld) removeOldGenerations(profilesDir);
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ int main(int argc, char ** argv)
|
||||
return true;
|
||||
});
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (sshHost.empty())
|
||||
throw UsageError("no host name specified");
|
||||
|
||||
|
||||
@@ -994,7 +994,7 @@ static void daemonLoop(char * * argv)
|
||||
if (matchUser(user, group, trustedUsers))
|
||||
trusted = true;
|
||||
|
||||
if (!trusted && !matchUser(user, group, allowedUsers))
|
||||
if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup)
|
||||
throw Error(format("user '%1%' is not allowed to connect to the Nix daemon") % user);
|
||||
|
||||
printInfo(format((string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""))
|
||||
@@ -1060,6 +1060,8 @@ int main(int argc, char * * argv)
|
||||
return true;
|
||||
});
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (stdio) {
|
||||
if (getStoreType() == tDaemon) {
|
||||
/* Forward on this connection to the real daemon */
|
||||
|
||||
@@ -1393,6 +1393,8 @@ int main(int argc, char * * argv)
|
||||
|
||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (!op) throw UsageError("no operation specified");
|
||||
|
||||
auto store = openStore();
|
||||
|
||||
@@ -151,6 +151,8 @@ int main(int argc, char * * argv)
|
||||
|
||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (evalOnly && !wantsReadWrite)
|
||||
settings.readOnlyMode = true;
|
||||
|
||||
@@ -182,7 +184,7 @@ int main(int argc, char * * argv)
|
||||
for (auto & i : files) {
|
||||
Expr * e = fromArgs
|
||||
? state.parseExprFromString(i, absPath("."))
|
||||
: state.parseExprFromFile(resolveExprPath(lookupFileArg(state, i)));
|
||||
: state.parseExprFromFile(resolveExprPath(state.checkSourcePath(lookupFileArg(state, i))));
|
||||
processExpr(state, attrPaths, parseOnly, strict, autoArgs,
|
||||
evalOnly, outputKind, xmlOutputSourceLocation, e);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ int main(int argc, char * * argv)
|
||||
|
||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (args.size() > 2)
|
||||
throw UsageError("too many arguments");
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ static void opRealise(Strings opFlags, Strings opArgs)
|
||||
if (i == "--dry-run") dryRun = true;
|
||||
else if (i == "--repair") buildMode = bmRepair;
|
||||
else if (i == "--check") buildMode = bmCheck;
|
||||
else if (i == "--hash") buildMode = bmHash;
|
||||
else if (i == "--ignore-unknown") ignoreUnknown = true;
|
||||
else throw UsageError(format("unknown flag '%1%'") % i);
|
||||
|
||||
@@ -1053,6 +1052,8 @@ int main(int argc, char * * argv)
|
||||
return true;
|
||||
});
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (!op) throw UsageError("no operation specified");
|
||||
|
||||
if (op != opDump && op != opRestore) /* !!! hack */
|
||||
|
||||
@@ -50,7 +50,9 @@ struct CmdBuild : MixDryRun, InstallablesCommand
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
auto buildables = toBuildables(store, dryRun ? DryRun : Build, installables);
|
||||
auto buildables = build(store, dryRun ? DryRun : Build, installables);
|
||||
|
||||
if (dryRun) return;
|
||||
|
||||
for (size_t i = 0; i < buildables.size(); ++i) {
|
||||
auto & b(buildables[i]);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
extern std::string programPath;
|
||||
|
||||
struct Value;
|
||||
class Bindings;
|
||||
class EvalState;
|
||||
@@ -196,7 +198,7 @@ std::shared_ptr<Installable> parseInstallable(
|
||||
SourceExprCommand & cmd, ref<Store> store, const std::string & installable,
|
||||
bool useDefaultInstallables);
|
||||
|
||||
Buildables toBuildables(ref<Store> store, RealiseMode mode,
|
||||
Buildables build(ref<Store> store, RealiseMode mode,
|
||||
std::vector<std::shared_ptr<Installable>> installables);
|
||||
|
||||
PathSet toStorePaths(ref<Store> store, RealiseMode mode,
|
||||
|
||||
@@ -57,15 +57,15 @@ struct CmdCopy : StorePathsCommand
|
||||
return {
|
||||
Example{
|
||||
"To copy Firefox from the local store to a binary cache in file:///tmp/cache:",
|
||||
"nix copy --to file:///tmp/cache -r $(type -p firefox)"
|
||||
"nix copy --to file:///tmp/cache $(type -p firefox)"
|
||||
},
|
||||
Example{
|
||||
"To copy the entire current NixOS system closure to another machine via SSH:",
|
||||
"nix copy --to ssh://server -r /run/current-system"
|
||||
"nix copy --to ssh://server /run/current-system"
|
||||
},
|
||||
Example{
|
||||
"To copy a closure from another machine via SSH:",
|
||||
"nix copy --from ssh://server -r /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"
|
||||
"nix copy --from ssh://server /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#include "eval.hh"
|
||||
#include "json.hh"
|
||||
#include "value-to-json.hh"
|
||||
#include "progress-bar.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdEval : MixJSON, InstallablesCommand
|
||||
struct CmdEval : MixJSON, InstallableCommand
|
||||
{
|
||||
bool raw = false;
|
||||
|
||||
@@ -56,20 +57,19 @@ struct CmdEval : MixJSON, InstallablesCommand
|
||||
|
||||
auto state = getEvalState();
|
||||
|
||||
auto jsonOut = json ? std::make_unique<JSONList>(std::cout) : nullptr;
|
||||
auto v = installable->toValue(*state);
|
||||
PathSet context;
|
||||
|
||||
for (auto & i : installables) {
|
||||
auto v = i->toValue(*state);
|
||||
PathSet context;
|
||||
if (raw) {
|
||||
std::cout << state->coerceToString(noPos, *v, context);
|
||||
} else if (json) {
|
||||
auto jsonElem = jsonOut->placeholder();
|
||||
printValueAsJSON(*state, true, *v, jsonElem, context);
|
||||
} else {
|
||||
state->forceValueDeep(*v);
|
||||
std::cout << *v << "\n";
|
||||
}
|
||||
stopProgressBar();
|
||||
|
||||
if (raw) {
|
||||
std::cout << state->coerceToString(noPos, *v, context);
|
||||
} else if (json) {
|
||||
JSONPlaceholder jsonOut(std::cout);
|
||||
printValueAsJSON(*state, true, *v, jsonOut, context);
|
||||
} else {
|
||||
state->forceValueDeep(*v);
|
||||
std::cout << *v << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,10 +30,8 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state)
|
||||
|
||||
vSourceExpr = state.allocValue();
|
||||
|
||||
if (file != "") {
|
||||
Expr * e = state.parseExprFromFile(resolveExprPath(lookupFileArg(state, file)));
|
||||
state.eval(e, *vSourceExpr);
|
||||
}
|
||||
if (file != "")
|
||||
state.evalFile(lookupFileArg(state, file), *vSourceExpr);
|
||||
|
||||
else {
|
||||
|
||||
@@ -255,7 +253,7 @@ std::shared_ptr<Installable> parseInstallable(
|
||||
return installables.front();
|
||||
}
|
||||
|
||||
Buildables toBuildables(ref<Store> store, RealiseMode mode,
|
||||
Buildables build(ref<Store> store, RealiseMode mode,
|
||||
std::vector<std::shared_ptr<Installable>> installables)
|
||||
{
|
||||
if (mode != Build)
|
||||
@@ -293,7 +291,7 @@ PathSet toStorePaths(ref<Store> store, RealiseMode mode,
|
||||
{
|
||||
PathSet outPaths;
|
||||
|
||||
for (auto & b : toBuildables(store, mode, installables))
|
||||
for (auto & b : build(store, mode, installables))
|
||||
for (auto & output : b.outputs)
|
||||
outPaths.insert(output.second);
|
||||
|
||||
|
||||
@@ -6,4 +6,6 @@ nix_SOURCES := $(wildcard $(d)/*.cc) $(wildcard src/linenoise/*.cpp)
|
||||
|
||||
nix_LIBS = libexpr libmain libstore libutil libformat
|
||||
|
||||
nix_LDFLAGS = -pthread
|
||||
|
||||
$(eval $(call install-symlink, nix, $(bindir)/nix-hash))
|
||||
|
||||
@@ -50,6 +50,7 @@ struct CmdLog : InstallableCommand
|
||||
|
||||
auto b = installable->toBuildable();
|
||||
|
||||
RunPager pager;
|
||||
for (auto & sub : subs) {
|
||||
auto log = b.drvPath != "" ? sub->getBuildLog(b.drvPath) : nullptr;
|
||||
for (auto & output : b.outputs) {
|
||||
|
||||
@@ -90,6 +90,16 @@ struct CmdLsStore : StoreCommand, MixLs
|
||||
expectArg("path", &path);
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To list the contents of a store path in a binary cache:",
|
||||
"nix ls-store --store https://cache.nixos.org/ -lR /nix/store/0i2jd68mp5g6h2sa5k9c85rb80sn8hi9-hello-2.10"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
std::string name() override
|
||||
{
|
||||
return "ls-store";
|
||||
@@ -116,6 +126,16 @@ struct CmdLsNar : Command, MixLs
|
||||
expectArg("path", &path);
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To list a specific file in a NAR:",
|
||||
"nix ls-nar -l hello.nar /bin/hello"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
std::string name() override
|
||||
{
|
||||
return "ls-nar";
|
||||
|
||||
@@ -16,6 +16,8 @@ void chrootHelper(int argc, char * * argv);
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string programPath;
|
||||
|
||||
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||
{
|
||||
NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix")
|
||||
@@ -78,7 +80,8 @@ void mainWrapped(int argc, char * * argv)
|
||||
initNix();
|
||||
initGC();
|
||||
|
||||
string programName = baseNameOf(argv[0]);
|
||||
programPath = argv[0];
|
||||
string programName = baseNameOf(programPath);
|
||||
|
||||
{
|
||||
auto legacy = (*RegisterLegacyCommand::commands)[programName];
|
||||
@@ -89,6 +92,8 @@ void mainWrapped(int argc, char * * argv)
|
||||
|
||||
args.parseCmdline(argvToStrings(argc, argv));
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (!args.command) args.showHelpAndExit();
|
||||
|
||||
Finally f([]() { stopProgressBar(); });
|
||||
|
||||
35
src/nix/ping-store.cc
Normal file
35
src/nix/ping-store.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "command.hh"
|
||||
#include "shared.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdPingStore : StoreCommand
|
||||
{
|
||||
std::string name() override
|
||||
{
|
||||
return "ping-store";
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
{
|
||||
return "test whether a store can be opened";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To test whether connecting to a remote Nix store via SSH works:",
|
||||
"nix ping-store --store ssh://mac1"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
store->connect();
|
||||
}
|
||||
};
|
||||
|
||||
static RegisterCommand r1(make_ref<CmdPingStore>());
|
||||
@@ -3,8 +3,9 @@
|
||||
#include "sync.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -22,44 +23,6 @@ static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n)
|
||||
return fields[n].i;
|
||||
}
|
||||
|
||||
/* Truncate a string to 'width' printable characters. ANSI escape
|
||||
sequences are copied but not included in the character count. Also,
|
||||
tabs are expanded to spaces. */
|
||||
static std::string ansiTruncate(const std::string & s, int width)
|
||||
{
|
||||
if (width <= 0) return s;
|
||||
|
||||
std::string t;
|
||||
size_t w = 0;
|
||||
auto i = s.begin();
|
||||
|
||||
while (w < (size_t) width && i != s.end()) {
|
||||
if (*i == '\e') {
|
||||
t += *i++;
|
||||
if (i != s.end() && *i == '[') {
|
||||
t += *i++;
|
||||
while (i != s.end() && (*i < 0x40 || *i > 0x7e)) {
|
||||
t += *i++;
|
||||
}
|
||||
if (i != s.end()) t += *i++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (*i == '\t') {
|
||||
t += ' '; w++;
|
||||
while (w < (size_t) width && w & 8) {
|
||||
t += ' '; w++;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
t += *i++; w++;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
class ProgressBar : public Logger
|
||||
{
|
||||
private:
|
||||
@@ -101,15 +64,28 @@ private:
|
||||
|
||||
Sync<State> state_;
|
||||
|
||||
std::thread updateThread;
|
||||
|
||||
std::condition_variable quitCV, updateCV;
|
||||
|
||||
public:
|
||||
|
||||
ProgressBar()
|
||||
{
|
||||
updateThread = std::thread([&]() {
|
||||
auto state(state_.lock());
|
||||
while (state->active) {
|
||||
state.wait(updateCV);
|
||||
draw(*state);
|
||||
state.wait_for(quitCV, std::chrono::milliseconds(50));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~ProgressBar()
|
||||
{
|
||||
stop();
|
||||
updateThread.join();
|
||||
}
|
||||
|
||||
void stop()
|
||||
@@ -121,6 +97,8 @@ public:
|
||||
writeToStderr("\r\e[K");
|
||||
if (status != "")
|
||||
writeToStderr("[" + status + "]\n");
|
||||
updateCV.notify_one();
|
||||
quitCV.notify_one();
|
||||
}
|
||||
|
||||
void log(Verbosity lvl, const FormatOrString & fs) override
|
||||
@@ -132,7 +110,7 @@ public:
|
||||
void log(State & state, Verbosity lvl, const std::string & s)
|
||||
{
|
||||
writeToStderr("\r\e[K" + s + ANSI_NORMAL "\n");
|
||||
update(state);
|
||||
draw(state);
|
||||
}
|
||||
|
||||
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
||||
@@ -167,7 +145,12 @@ public:
|
||||
|
||||
if (type == actSubstitute) {
|
||||
auto name = storePathToName(getS(fields, 0));
|
||||
i->s = fmt("fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s", name, getS(fields, 1));
|
||||
auto sub = getS(fields, 1);
|
||||
i->s = fmt(
|
||||
hasPrefix(sub, "local")
|
||||
? "copying " ANSI_BOLD "%s" ANSI_NORMAL " from %s"
|
||||
: "fetching " ANSI_BOLD "%s" ANSI_NORMAL " from %s",
|
||||
name, sub);
|
||||
}
|
||||
|
||||
if (type == actQueryPathInfo) {
|
||||
@@ -180,7 +163,7 @@ public:
|
||||
|| (type == actCopyPath && hasAncestor(*state, actSubstitute, parent)))
|
||||
i->visible = false;
|
||||
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
/* Check whether an activity has an ancestore with the specified
|
||||
@@ -215,7 +198,7 @@ public:
|
||||
state->its.erase(i);
|
||||
}
|
||||
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override
|
||||
@@ -225,7 +208,7 @@ public:
|
||||
if (type == resFileLinked) {
|
||||
state->filesLinked++;
|
||||
state->bytesLinked += getI(fields, 0);
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
else if (type == resBuildLogLine) {
|
||||
@@ -238,25 +221,25 @@ public:
|
||||
info.lastLine = lastLine;
|
||||
state->activities.emplace_back(info);
|
||||
i->second = std::prev(state->activities.end());
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == resUntrustedPath) {
|
||||
state->untrustedPaths++;
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
else if (type == resCorruptedPath) {
|
||||
state->corruptedPaths++;
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
else if (type == resSetPhase) {
|
||||
auto i = state->its.find(act);
|
||||
assert(i != state->its.end());
|
||||
i->second->phase = getS(fields, 0);
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
else if (type == resProgress) {
|
||||
@@ -267,7 +250,7 @@ public:
|
||||
actInfo.expected = getI(fields, 1);
|
||||
actInfo.running = getI(fields, 2);
|
||||
actInfo.failed = getI(fields, 3);
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
|
||||
else if (type == resSetExpected) {
|
||||
@@ -279,17 +262,16 @@ public:
|
||||
state->activitiesByType[type].expected -= j;
|
||||
j = getI(fields, 1);
|
||||
state->activitiesByType[type].expected += j;
|
||||
update(*state);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
auto state(state_.lock());
|
||||
update(*state);
|
||||
updateCV.notify_one();
|
||||
}
|
||||
|
||||
void update(State & state)
|
||||
void draw(State & state)
|
||||
{
|
||||
if (!state.active) return;
|
||||
|
||||
@@ -323,7 +305,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
writeToStderr("\r" + ansiTruncate(line, getWindowSize().second) + "\e[K");
|
||||
auto width = getWindowSize().second;
|
||||
if (width <= 0) std::numeric_limits<decltype(width)>::max();
|
||||
|
||||
writeToStderr("\r" + filterANSIEscapes(line, width) + "\e[K");
|
||||
}
|
||||
|
||||
std::string getStatus(State & state)
|
||||
|
||||
@@ -85,6 +85,10 @@ struct CmdRun : InstallablesCommand
|
||||
"To run GNU Hello:",
|
||||
"nix run nixpkgs.hello -c hello --greeting 'Hi everybody!'"
|
||||
},
|
||||
Example{
|
||||
"To run GNU Hello in a chroot store:",
|
||||
"nix run --store ~/my-nix nixpkgs.hello -c hello"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
131
src/nix/upgrade-nix.cc
Normal file
131
src/nix/upgrade-nix.cc
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "command.hh"
|
||||
#include "store-api.hh"
|
||||
#include "download.hh"
|
||||
#include "eval.hh"
|
||||
#include "attr-path.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdUpgradeNix : StoreCommand
|
||||
{
|
||||
Path profileDir;
|
||||
|
||||
CmdUpgradeNix()
|
||||
{
|
||||
mkFlag()
|
||||
.longName("profile")
|
||||
.shortName('p')
|
||||
.labels({"profile-dir"})
|
||||
.description("the Nix profile to upgrade")
|
||||
.dest(&profileDir);
|
||||
}
|
||||
|
||||
std::string name() override
|
||||
{
|
||||
return "upgrade-nix";
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
{
|
||||
return "upgrade Nix to the latest stable version";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To upgrade Nix to the latest stable version:",
|
||||
"nix upgrade-nix"
|
||||
},
|
||||
Example{
|
||||
"To upgrade Nix in a specific profile:",
|
||||
"nix upgrade-nix -p /nix/var/nix/profiles/per-user/alice/profile"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
settings.pureEval = true;
|
||||
|
||||
if (profileDir == "")
|
||||
profileDir = getProfileDir(store);
|
||||
|
||||
printInfo("upgrading Nix in profile '%s'", profileDir);
|
||||
|
||||
Path storePath;
|
||||
{
|
||||
Activity act(*logger, lvlInfo, actUnknown, "querying latest Nix version");
|
||||
storePath = getLatestNix(store);
|
||||
}
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlInfo, actUnknown, fmt("downloading '%s'...", storePath));
|
||||
store->ensurePath(storePath);
|
||||
}
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlInfo, actUnknown, fmt("verifying that '%s' works...", storePath));
|
||||
auto program = storePath + "/bin/nix-env";
|
||||
auto s = runProgram(program, false, {"--version"});
|
||||
if (s.find("Nix") == std::string::npos)
|
||||
throw Error("could not verify that '%s' works", program);
|
||||
}
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlInfo, actUnknown, fmt("installing '%s' into profile '%s'...", storePath, profileDir));
|
||||
runProgram(settings.nixBinDir + "/nix-env", false,
|
||||
{"--profile", profileDir, "-i", storePath, "--no-sandbox"});
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the profile in which Nix is installed. */
|
||||
Path getProfileDir(ref<Store> store)
|
||||
{
|
||||
Path where;
|
||||
|
||||
for (auto & dir : tokenizeString<Strings>(getEnv("PATH"), ":"))
|
||||
if (pathExists(dir + "/nix-env")) {
|
||||
where = dir;
|
||||
break;
|
||||
}
|
||||
|
||||
if (where == "")
|
||||
throw Error("couldn't figure out how Nix is installed, so I can't upgrade it");
|
||||
|
||||
printInfo("found Nix in '%s'", where);
|
||||
|
||||
if (hasPrefix(where, "/run/current-system"))
|
||||
throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'");
|
||||
|
||||
Path profileDir;
|
||||
Path userEnv;
|
||||
|
||||
if (baseNameOf(where) != "bin" ||
|
||||
!hasSuffix(userEnv = canonPath(profileDir = dirOf(where), true), "user-environment"))
|
||||
throw Error("directory '%s' does not appear to be part of a Nix profile", where);
|
||||
|
||||
if (!store->isValidPath(userEnv))
|
||||
throw Error("directory '%s' is not in the Nix store", userEnv);
|
||||
|
||||
return profileDir;
|
||||
}
|
||||
|
||||
/* Return the store path of the latest stable Nix. */
|
||||
Path getLatestNix(ref<Store> store)
|
||||
{
|
||||
// FIXME: use nixos.org?
|
||||
auto req = DownloadRequest("https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/tools/nix-fallback-paths.nix");
|
||||
auto res = getDownloader()->download(req);
|
||||
|
||||
EvalState state(Strings(), store);
|
||||
auto v = state.allocValue();
|
||||
state.eval(state.parseExprFromString(*res.data, "/no-such-path"), *v);
|
||||
Bindings & bindings(*state.allocBindings(0));
|
||||
auto v2 = findAlongAttrPath(state, settings.thisSystem, bindings, *v);
|
||||
|
||||
return state.forceString(*v2);
|
||||
}
|
||||
};
|
||||
|
||||
static RegisterCommand r1(make_ref<CmdUpgradeNix>());
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user