Compare commits
4 Commits
memoise
...
darwin-tar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7763e4576 | ||
|
|
9f29ddd07c | ||
|
|
af10cd93a5 | ||
|
|
555f283064 |
27
.github/ISSUE_TEMPLATE.md
vendored
27
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,27 +0,0 @@
|
||||
<!--
|
||||
|
||||
# Filing a Nix issue
|
||||
|
||||
*WAIT* Are you sure you're filing your issue in the right repository?
|
||||
|
||||
We appreciate you taking the time to tell us about issues you encounter, but routing the issue to the right place will get you help sooner and save everyone time.
|
||||
|
||||
This is the Nix repository, and issues here should be about Nix the build and package management *_tool_*.
|
||||
|
||||
If you have a problem with a specific package on NixOS or when using Nix, you probably want to file an issue with _nixpkgs_, whose issue tracker is over at https://github.com/NixOS/nixpkgs/issues.
|
||||
|
||||
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
|
||||
|
||||
Examples of _nixpkgs_ issues:
|
||||
|
||||
- glibc is b0rked on aarch64
|
||||
- chromium in NixOS doesn't support U2F but google-chrome does!
|
||||
- The OpenJDK package on macOS is missing a key component
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -99,20 +99,16 @@ perl/Makefile.config
|
||||
/misc/systemd/nix-daemon.socket
|
||||
/misc/upstart/nix-daemon.conf
|
||||
|
||||
/src/resolve-system-dependencies/resolve-system-dependencies
|
||||
|
||||
inst/
|
||||
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.exe
|
||||
*.dep
|
||||
*~
|
||||
*.pc
|
||||
*.plist
|
||||
|
||||
# GNU Global
|
||||
GPATH
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
os: osx
|
||||
script: ./tests/install-darwin.sh
|
||||
1
Makefile
1
Makefile
@@ -23,6 +23,7 @@ makefiles = \
|
||||
misc/systemd/local.mk \
|
||||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk \
|
||||
misc/emacs/local.mk \
|
||||
doc/manual/local.mk \
|
||||
tests/local.mk
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ CXXFLAGS = @CXXFLAGS@
|
||||
ENABLE_S3 = @ENABLE_S3@
|
||||
HAVE_SODIUM = @HAVE_SODIUM@
|
||||
HAVE_READLINE = @HAVE_READLINE@
|
||||
HAVE_BROTLI = @HAVE_BROTLI@
|
||||
LIBCURL_LIBS = @LIBCURL_LIBS@
|
||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
@@ -14,10 +13,9 @@ PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
SODIUM_LIBS = @SODIUM_LIBS@
|
||||
LIBLZMA_LIBS = @LIBLZMA_LIBS@
|
||||
SQLITE3_LIBS = @SQLITE3_LIBS@
|
||||
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
|
||||
bash = @bash@
|
||||
bindir = @bindir@
|
||||
brotli = @brotli@
|
||||
bro = @bro@
|
||||
lsof = @lsof@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
|
||||
17
configure.ac
17
configure.ac
@@ -127,7 +127,7 @@ NEED_PROG(gzip, gzip)
|
||||
NEED_PROG(xz, xz)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(pv, pv, pv)
|
||||
AC_PATH_PROGS(brotli, brotli bro, bro)
|
||||
AC_PATH_PROG(bro, bro, bro)
|
||||
AC_PATH_PROG(lsof, lsof, lsof)
|
||||
|
||||
|
||||
@@ -176,13 +176,6 @@ AC_SUBST(HAVE_SODIUM, [$have_sodium])
|
||||
PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"])
|
||||
|
||||
|
||||
# Look for libbrotli{enc,dec}, optional dependencies
|
||||
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec],
|
||||
[AC_DEFINE([HAVE_BROTLI], [1], [Whether to use libbrotli.])
|
||||
CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"]
|
||||
have_brotli=1], [have_brotli=])
|
||||
AC_SUBST(HAVE_BROTLI, [$have_brotli])
|
||||
|
||||
# Look for libseccomp, required for Linux sandboxing.
|
||||
if test "$sys_name" = linux; then
|
||||
PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp],
|
||||
@@ -193,17 +186,11 @@ fi
|
||||
# Look for aws-cpp-sdk-s3.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([aws/s3/S3Client.h],
|
||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-sdk-cpp.])
|
||||
[AC_DEFINE([ENABLE_S3], [1], [Whether to enable S3 support via aws-cpp-sdk-s3.])
|
||||
enable_s3=1], [enable_s3=])
|
||||
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 '.' ' '))
|
||||
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
|
||||
|
||||
|
||||
# Whether to use the Boehm garbage collector.
|
||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{ system ? builtins.currentSystem
|
||||
, url
|
||||
, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""
|
||||
, md5 ? "", sha1 ? "", sha256 ? ""
|
||||
, outputHash ?
|
||||
if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
|
||||
if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
|
||||
, outputHashAlgo ?
|
||||
if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
|
||||
if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
|
||||
, executable ? false
|
||||
, unpack ? false
|
||||
, name ? baseNameOf (toString url)
|
||||
|
||||
@@ -45,12 +45,13 @@ Comments start with a <literal>#</literal> character. Here is an
|
||||
example configuration file:</para>
|
||||
|
||||
<programlisting>
|
||||
keep-outputs = true # Nice for developers
|
||||
keep-derivations = true # Idem
|
||||
gc-keep-outputs = true # Nice for developers
|
||||
gc-keep-derivations = true # Idem
|
||||
env-keep-derivations = false
|
||||
</programlisting>
|
||||
|
||||
<para>You can override settings on the command line using the
|
||||
<option>--option</option> flag, e.g. <literal>--option keep-outputs
|
||||
<option>--option</option> flag, e.g. <literal>--option gc-keep-outputs
|
||||
false</literal>.</para>
|
||||
|
||||
<para>The following settings are currently available:
|
||||
@@ -58,7 +59,7 @@ false</literal>.</para>
|
||||
<variablelist>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-keep-outputs"><term><literal>keep-outputs</literal></term>
|
||||
<varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term>
|
||||
|
||||
<listitem><para>If <literal>true</literal>, the garbage collector
|
||||
will keep the outputs of non-garbage derivations. If
|
||||
@@ -75,7 +76,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-keep-derivations"><term><literal>keep-derivations</literal></term>
|
||||
<varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term>
|
||||
|
||||
<listitem><para>If <literal>true</literal> (default), the garbage
|
||||
collector will keep the derivations from which non-garbage store
|
||||
@@ -87,12 +88,12 @@ false</literal>.</para>
|
||||
traceability (e.g., it allows you to ask with what dependencies or
|
||||
options a store path was built), so by default this option is on.
|
||||
Turn it off to save a bit of disk space (or a lot if
|
||||
<literal>keep-outputs</literal> is also turned on).</para></listitem>
|
||||
<literal>gc-keep-outputs</literal> is also turned on).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>keep-env-derivations</literal></term>
|
||||
<varlistentry><term><literal>env-keep-derivations</literal></term>
|
||||
|
||||
<listitem><para>If <literal>false</literal> (default), derivations
|
||||
are not stored in Nix user environments. That is, the derivation
|
||||
@@ -104,19 +105,19 @@ false</literal>.</para>
|
||||
garbage-collected until the user environment generation is deleted
|
||||
(<command>nix-env --delete-generations</command>). To prevent
|
||||
build-time-only dependencies from being collected, you should also
|
||||
turn on <literal>keep-outputs</literal>.</para>
|
||||
turn on <literal>gc-keep-outputs</literal>.</para>
|
||||
|
||||
<para>The difference between this option and
|
||||
<literal>keep-derivations</literal> is that this one is
|
||||
<literal>gc-keep-derivations</literal> is that this one is
|
||||
“sticky”: it applies to any user environment created while this
|
||||
option was enabled, while <literal>keep-derivations</literal>
|
||||
option was enabled, while <literal>gc-keep-derivations</literal>
|
||||
only applies at the moment the garbage collector is
|
||||
run.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-max-jobs"><term><literal>max-jobs</literal></term>
|
||||
<varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term>
|
||||
|
||||
<listitem><para>This option defines the maximum number of jobs
|
||||
that Nix will try to build in parallel. The default is
|
||||
@@ -129,7 +130,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-cores"><term><literal>cores</literal></term>
|
||||
<varlistentry xml:id="conf-build-cores"><term><literal>build-cores</literal></term>
|
||||
|
||||
<listitem><para>Sets the value of the
|
||||
<envar>NIX_BUILD_CORES</envar> environment variable in the
|
||||
@@ -148,7 +149,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-max-silent-time"><term><literal>max-silent-time</literal></term>
|
||||
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
@@ -169,7 +170,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-timeout"><term><literal>timeout</literal></term>
|
||||
<varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
@@ -189,7 +190,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-max-build-log-size"><term><literal>max-build-log-size</literal></term>
|
||||
<varlistentry xml:id="conf-build-max-log-size"><term><literal>build-max-log-size</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
@@ -244,7 +245,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>sandbox</literal></term>
|
||||
<varlistentry><term><literal>build-use-sandbox</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, builds will be
|
||||
performed in a <emphasis>sandboxed environment</emphasis>, i.e.,
|
||||
@@ -253,7 +254,7 @@ false</literal>.</para>
|
||||
directory, private versions of <filename>/proc</filename>,
|
||||
<filename>/dev</filename>, <filename>/dev/shm</filename> and
|
||||
<filename>/dev/pts</filename> (on Linux), and the paths configured with the
|
||||
<link linkend='conf-sandbox-paths'><literal>sandbox-paths</literal>
|
||||
<link linkend='conf-build-sandbox-paths'><literal>build-sandbox-paths</literal>
|
||||
option</link>. This is useful to prevent undeclared dependencies
|
||||
on files in directories such as <filename>/usr/bin</filename>. In
|
||||
addition, on Linux, builds run in private PID, mount, network, IPC
|
||||
@@ -261,7 +262,7 @@ false</literal>.</para>
|
||||
system (except that fixed-output derivations do not run in private
|
||||
network namespace to ensure they can access the network).</para>
|
||||
|
||||
<para>Currently, sandboxing only work on Linux and macOS. The use
|
||||
<para>Currently, sandboxing only work on Linux and Mac OS X. The use
|
||||
of a sandbox requires that Nix is run as root (so you should use
|
||||
the <link linkend='conf-build-users-group'>“build users”
|
||||
feature</link> to perform the actual builds under different users
|
||||
@@ -279,8 +280,8 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-sandbox-paths">
|
||||
<term><literal>sandbox-paths</literal></term>
|
||||
<varlistentry xml:id="conf-build-sandbox-paths">
|
||||
<term><literal>build-sandbox-paths</literal></term>
|
||||
|
||||
<listitem><para>A list of paths bind-mounted into Nix sandbox
|
||||
environments. You can use the syntax
|
||||
@@ -302,17 +303,17 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-extra-sandbox-paths">
|
||||
<varlistentry xml:id="conf-build-extra-sandbox-paths">
|
||||
<term><literal>build-extra-sandbox-paths</literal></term>
|
||||
|
||||
<listitem><para>A list of additional paths appended to
|
||||
<option>sandbox-paths</option>. Useful if you want to extend
|
||||
<option>build-sandbox-paths</option>. Useful if you want to extend
|
||||
its default value.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>substitute</literal></term>
|
||||
<varlistentry><term><literal>build-use-substitutes</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (default), Nix
|
||||
will use binary substitutes if available. This option can be
|
||||
@@ -321,21 +322,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>builders-use-substitutes</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will instruct
|
||||
remote build machines to use their own binary substitutes if available. In
|
||||
practical terms, this means that remote hosts will fetch as many build
|
||||
dependencies as possible from their own substitutes (e.g, from
|
||||
<literal>cache.nixos.org</literal>), instead of waiting for this host to
|
||||
upload them all. This can drastically reduce build times if the network
|
||||
connection between this computer and the remote build host is slow. Defaults
|
||||
to <literal>false</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>fallback</literal></term>
|
||||
<varlistentry><term><literal>build-fallback</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will fall
|
||||
back to building from source if a binary substitute fails. This
|
||||
@@ -345,7 +332,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>keep-build-log</literal></term>
|
||||
<varlistentry><term><literal>build-keep-log</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
Nix will write the build log of a derivation (i.e. the standard
|
||||
@@ -357,7 +344,7 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>compress-build-log</literal></term>
|
||||
<varlistentry><term><literal>build-compress-log</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
build logs written to <filename>/nix/var/log/nix/drvs</filename>
|
||||
@@ -416,43 +403,27 @@ false</literal>.</para>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>require-sigs</literal></term>
|
||||
<varlistentry><term><literal>signed-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
any non-content-addressed path added or copied to the Nix store
|
||||
(e.g. when substituting from a binary cache) must have a valid
|
||||
signature, that is, be signed using one of the keys listed in
|
||||
<option>trusted-public-keys</option> or
|
||||
<option>secret-key-files</option>. Set to <literal>false</literal>
|
||||
to disable signature checking.</para></listitem>
|
||||
<listitem><para>If set to <literal>*</literal> (the default), Nix
|
||||
will only download binaries if they are signed using one of the
|
||||
keys listed in <option>binary-cache-public-keys</option>. Set to
|
||||
the empty string to disable signature checking.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>trusted-public-keys</literal></term>
|
||||
<varlistentry><term><literal>binary-cache-public-keys</literal></term>
|
||||
|
||||
<listitem><para>A whitespace-separated list of public keys. When
|
||||
paths are copied from another Nix store (such as a binary cache),
|
||||
they must be signed with one of these keys. For example:
|
||||
<listitem><para>A whitespace-separated list of public keys
|
||||
corresponding to the secret keys trusted to sign binary
|
||||
caches. For example:
|
||||
<literal>cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
|
||||
hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>secret-key-files</literal></term>
|
||||
|
||||
<listitem><para>A whitespace-separated list of files containing
|
||||
secret (private) keys. These are used to sign locally-built
|
||||
paths. They can be generated using <command>nix-store
|
||||
--generate-binary-cache-key</command>. The corresponding public
|
||||
key can be distributed to other users, who can add it to
|
||||
<option>trusted-public-keys</option> in their
|
||||
<filename>nix.conf</filename>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>http-connections</literal></term>
|
||||
|
||||
<listitem><para>The maximum number of parallel TCP connections
|
||||
@@ -557,12 +528,11 @@ password <replaceable>my-password</replaceable>
|
||||
<literal>wheel</literal> group. The default is
|
||||
<literal>root</literal>.</para>
|
||||
|
||||
<warning><para>Adding a user to <option>trusted-users</option>
|
||||
is essentially equivalent to giving that user root access to the
|
||||
system. For example, the user can set
|
||||
<option>sandbox-paths</option> and thereby obtain read access to
|
||||
directories that are otherwise inacessible to
|
||||
them.</para></warning>
|
||||
<warning><para>The users listed here have the ability to
|
||||
compromise the security of a multi-user Nix store. For instance,
|
||||
they could install Trojan horses subsequently executed by other
|
||||
users. So you should consider carefully whether to add users to
|
||||
this list.</para></warning>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -594,8 +564,7 @@ password <replaceable>my-password</replaceable>
|
||||
<para>If set to <literal>true</literal>, the Nix evaluator will
|
||||
not allow access to any files outside of the Nix search path (as
|
||||
set via the <envar>NIX_PATH</envar> environment variable or the
|
||||
<option>-I</option> option), or to URIs outside of
|
||||
<option>allowed-uri</option>. The default is
|
||||
<option>-I</option> option). The default is
|
||||
<literal>false</literal>.</para>
|
||||
|
||||
</listitem>
|
||||
@@ -603,21 +572,6 @@ password <replaceable>my-password</replaceable>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-allowed-uris"><term><literal>allowed-uris</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>A list of URI prefixes to which access is allowed in
|
||||
restricted evaluation mode. For example, when set to
|
||||
<literal>https://github.com/NixOS</literal>, builtin functions
|
||||
such as <function>fetchGit</function> are allowed to access
|
||||
<literal>https://github.com/NixOS/patchelf.git</literal>.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-pre-build-hook"><term><literal>pre-build-hook</literal></term>
|
||||
|
||||
<listitem>
|
||||
@@ -643,7 +597,7 @@ password <replaceable>my-password</replaceable>
|
||||
<para>Pass a list of files and directories to be included in the
|
||||
sandbox for this build. One entry per line, terminated by an empty
|
||||
line. Entries have the same format as
|
||||
<literal>sandbox-paths</literal>.</para>
|
||||
<literal>build-sandbox-paths</literal>.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -654,7 +608,7 @@ password <replaceable>my-password</replaceable>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-repeat"><term><literal>repeat</literal></term>
|
||||
<varlistentry xml:id="conf-build-repeat"><term><literal>build-repeat</literal></term>
|
||||
|
||||
<listitem><para>How many times to repeat builds to check whether
|
||||
they are deterministic. The default value is 0. If the value is
|
||||
@@ -689,51 +643,6 @@ password <replaceable>my-password</replaceable>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-allow-new-privileges"><term><literal>allow-new-privileges</literal></term>
|
||||
|
||||
<listitem><para>(Linux-specific.) By default, builders on Linux
|
||||
cannot acquire new privileges by calling setuid/setgid programs or
|
||||
programs that have file capabilities. For example, programs such
|
||||
as <command>sudo</command> or <command>ping</command> will
|
||||
fail. (Note that in sandbox builds, no such programs are available
|
||||
unless you bind-mount them into the sandbox via the
|
||||
<option>sandbox-paths</option> option.) You can allow the
|
||||
use of such programs by enabling this option. This is impure and
|
||||
usually undesirable, but may be useful in certain scenarios
|
||||
(e.g. to spin up containers or set up userspace network interfaces
|
||||
in tests).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-hashed-mirrors"><term><literal>hashed-mirrors</literal></term>
|
||||
|
||||
<listitem><para>A list of web servers used by
|
||||
<function>builtins.fetchurl</function> to obtain files by
|
||||
hash. The default is
|
||||
<literal>http://tarballs.nixos.org/</literal>. Given a hash type
|
||||
<replaceable>ht</replaceable> and a base-16 hash
|
||||
<replaceable>h</replaceable>, Nix will try to download the file
|
||||
from
|
||||
<literal>hashed-mirror/<replaceable>ht</replaceable>/<replaceable>h</replaceable></literal>.
|
||||
This allows files to be downloaded even if they have disappeared
|
||||
from their original URI. For example, given the default mirror
|
||||
<literal>http://tarballs.nixos.org/</literal>, when building the derivation
|
||||
|
||||
<programlisting>
|
||||
builtins.fetchurl {
|
||||
url = https://example.org/foo-1.2.3.tar.xz;
|
||||
sha256 = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae";
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
Nix will attempt to download this file from
|
||||
<literal>http://tarballs.nixos.org/sha256/2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae</literal>
|
||||
first. If it is not available there, if will try the original URI.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
@@ -27,10 +27,8 @@
|
||||
<arg><option>- -show-progress</option></arg>
|
||||
-->
|
||||
<arg><option>--include-outputs</option></arg>
|
||||
<group>
|
||||
<arg choice='plain'><option>--use-substitutes</option></arg>
|
||||
<arg choice='plain'><option>-s</option></arg>
|
||||
</group>
|
||||
<arg><option>--use-substitutes</option></arg>
|
||||
<arg><option>-s</option></arg>
|
||||
<arg><option>-v</option></arg>
|
||||
<arg choice='plain'>
|
||||
<replaceable>user@</replaceable><replaceable>machine</replaceable>
|
||||
|
||||
@@ -146,7 +146,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--file</option> / <option>-f</option> <replaceable>path</replaceable></term>
|
||||
<varlistentry><term><option>--file</option></term>
|
||||
<term><option>-f</option></term>
|
||||
|
||||
<listitem><para>Specifies the Nix expression (designated below as
|
||||
the <emphasis>active Nix expression</emphasis>) used by the
|
||||
@@ -165,7 +166,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--profile</option> / <option>-p</option> <replaceable>path</replaceable></term>
|
||||
<varlistentry><term><option>--profile</option></term>
|
||||
<term><option>-p</option></term>
|
||||
|
||||
<listitem><para>Specifies the profile to be used by those
|
||||
operations that operate on a profile (designated below as the
|
||||
|
||||
@@ -44,9 +44,7 @@
|
||||
cryptographic hash of the contents of each
|
||||
<replaceable>path</replaceable> and prints it on standard output. By
|
||||
default, it computes an MD5 hash, but other hash algorithms are
|
||||
available as well. The hash is printed in hexadecimal. To generate
|
||||
the same hash as <command>nix-prefetch-url</command> you have to
|
||||
specify multiple arguments, see below for an example.</para>
|
||||
available as well. The hash is printed in hexadecimal.</para>
|
||||
|
||||
<para>The hash is computed over a <emphasis>serialisation</emphasis>
|
||||
of each path: a dump of the file system tree rooted at the path. This
|
||||
@@ -124,15 +122,6 @@ cryptographic hash as <literal>nix-store --dump
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>Computing the same hash as <command>nix-prefetch-url</command>:
|
||||
<screen>
|
||||
$ nix-prefetch-url file://<(echo test)
|
||||
1lkgqb6fclns49861dwk9rzb6xnfkxbpws74mxnx01z9qyv1pjpj
|
||||
$ nix-hash --type sha256 --flat --base32 <(echo test)
|
||||
1lkgqb6fclns49861dwk9rzb6xnfkxbpws74mxnx01z9qyv1pjpj
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>Computing hashes:
|
||||
|
||||
<screen>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<arg choice='plain'>
|
||||
<option>--eval</option>
|
||||
<arg><option>--strict</option></arg>
|
||||
<arg><option>--json</option></arg>
|
||||
<arg><option>--xml</option></arg>
|
||||
</arg>
|
||||
</group>
|
||||
@@ -39,13 +38,12 @@
|
||||
</arg>
|
||||
<arg><option>--add-root</option> <replaceable>path</replaceable></arg>
|
||||
<arg><option>--indirect</option></arg>
|
||||
<group>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--expr</option></arg>
|
||||
<arg choice='plain'><option>-E</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<sbr/>
|
||||
<command>nix-instantiate</command>
|
||||
<arg choice='plain'><option>--find-file</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>files</replaceable></arg>
|
||||
@@ -117,17 +115,13 @@ input.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--strict</option></term>
|
||||
<varlistentry><term><option>--xml</option></term>
|
||||
|
||||
<listitem><para>When used with <option>--eval</option>,
|
||||
recursively evaluate list elements and attributes. Normally, such
|
||||
sub-expressions are left unevaluated (since the Nix expression
|
||||
language is lazy).</para>
|
||||
|
||||
<warning><para>This option can cause non-termination, because lazy
|
||||
data structures can be infinitely large.</para></warning>
|
||||
|
||||
</listitem>
|
||||
<listitem><para>When used with <option>--eval</option>, print the resulting
|
||||
value as an XML representation of the abstract syntax tree rather than as
|
||||
an ATerm. The schema is the same as that used by the <link
|
||||
linkend="builtin-toXML"><function>toXML</function> built-in</link>.
|
||||
</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -139,13 +133,17 @@ input.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--xml</option></term>
|
||||
<varlistentry><term><option>--strict</option></term>
|
||||
|
||||
<listitem><para>When used with <option>--eval</option>, print the resulting
|
||||
value as an XML representation of the abstract syntax tree rather than as
|
||||
an ATerm. The schema is the same as that used by the <link
|
||||
linkend="builtin-toXML"><function>toXML</function> built-in</link>.
|
||||
</para></listitem>
|
||||
<listitem><para>When used with <option>--eval</option>,
|
||||
recursively evaluate list elements and attributes. Normally, such
|
||||
sub-expressions are left unevaluated (since the Nix expression
|
||||
language is lazy).</para>
|
||||
|
||||
<warning><para>This option can cause non-termination, because lazy
|
||||
data structures can be infinitely large.</para></warning>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -19,16 +19,14 @@
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-prefetch-url</command>
|
||||
<arg><option>--version</option></arg>
|
||||
<arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
|
||||
<arg><option>--print-path</option></arg>
|
||||
<arg><option>--unpack</option></arg>
|
||||
<arg><option>--name</option> <replaceable>name</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>url</replaceable></arg>
|
||||
<arg><replaceable>hash</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-prefetch-url</command> downloads the
|
||||
@@ -93,7 +91,7 @@ downloaded file in the Nix store is also printed.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--name</option> <replaceable>name</replaceable></term>
|
||||
<varlistentry><term><option>--name</option></term>
|
||||
|
||||
<listitem><para>Override the name of the file in the Nix store. By
|
||||
default, this is
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
|
||||
<arg><option>--pure</option></arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'>
|
||||
<group choice='req'>
|
||||
<group choice='plain'>
|
||||
<group>
|
||||
<arg choice='plain'><option>--packages</option></arg>
|
||||
<arg choice='plain'><option>-p</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>packages</replaceable></arg>
|
||||
</arg>
|
||||
<replaceable>packages</replaceable>
|
||||
</group>
|
||||
<arg><replaceable>path</replaceable></arg>
|
||||
</group>
|
||||
</cmdsynopsis>
|
||||
@@ -144,7 +144,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--packages</option> / <option>-p</option> <replaceable>packages</replaceable>…</term>
|
||||
<varlistentry><term><option>--packages</option> / <option>-p</option></term>
|
||||
|
||||
<listitem><para>Set up an environment in which the specified
|
||||
packages are present. The command line arguments are interpreted
|
||||
|
||||
@@ -234,7 +234,7 @@ linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
|
||||
<para>To test whether a previously-built derivation is deterministic:
|
||||
|
||||
<screen>
|
||||
$ nix-build '<nixpkgs>' -A hello --check -K
|
||||
$ nix-build -r '<nixpkgs>' -A hello --check -K
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
@@ -397,9 +397,9 @@ options control what gets deleted and in what order:
|
||||
</para>
|
||||
|
||||
<para>The behaviour of the collector is also influenced by the <link
|
||||
linkend="conf-keep-outputs"><literal>keep-outputs</literal></link>
|
||||
linkend="conf-gc-keep-outputs"><literal>gc-keep-outputs</literal></link>
|
||||
and <link
|
||||
linkend="conf-keep-derivations"><literal>keep-derivations</literal></link>
|
||||
linkend="conf-gc-keep-derivations"><literal>gc-keep-derivations</literal></link>
|
||||
variables in the Nix configuration file.</para>
|
||||
|
||||
<para>With <option>--delete</option>, the collector prints the total
|
||||
|
||||
@@ -2,18 +2,10 @@
|
||||
|
||||
<arg><option>--help</option></arg>
|
||||
<arg><option>--version</option></arg>
|
||||
<arg rep='repeat'>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--verbose</option></arg>
|
||||
<arg choice='plain'><option>-v</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
<arg>
|
||||
<group choice='plain'>
|
||||
<arg choice='plain'><option>--no-build-output</option></arg>
|
||||
<arg choice='plain'><option>-Q</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
<arg rep='repeat'><option>--verbose</option></arg>
|
||||
<arg rep='repeat'><option>-v</option></arg>
|
||||
<arg><option>--no-build-output</option></arg>
|
||||
<arg><option>-Q</option></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--max-jobs</option></arg>
|
||||
@@ -33,18 +25,10 @@
|
||||
<option>--timeout</option>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<group choice='plain'>
|
||||
<arg choice='plain'><option>--keep-going</option></arg>
|
||||
<arg choice='plain'><option>-k</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
<arg>
|
||||
<group choice='plain'>
|
||||
<arg choice='plain'><option>--keep-failed</option></arg>
|
||||
<arg choice='plain'><option>-K</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
<arg><option>--keep-going</option></arg>
|
||||
<arg><option>-k</option></arg>
|
||||
<arg><option>--keep-failed</option></arg>
|
||||
<arg><option>-K</option></arg>
|
||||
<arg><option>--fallback</option></arg>
|
||||
<arg><option>--readonly-mode</option></arg>
|
||||
<arg><option>--show-trace</option></arg>
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--verbose</option> / <option>-v</option></term>
|
||||
<varlistentry><term><option>--verbose</option></term>
|
||||
<term><option>-v</option></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
@@ -75,7 +76,8 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--no-build-output</option> / <option>-Q</option></term>
|
||||
<varlistentry><term><option>--no-build-output</option></term>
|
||||
<term><option>-Q</option></term>
|
||||
|
||||
<listitem><para>By default, output written by builders to standard
|
||||
output and standard error is echoed to the Nix command's standard
|
||||
@@ -87,14 +89,14 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option> / <option>-j</option>
|
||||
<replaceable>number</replaceable></term>
|
||||
<varlistentry xml:id="opt-max-jobs"><term><option>--max-jobs</option></term>
|
||||
<term><option>-j</option></term>
|
||||
|
||||
<listitem><para>Sets the maximum number of build jobs that Nix will
|
||||
perform in parallel to the specified number. Specify
|
||||
<literal>auto</literal> to use the number of CPUs in the system.
|
||||
The default is specified by the <link
|
||||
linkend='conf-max-jobs'><literal>max-jobs</literal></link>
|
||||
linkend='conf-build-max-jobs'><literal>build-max-jobs</literal></link>
|
||||
configuration setting, which itself defaults to
|
||||
<literal>1</literal>. A higher value is useful on SMP systems or to
|
||||
exploit I/O latency.</para></listitem>
|
||||
@@ -112,7 +114,7 @@
|
||||
<literal>true</literal>, the builder passes the
|
||||
<option>-j<replaceable>N</replaceable></option> flag to GNU Make.
|
||||
It defaults to the value of the <link
|
||||
linkend='conf-cores'><literal>cores</literal></link>
|
||||
linkend='conf-build-cores'><literal>build-cores</literal></link>
|
||||
configuration setting, if set, or <literal>1</literal> otherwise.
|
||||
The value <literal>0</literal> means that the builder should use all
|
||||
available CPU cores in the system.</para></listitem>
|
||||
@@ -125,7 +127,7 @@
|
||||
<listitem><para>Sets the maximum number of seconds that a builder
|
||||
can go without producing any data on standard output or standard
|
||||
error. The default is specified by the <link
|
||||
linkend='conf-max-silent-time'><literal>max-silent-time</literal></link>
|
||||
linkend='conf-build-max-silent-time'><literal>build-max-silent-time</literal></link>
|
||||
configuration setting. <literal>0</literal> means no
|
||||
time-out.</para></listitem>
|
||||
|
||||
@@ -135,13 +137,14 @@
|
||||
|
||||
<listitem><para>Sets the maximum number of seconds that a builder
|
||||
can run. The default is specified by the <link
|
||||
linkend='conf-timeout'><literal>timeout</literal></link>
|
||||
linkend='conf-build-timeout'><literal>build-timeout</literal></link>
|
||||
configuration setting. <literal>0</literal> means no
|
||||
timeout.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--keep-going</option> / <option>-k</option></term>
|
||||
<varlistentry><term><option>--keep-going</option></term>
|
||||
<term><option>-k</option></term>
|
||||
|
||||
<listitem><para>Keep going in case of failed builds, to the
|
||||
greatest extent possible. That is, if building an input of some
|
||||
@@ -153,7 +156,8 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--keep-failed</option> / <option>-K</option></term>
|
||||
<varlistentry><term><option>--keep-failed</option></term>
|
||||
<term><option>-K</option></term>
|
||||
|
||||
<listitem><para>Specifies that in case of a build failure, the
|
||||
temporary directory (usually in <filename>/tmp</filename>) in which
|
||||
|
||||
@@ -139,7 +139,7 @@ impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
|
||||
<programlisting>
|
||||
fetchurl {
|
||||
url = http://ftp.gnu.org/pub/gnu/hello/hello-2.1.1.tar.gz;
|
||||
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
|
||||
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
@@ -150,7 +150,7 @@ fetchurl {
|
||||
<programlisting>
|
||||
fetchurl {
|
||||
url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
|
||||
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
|
||||
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
|
||||
@@ -134,14 +134,6 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.concatStringsSep</function>
|
||||
<replaceable>separator</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Concatenate a list of strings with a separator
|
||||
between each element, e.g. <literal>concatStringsSep "/"
|
||||
["usr" "local" "bin"] == "usr/local/bin"</literal></para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
|
||||
@@ -218,6 +210,42 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.match</function>
|
||||
<replaceable>regex</replaceable> <replaceable>str</replaceable></term>
|
||||
|
||||
<listitem><para>Returns a list if the <link
|
||||
xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
|
||||
POSIX regular expression</link> <replaceable>regex</replaceable>
|
||||
matches <replaceable>str</replaceable> precisely, otherwise returns
|
||||
<literal>null</literal>. Each item in the list is a regex group.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "ab" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>null</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "abc" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "a(b)(c)" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "b" "c" ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO "
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "foo" ]</literal>.
|
||||
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.elem</function>
|
||||
<replaceable>x</replaceable> <replaceable>xs</replaceable></term>
|
||||
|
||||
@@ -272,13 +300,13 @@ stdenv.mkDerivation { … }
|
||||
|
||||
Note that when obtaining the hash with <varname>nix-prefetch-url
|
||||
</varname> the option <varname>--unpack</varname> is required.
|
||||
</para>
|
||||
|
||||
<para>This function can also verify the contents against a hash.
|
||||
In that case, the function takes a set instead of a URL. The set
|
||||
</para>
|
||||
|
||||
<para>This function can also verify the contents against a hash.
|
||||
In that case, the function takes a set instead of a URL. The set
|
||||
requires the attribute <varname>url</varname> and the attribute
|
||||
<varname>sha256</varname>, e.g.
|
||||
|
||||
|
||||
<programlisting>
|
||||
with import (fetchTarball {
|
||||
url = https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz;
|
||||
@@ -287,7 +315,7 @@ with import (fetchTarball {
|
||||
|
||||
stdenv.mkDerivation { … }
|
||||
</programlisting>
|
||||
|
||||
|
||||
</para>
|
||||
|
||||
<para>This function is not available if <link
|
||||
@@ -608,16 +636,6 @@ x: x + 456</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isFloat</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a float, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isBool</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -708,42 +726,6 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.match</function>
|
||||
<replaceable>regex</replaceable> <replaceable>str</replaceable></term>
|
||||
|
||||
<listitem><para>Returns a list if the <link
|
||||
xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
|
||||
POSIX regular expression</link> <replaceable>regex</replaceable>
|
||||
matches <replaceable>str</replaceable> precisely, otherwise returns
|
||||
<literal>null</literal>. Each item in the list is a regex group.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "ab" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>null</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "abc" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "a(b)(c)" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "b" "c" ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO "
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "foo" ]</literal>.
|
||||
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.mul</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -891,43 +873,6 @@ builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.split</function>
|
||||
<replaceable>regex</replaceable> <replaceable>str</replaceable></term>
|
||||
|
||||
<listitem><para>Returns a list composed of non matched strings interleaved
|
||||
with the lists of the <link
|
||||
xlink:href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04">extended
|
||||
POSIX regular expression</link> <replaceable>regex</replaceable> matches
|
||||
of <replaceable>str</replaceable>. Each item in the lists of matched
|
||||
sequences is a regex group.
|
||||
|
||||
<programlisting>
|
||||
builtins.split "(a)b" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "" [ "a" ] "c" ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.split "([ac])" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "" [ "a" ] "b" [ "c" ] "" ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.split "(a)|(c)" "abc"
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ "" [ "a" null ] "b" [ null "c" ] "" ]</literal>.
|
||||
|
||||
<programlisting>
|
||||
builtins.split "([[:upper:]]+)" " FOO "
|
||||
</programlisting>
|
||||
|
||||
Evaluates to <literal>[ " " [ "FOO" ] " " ]</literal>.
|
||||
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.stringLength</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -1028,8 +973,8 @@ stdenv.mkDerivation {
|
||||
";
|
||||
|
||||
src = fetchurl {
|
||||
url = http://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
|
||||
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
|
||||
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
|
||||
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
||||
};
|
||||
inherit perl;
|
||||
}</programlisting>
|
||||
@@ -1263,9 +1208,8 @@ stdenv.mkDerivation (rec {
|
||||
<replaceable>e</replaceable>, namely <literal>"int"</literal>,
|
||||
<literal>"bool"</literal>, <literal>"string"</literal>,
|
||||
<literal>"path"</literal>, <literal>"null"</literal>,
|
||||
<literal>"set"</literal>, <literal>"list"</literal>,
|
||||
<literal>"lambda"</literal> or
|
||||
<literal>"float"</literal>.</para></listitem>
|
||||
<literal>"set"</literal>, <literal>"list"</literal> or
|
||||
<literal>"lambda"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ stdenv.mkDerivation { <co xml:id='ex-hello-nix-co-2' />
|
||||
builder = ./builder.sh; <co xml:id='ex-hello-nix-co-4' />
|
||||
src = fetchurl { <co xml:id='ex-hello-nix-co-5' />
|
||||
url = ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz;
|
||||
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
|
||||
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
||||
};
|
||||
inherit perl; <co xml:id='ex-hello-nix-co-6' />
|
||||
}</programlisting>
|
||||
@@ -108,7 +108,7 @@ the single Nix expression in that directory
|
||||
<para>The builder has to know what the sources of the package
|
||||
are. Here, the attribute <varname>src</varname> is bound to the
|
||||
result of a call to the <command>fetchurl</command> function.
|
||||
Given a URL and a SHA-256 hash of the expected contents of the file
|
||||
Given a URL and an MD5 hash of the expected contents of the file
|
||||
at that URL, this function builds a derivation that downloads the
|
||||
file and checks its hash. So the sources are a dependency that
|
||||
like all other dependencies is built before Hello itself is
|
||||
@@ -145,4 +145,4 @@ perl = perl;</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
@@ -333,20 +333,7 @@ with (import ./definitions.nix); ...</programlisting>
|
||||
|
||||
makes all attributes defined in the file
|
||||
<filename>definitions.nix</filename> available as if they were defined
|
||||
locally in a <literal>let</literal>-expression.</para>
|
||||
|
||||
<para>The bindings introduced by <literal>with</literal> do not shadow bindings
|
||||
introduced by other means, e.g.
|
||||
|
||||
<programlisting>
|
||||
let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ...</programlisting>
|
||||
|
||||
establishes the same scope as
|
||||
|
||||
<programlisting>
|
||||
let a = 1; in let a = 2; in let a = 3; in let a = 4; in ...</programlisting>
|
||||
|
||||
</para>
|
||||
locally in a <literal>rec</literal>-expression.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
@@ -40,11 +40,6 @@ weakest binding).</para>
|
||||
<entry>Call function <replaceable>e1</replaceable> with
|
||||
argument <replaceable>e2</replaceable>.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>-</literal> <replaceable>e</replaceable></entry>
|
||||
<entry>none</entry>
|
||||
<entry>Arithmetic negation.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><replaceable>e</replaceable> <literal>?</literal>
|
||||
<replaceable>attrpath</replaceable></entry>
|
||||
@@ -60,24 +55,13 @@ weakest binding).</para>
|
||||
<entry>List concatenation.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<replaceable>e1</replaceable> <literal>*</literal> <replaceable>e2</replaceable>,
|
||||
<replaceable>e1</replaceable> <literal>/</literal> <replaceable>e2</replaceable>
|
||||
</entry>
|
||||
<entry><replaceable>e1</replaceable> <literal>+</literal> <replaceable>e2</replaceable></entry>
|
||||
<entry>left</entry>
|
||||
<entry>Arithmetic multiplication and division.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<replaceable>e1</replaceable> <literal>+</literal> <replaceable>e2</replaceable>,
|
||||
<replaceable>e1</replaceable> <literal>-</literal> <replaceable>e2</replaceable>
|
||||
</entry>
|
||||
<entry>left</entry>
|
||||
<entry>Arithmetic addition and subtraction. String or path concatenation (only by <literal>+</literal>).</entry>
|
||||
<entry>String or path concatenation.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>!</literal> <replaceable>e</replaceable></entry>
|
||||
<entry>none</entry>
|
||||
<entry>left</entry>
|
||||
<entry>Boolean negation.</entry>
|
||||
</row>
|
||||
<row>
|
||||
@@ -91,22 +75,16 @@ weakest binding).</para>
|
||||
attributes).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<replaceable>e1</replaceable> <literal><</literal> <replaceable>e2</replaceable>,
|
||||
<replaceable>e1</replaceable> <literal>></literal> <replaceable>e2</replaceable>,
|
||||
<replaceable>e1</replaceable> <literal><=</literal> <replaceable>e2</replaceable>,
|
||||
<replaceable>e1</replaceable> <literal>>=</literal> <replaceable>e2</replaceable>
|
||||
</entry>
|
||||
<entry><replaceable>e1</replaceable> <literal>==</literal>
|
||||
<replaceable>e2</replaceable></entry>
|
||||
<entry>none</entry>
|
||||
<entry>Arithmetic comparison.</entry>
|
||||
<entry>Equality.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<replaceable>e1</replaceable> <literal>==</literal> <replaceable>e2</replaceable>,
|
||||
<replaceable>e1</replaceable> <literal>!=</literal> <replaceable>e2</replaceable>
|
||||
</entry>
|
||||
<entry><replaceable>e1</replaceable> <literal>!=</literal>
|
||||
<replaceable>e2</replaceable></entry>
|
||||
<entry>none</entry>
|
||||
<entry>Equality and inequality.</entry>
|
||||
<entry>Inequality.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><replaceable>e1</replaceable> <literal>&&</literal>
|
||||
|
||||
@@ -98,17 +98,13 @@ configureFlags = "
|
||||
|
||||
<para>Since <literal>${</literal> and <literal>''</literal> have
|
||||
special meaning in indented strings, you need a way to quote them.
|
||||
<literal>$</literal> can be escaped by prefixing it with
|
||||
<literal>${</literal> can be escaped by prefixing it with
|
||||
<literal>''</literal> (that is, two single quotes), i.e.,
|
||||
<literal>''$</literal>. <literal>''</literal> can be escaped by
|
||||
<literal>''${</literal>. <literal>''</literal> can be escaped by
|
||||
prefixing it with <literal>'</literal>, i.e.,
|
||||
<literal>'''</literal>. <literal>$</literal> removes any special meaning
|
||||
from the following <literal>$</literal>. Linefeed, carriage-return and tab
|
||||
characters can be written as <literal>''\n</literal>,
|
||||
<literal>''\r</literal>, <literal>''\t</literal>, and <literal>''\</literal>
|
||||
escapes any other character.
|
||||
|
||||
</para>
|
||||
<literal>'''</literal>. Finally, linefeed, carriage-return and
|
||||
tab characters can be written as <literal>''\n</literal>,
|
||||
<literal>''\r</literal>, <literal>''\t</literal>.</para>
|
||||
|
||||
<para>Indented strings are primarily useful in that they allow
|
||||
multi-line string literals to follow the indentation of the
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
<glossdef><para>A substitute is a command invocation stored in the
|
||||
Nix database that describes how to build a store object, bypassing
|
||||
the normal build mechanism (i.e., derivations). Typically, the
|
||||
normal the build mechanism (i.e., derivations). Typically, the
|
||||
substitute builds the store object by downloading a pre-built
|
||||
version of the store object from some server.</para></glossdef>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<title>Installing a Binary Distribution</title>
|
||||
|
||||
<para>If you are using Linux or macOS, the easiest way to install
|
||||
<para>If you are using Linux or Mac OS X, the easiest way to install
|
||||
Nix is to run the following command:
|
||||
|
||||
<screen>
|
||||
@@ -39,7 +39,7 @@ behaviour.
|
||||
<!--
|
||||
<para>You can also manually download and install a binary package.
|
||||
Binary packages of the latest stable release are available for Fedora,
|
||||
Debian, Ubuntu, macOS and various other systems from the <link
|
||||
Debian, Ubuntu, Mac OS X and various other systems from the <link
|
||||
xlink:href="http://nixos.org/nix/download.html">Nix homepage</link>.
|
||||
You can also get builds of the latest development release from our
|
||||
<link
|
||||
|
||||
@@ -52,7 +52,7 @@ 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
|
||||
<para>On Mac OS X, you can create the required group and users by
|
||||
running the following script:
|
||||
|
||||
<programlisting>
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
|
||||
<listitem><para>GNU Make.</para></listitem>
|
||||
|
||||
<listitem><para>A version of GCC or Clang that supports C++14.</para></listitem>
|
||||
<listitem><para>A version of GCC or Clang that supports C++11.</para></listitem>
|
||||
|
||||
<listitem><para><command>pkg-config</command> to locate
|
||||
dependencies. If your distribution does not provide it, you can get
|
||||
it from <link
|
||||
xlink:href="http://www.freedesktop.org/wiki/Software/pkg-config"
|
||||
/>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The OpenSSL library to calculate cryptographic hashes.
|
||||
If your distribution does not provide it, you can get it from <link
|
||||
xlink:href="https://www.openssl.org"/>.</para></listitem>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<listitem><para>Linux (i686, x86_64).</para></listitem>
|
||||
|
||||
<listitem><para>macOS (x86_64).</para></listitem>
|
||||
<listitem><para>Mac OS X (x86_64).</para></listitem>
|
||||
|
||||
<!--
|
||||
<listitem><para>FreeBSD (only tested on Intel).</para></listitem>
|
||||
|
||||
@@ -236,7 +236,7 @@ href="[%root%]hydra">a continuous build system</a>.</para>
|
||||
|
||||
<simplesect><title>Portability</title>
|
||||
|
||||
<para>Nix runs on Linux and macOS.</para>
|
||||
<para>Nix runs on Linux and Mac OS X.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
@@ -262,7 +262,7 @@ xlink:href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">GNU
|
||||
LGPLv2.1 or (at your option) any later version</link>.</para>
|
||||
|
||||
<para>Nix uses the <link
|
||||
xlink:href="https://github.com/arangodb/linenoise-ng">linenoise-ng
|
||||
xlink:href="https://github.com/antirez/linenoise">linenoise
|
||||
library</link>, which has the following license:</para>
|
||||
|
||||
<programlisting><xi:include href="../../../src/linenoise/LICENSE" parse="text" /></programlisting>
|
||||
|
||||
@@ -10,97 +10,6 @@
|
||||
|
||||
<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
|
||||
@@ -112,313 +21,9 @@
|
||||
<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>
|
||||
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>
|
||||
|
||||
@@ -40,7 +40,7 @@ $ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
|
||||
<para>Binary caches are created using <command>nix-push</command>.
|
||||
For details on the operation and format of binary caches, see the
|
||||
<command>nix-push</command> manpage. More details are provided in
|
||||
<link xlink:href="https://nixos.org/nix-dev/2012-September/009826.html">this
|
||||
<link xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2012-September/009826.html">this
|
||||
nix-dev posting</link>.</para>
|
||||
</listitem>
|
||||
|
||||
|
||||
3
local.mk
3
local.mk
@@ -1,5 +1,6 @@
|
||||
ifeq ($(MAKECMDGOALS), dist)
|
||||
dist-files += $(shell cat .dist-files)
|
||||
# Make sure we are in repo root with `--git-dir`
|
||||
dist-files += $(shell git --git-dir=.git ls-files || find * -type f)
|
||||
endif
|
||||
|
||||
dist-files += configure config.h.in nix.spec perl/configure
|
||||
|
||||
@@ -83,7 +83,6 @@ downloadFile("tarball", "3"); # .tar.bz2
|
||||
my ($tarball, $tarballHash) = downloadFile("tarball", "4"); # .tar.xz
|
||||
my ($tarball_i686_linux, $tarball_i686_linux_hash) = downloadFile("binaryTarball.i686-linux", "1");
|
||||
my ($tarball_x86_64_linux, $tarball_x86_64_linux_hash) = downloadFile("binaryTarball.x86_64-linux", "1");
|
||||
my ($tarball_aarch64_linux, $tarball_aarch64_linux_hash) = downloadFile("binaryTarball.aarch64-linux", "1");
|
||||
my ($tarball_x86_64_darwin, $tarball_x86_64_darwin_hash) = downloadFile("binaryTarball.x86_64-darwin", "1");
|
||||
|
||||
# Update Nixpkgs in a very hacky way.
|
||||
@@ -112,7 +111,6 @@ write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix",
|
||||
"{\n" .
|
||||
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
|
||||
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
|
||||
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
|
||||
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
|
||||
"}\n");
|
||||
|
||||
@@ -146,7 +144,6 @@ write_file("$siteDir/nix-release.tt",
|
||||
"latestNixVersion = \"$version\"\n" .
|
||||
"nix_hash_i686_linux = \"$tarball_i686_linux_hash\"\n" .
|
||||
"nix_hash_x86_64_linux = \"$tarball_x86_64_linux_hash\"\n" .
|
||||
"nix_hash_aarch64_linux = \"$tarball_aarch64_linux_hash\"\n" .
|
||||
"nix_hash_x86_64_darwin = \"$tarball_x86_64_darwin_hash\"\n" .
|
||||
"-%]\n");
|
||||
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
FROM alpine
|
||||
|
||||
# Enable HTTPS support in wget.
|
||||
RUN apk add --update openssl
|
||||
|
||||
# Download Nix and install it into the system.
|
||||
RUN wget -O- https://nixos.org/releases/nix/nix-1.11.14/nix-1.11.14-x86_64-linux.tar.bz2 | bzcat - | tar xf - \
|
||||
&& addgroup -g 30000 -S nixbld \
|
||||
&& for i in $(seq 1 30); do adduser -S -D -h /var/empty -g "Nix build user $i" -u $((30000 + i)) -G nixbld nixbld$i ; done \
|
||||
&& mkdir -m 0755 /nix && USER=root sh nix-*-x86_64-linux/install \
|
||||
&& ln -s /nix/var/nix/profiles/default/etc/profile.d/nix.sh /etc/profile.d/ \
|
||||
&& rm -r /nix-*-x86_64-linux \
|
||||
&& rm -r /var/cache/apk/*
|
||||
RUN wget -O- http://nixos.org/releases/nix/nix-1.11.7/nix-1.11.7-x86_64-linux.tar.bz2 | bzcat - | tar xf - \
|
||||
&& echo "nixbld:x:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10,nixbld11,nixbld12,nixbld13,nixbld14,nixbld15,nixbld16,nixbld17,nixbld18,nixbld19,nixbld20,nixbld21,nixbld22,nixbld23,nixbld24,nixbld25,nixbld26,nixbld27,nixbld28,nixbld29,nixbld30" >> /etc/group \
|
||||
&& for i in $(seq 1 30); do echo "nixbld$i:x:$((30000 + $i)):30000:::" >> /etc/passwd; done \
|
||||
&& mkdir -m 0755 /nix && USER=root sh nix-*-x86_64-linux/install \
|
||||
&& echo ". /root/.nix-profile/etc/profile.d/nix.sh" >> /etc/profile \
|
||||
&& rm -r /nix-*-x86_64-linux \
|
||||
&& apk --update add bash tar \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
ONBUILD ENV \
|
||||
ENV=/etc/profile \
|
||||
PATH=/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
|
||||
GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt \
|
||||
NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt
|
||||
PATH=/root/.nix-profile/bin:/root/.nix-profile/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
|
||||
GIT_SSL_CAINFO=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt \
|
||||
NIX_SSL_CERT_FILE=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt
|
||||
|
||||
ENV \
|
||||
ENV=/etc/profile \
|
||||
PATH=/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
|
||||
GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt \
|
||||
NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt \
|
||||
NIX_PATH=/nix/var/nix/profiles/per-user/root/channels
|
||||
PATH=/root/.nix-profile/bin:/root/.nix-profile/sbin:/bin:/sbin:/usr/bin:/usr/sbin \
|
||||
GIT_SSL_CAINFO=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt \
|
||||
NIX_SSL_CERT_FILE=/root/.nix-profile/etc/ssl/certs/ca-bundle.crt \
|
||||
NIX_PATH=/nix/var/nix/profiles/per-user/root/channels/
|
||||
|
||||
10
misc/emacs/README
Normal file
10
misc/emacs/README
Normal file
@@ -0,0 +1,10 @@
|
||||
The Nix Emacs mode supports syntax highlighting, somewhat sensible
|
||||
indenting, and refilling of comments.
|
||||
|
||||
To enable Nix mode in Emacs, add something like this to your ~/.emacs
|
||||
file:
|
||||
|
||||
(load "/nix/share/emacs/site-lisp/nix-mode.el")
|
||||
|
||||
This automatically causes Nix mode to be activated for all files with
|
||||
extension `.nix'.
|
||||
1
misc/emacs/local.mk
Normal file
1
misc/emacs/local.mk
Normal file
@@ -0,0 +1 @@
|
||||
$(eval $(call install-data-in,$(d)/nix-mode.el,$(datadir)/emacs/site-lisp))
|
||||
177
misc/emacs/nix-mode.el
Normal file
177
misc/emacs/nix-mode.el
Normal file
@@ -0,0 +1,177 @@
|
||||
;;; nix-mode.el --- Major mode for editing Nix expressions
|
||||
|
||||
;; Author: Eelco Dolstra
|
||||
;; URL: https://github.com/NixOS/nix/tree/master/misc/emacs
|
||||
;; Version: 1.0
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defun nix-syntax-match-antiquote (limit)
|
||||
(let ((pos (next-single-char-property-change (point) 'nix-syntax-antiquote
|
||||
nil limit)))
|
||||
(when (and pos (> pos (point)))
|
||||
(goto-char pos)
|
||||
(let ((char (char-after pos)))
|
||||
(pcase char
|
||||
(`?$
|
||||
(forward-char 2))
|
||||
(`?}
|
||||
(forward-char 1)))
|
||||
(set-match-data (list pos (point)))
|
||||
t))))
|
||||
|
||||
(defconst nix-font-lock-keywords
|
||||
'("\\_<if\\_>" "\\_<then\\_>" "\\_<else\\_>" "\\_<assert\\_>" "\\_<with\\_>"
|
||||
"\\_<let\\_>" "\\_<in\\_>" "\\_<rec\\_>" "\\_<inherit\\_>" "\\_<or\\_>"
|
||||
("\\_<true\\_>" . font-lock-builtin-face)
|
||||
("\\_<false\\_>" . font-lock-builtin-face)
|
||||
("\\_<null\\_>" . font-lock-builtin-face)
|
||||
("\\_<import\\_>" . font-lock-builtin-face)
|
||||
("\\_<derivation\\_>" . font-lock-builtin-face)
|
||||
("\\_<baseNameOf\\_>" . font-lock-builtin-face)
|
||||
("\\_<toString\\_>" . font-lock-builtin-face)
|
||||
("\\_<isNull\\_>" . font-lock-builtin-face)
|
||||
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
|
||||
. font-lock-constant-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\-\.]*\\)[ \t]*="
|
||||
(1 font-lock-variable-name-face nil nil))
|
||||
("<[a-zA-Z0-9._\\+-]+\\(/[a-zA-Z0-9._\\+-]+\\)*>"
|
||||
. font-lock-constant-face)
|
||||
("[a-zA-Z0-9._\\+-]*\\(/[a-zA-Z0-9._\\+-]+\\)+"
|
||||
. font-lock-constant-face)
|
||||
(nix-syntax-match-antiquote 0 font-lock-preprocessor-face t))
|
||||
"Font lock keywords for nix.")
|
||||
|
||||
(defvar nix-mode-syntax-table
|
||||
(let ((table (make-syntax-table)))
|
||||
(modify-syntax-entry ?/ ". 14" table)
|
||||
(modify-syntax-entry ?* ". 23" table)
|
||||
(modify-syntax-entry ?# "< b" table)
|
||||
(modify-syntax-entry ?\n "> b" table)
|
||||
table)
|
||||
"Syntax table for Nix mode.")
|
||||
|
||||
(defun nix-syntax-propertize-escaped-antiquote ()
|
||||
"Set syntax properies for escaped antiquote marks."
|
||||
nil)
|
||||
|
||||
(defun nix-syntax-propertize-multiline-string ()
|
||||
"Set syntax properies for multiline string delimiters."
|
||||
(let* ((start (match-beginning 0))
|
||||
(end (match-end 0))
|
||||
(context (save-excursion (save-match-data (syntax-ppss start))))
|
||||
(string-type (nth 3 context)))
|
||||
(pcase string-type
|
||||
(`t
|
||||
;; inside a multiline string
|
||||
;; ending multi-line string delimiter
|
||||
(put-text-property (1- end) end
|
||||
'syntax-table (string-to-syntax "|")))
|
||||
(`nil
|
||||
;; beginning multi-line string delimiter
|
||||
(put-text-property start (1+ start)
|
||||
'syntax-table (string-to-syntax "|"))))))
|
||||
|
||||
(defun nix-syntax-propertize-antiquote ()
|
||||
"Set syntax properties for antiquote marks."
|
||||
(let* ((start (match-beginning 0)))
|
||||
(put-text-property start (1+ start)
|
||||
'syntax-table (string-to-syntax "|"))
|
||||
(put-text-property start (+ start 2)
|
||||
'nix-syntax-antiquote t)))
|
||||
|
||||
(defun nix-syntax-propertize-close-brace ()
|
||||
"Set syntax properties for close braces.
|
||||
If a close brace `}' ends an antiquote, the next character begins a string."
|
||||
(let* ((start (match-beginning 0))
|
||||
(end (match-end 0))
|
||||
(context (save-excursion (save-match-data (syntax-ppss start))))
|
||||
(open (nth 1 context)))
|
||||
(when open ;; a corresponding open-brace was found
|
||||
(let* ((antiquote (get-text-property open 'nix-syntax-antiquote)))
|
||||
(when antiquote
|
||||
(put-text-property (+ start 1) (+ start 2)
|
||||
'syntax-table (string-to-syntax "|"))
|
||||
(put-text-property start (1+ start)
|
||||
'nix-syntax-antiquote t))))))
|
||||
|
||||
(defun nix-syntax-propertize (start end)
|
||||
"Special syntax properties for Nix."
|
||||
;; search for multi-line string delimiters
|
||||
(goto-char start)
|
||||
(remove-text-properties start end '(syntax-table nil nix-syntax-antiquote nil))
|
||||
(funcall
|
||||
(syntax-propertize-rules
|
||||
("''\\${"
|
||||
(0 (ignore (nix-syntax-propertize-escaped-antiquote))))
|
||||
("''"
|
||||
(0 (ignore (nix-syntax-propertize-multiline-string))))
|
||||
("\\${"
|
||||
(0 (ignore (nix-syntax-propertize-antiquote))))
|
||||
("}"
|
||||
(0 (ignore (nix-syntax-propertize-close-brace)))))
|
||||
start end))
|
||||
|
||||
(defun nix-indent-line ()
|
||||
"Indent current line in a Nix expression."
|
||||
(interactive)
|
||||
(indent-relative-maybe))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode nix-mode prog-mode "Nix"
|
||||
"Major mode for editing Nix expressions.
|
||||
|
||||
The following commands may be useful:
|
||||
|
||||
'\\[newline-and-indent]'
|
||||
Insert a newline and move the cursor to align with the previous
|
||||
non-empty line.
|
||||
|
||||
'\\[fill-paragraph]'
|
||||
Refill a paragraph so that all lines are at most `fill-column'
|
||||
lines long. This should do the right thing for comments beginning
|
||||
with `#'. However, this command doesn't work properly yet if the
|
||||
comment is adjacent to code (i.e., no intervening empty lines).
|
||||
In that case, select the text to be refilled and use
|
||||
`\\[fill-region]' instead.
|
||||
|
||||
The hook `nix-mode-hook' is run when Nix mode is started.
|
||||
|
||||
\\{nix-mode-map}
|
||||
"
|
||||
(set-syntax-table nix-mode-syntax-table)
|
||||
|
||||
;; Font lock support.
|
||||
(setq-local font-lock-defaults '(nix-font-lock-keywords nil nil nil nil))
|
||||
|
||||
;; Special syntax properties for Nix
|
||||
(setq-local syntax-propertize-function 'nix-syntax-propertize)
|
||||
|
||||
;; Look at text properties when parsing
|
||||
(setq-local parse-sexp-lookup-properties t)
|
||||
|
||||
;; Automatic indentation [C-j].
|
||||
(set (make-local-variable 'indent-line-function) 'nix-indent-line)
|
||||
|
||||
;; Indenting of comments.
|
||||
(set (make-local-variable 'comment-start) "# ")
|
||||
(set (make-local-variable 'comment-end) "")
|
||||
(set (make-local-variable 'comment-start-skip) "\\(^\\|\\s-\\);?#+ *")
|
||||
|
||||
;; Filling of comments.
|
||||
(set (make-local-variable 'adaptive-fill-mode) t)
|
||||
(set (make-local-variable 'paragraph-start) "[ \t]*\\(#+[ \t]*\\)?$")
|
||||
(set (make-local-variable 'paragraph-separate) paragraph-start))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(progn
|
||||
(add-to-list 'auto-mode-alist '("\\.nix\\'" . nix-mode))
|
||||
(add-to-list 'auto-mode-alist '("\\.nix.in\\'" . nix-mode)))
|
||||
|
||||
(provide 'nix-mode)
|
||||
|
||||
;;; nix-mode.el ends here
|
||||
31
mk/tests.mk
31
mk/tests.mk
@@ -7,39 +7,20 @@ define run-install-test
|
||||
|
||||
endef
|
||||
|
||||
# Color code from https://unix.stackexchange.com/a/10065
|
||||
installcheck:
|
||||
@total=0; failed=0; \
|
||||
red=""; \
|
||||
green=""; \
|
||||
yellow=""; \
|
||||
normal=""; \
|
||||
if [ -t 1 ]; then \
|
||||
red="[31;1m"; \
|
||||
green="[32;1m"; \
|
||||
yellow="[33;1m"; \
|
||||
normal="[m"; \
|
||||
fi; \
|
||||
for i in $(_installcheck-list); do \
|
||||
@total=0; failed=0; for i in $(_installcheck-list); do \
|
||||
total=$$((total + 1)); \
|
||||
printf "running test $$i..."; \
|
||||
log="$$(cd $$(dirname $$i) && $(tests-environment) $$(basename $$i) 2>&1)"; \
|
||||
status=$$?; \
|
||||
if [ $$status -eq 0 ]; then \
|
||||
echo " [$${green}PASS$$normal]"; \
|
||||
elif [ $$status -eq 99 ]; then \
|
||||
echo " [$${yellow}SKIP$$normal]"; \
|
||||
echo "running test $$i"; \
|
||||
if (cd $$(dirname $$i) && $(tests-environment) $$(basename $$i)); then \
|
||||
echo "PASS: $$i"; \
|
||||
else \
|
||||
echo " [$${red}FAIL$$normal]"; \
|
||||
echo "$$log" | sed 's/^/ /'; \
|
||||
echo "FAIL: $$i"; \
|
||||
failed=$$((failed + 1)); \
|
||||
fi; \
|
||||
done; \
|
||||
if [ "$$failed" != 0 ]; then \
|
||||
echo "$${red}$$failed out of $$total tests failed $$normal"; \
|
||||
echo "$$failed out of $$total tests failed "; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "$${green}All tests succeeded"; \
|
||||
fi
|
||||
|
||||
.PHONY: check installcheck
|
||||
|
||||
40
nix.spec.in
40
nix.spec.in
@@ -60,6 +60,33 @@ Requires: %{name} = %{version}-%{release}
|
||||
%description doc
|
||||
The %{name}-doc package contains documentation files for %{name}.
|
||||
|
||||
|
||||
%package -n emacs-%{name}
|
||||
Summary: Nix mode for Emacs
|
||||
%if 0%{?rhel} && 0%{?rhel} < 7
|
||||
Group: Applications/Editors
|
||||
%endif
|
||||
BuildArch: noarch
|
||||
BuildRequires: emacs
|
||||
Requires: emacs(bin) >= %{_emacs_version}
|
||||
|
||||
%description -n emacs-%{name}
|
||||
This package provides a major mode for editing Nix expressions.
|
||||
|
||||
%package -n emacs-%{name}-el
|
||||
Summary: Elisp source files for emacs-%{name}
|
||||
%if 0%{?rhel} && 0%{?rhel} < 7
|
||||
Group: Applications/Editors
|
||||
%endif
|
||||
BuildArch: noarch
|
||||
Requires: emacs-%{name} = %{version}-%{release}
|
||||
|
||||
%description -n emacs-%{name}-el
|
||||
This package contains the elisp source file for the Nix major mode for
|
||||
GNU Emacs. You do not need to install this package to run Nix. Install
|
||||
the emacs-%{name} package to edit Nix expressions with GNU Emacs.
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
@@ -73,6 +100,7 @@ extraFlags=
|
||||
--docdir=%{_defaultdocdir}/%{name}-doc-%{version} \
|
||||
$extraFlags
|
||||
make -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
|
||||
%{_emacs_bytecompile} misc/emacs/nix-mode.el
|
||||
|
||||
|
||||
%install
|
||||
@@ -98,6 +126,9 @@ done
|
||||
# (until this is fixed in the relevant Makefile)
|
||||
chmod -x $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/nix.sh
|
||||
|
||||
# Copy the byte-compiled mode file by hand
|
||||
cp -p misc/emacs/nix-mode.elc $RPM_BUILD_ROOT%{_emacs_sitelispdir}/
|
||||
|
||||
# we ship this file in the base package
|
||||
rm -f $RPM_BUILD_ROOT%{_defaultdocdir}/%{name}-doc-%{version}/README
|
||||
|
||||
@@ -135,12 +166,12 @@ systemctl start nix-daemon.socket
|
||||
%{_prefix}/lib/systemd/system/nix-daemon.socket
|
||||
%{_prefix}/lib/systemd/system/nix-daemon.service
|
||||
%endif
|
||||
%{_datadir}/emacs/site-lisp/nix-mode.el
|
||||
%{_datadir}/nix
|
||||
%{_mandir}/man1/*.1*
|
||||
%{_mandir}/man5/*.5*
|
||||
%{_mandir}/man8/*.8*
|
||||
%config(noreplace) %{_sysconfdir}/profile.d/nix.sh
|
||||
%config(noreplace) %{_sysconfdir}/profile.d/nix-daemon.sh
|
||||
/nix
|
||||
|
||||
%files devel
|
||||
@@ -150,3 +181,10 @@ systemctl start nix-daemon.socket
|
||||
%files doc
|
||||
%docdir %{_defaultdocdir}/%{name}-doc-%{version}
|
||||
%{_defaultdocdir}/%{name}-doc-%{version}
|
||||
|
||||
%files -n emacs-%{name}
|
||||
%{_emacs_sitelispdir}/*.elc
|
||||
#{_emacs_sitestartdir}/*.el
|
||||
|
||||
%files -n emacs-%{name}-el
|
||||
%{_emacs_sitelispdir}/*.el
|
||||
|
||||
@@ -23,7 +23,7 @@ sub readConfig {
|
||||
my $config = "$confDir/nix.conf";
|
||||
return unless -f $config;
|
||||
|
||||
open CONFIG, "<$config" or die "cannot open '$config'";
|
||||
open CONFIG, "<$config" or die "cannot open ‘$config’";
|
||||
while (<CONFIG>) {
|
||||
/^\s*([\w\-\.]+)\s*=\s*(.*)$/ or next;
|
||||
$config{$1} = $2;
|
||||
|
||||
@@ -35,14 +35,14 @@ sub copyToOpen {
|
||||
my $missingSize = 0;
|
||||
$missingSize += (queryPathInfo($_, 1))[3] foreach @missing;
|
||||
|
||||
printf STDERR "copying %d missing paths (%.2f MiB) to '$sshHost'...\n",
|
||||
printf STDERR "copying %d missing paths (%.2f MiB) to ‘$sshHost’...\n",
|
||||
scalar(@missing), $missingSize / (1024**2);
|
||||
return if $dryRun;
|
||||
|
||||
# Send the "import paths" command.
|
||||
syswrite($to, pack("L<x4", 4)) or die;
|
||||
exportPaths(fileno($to), @missing);
|
||||
readInt($from) == 1 or die "remote machine '$sshHost' failed to import closure\n";
|
||||
readInt($from) == 1 or die "remote machine ‘$sshHost’ failed to import closure\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -60,10 +60,10 @@ sub readManifest_ {
|
||||
# Decompress the manifest if necessary.
|
||||
if ($manifest =~ /\.bz2$/) {
|
||||
open MANIFEST, "$Nix::Config::bzip2 -d < $manifest |"
|
||||
or die "cannot decompress '$manifest': $!";
|
||||
or die "cannot decompress ‘$manifest’: $!";
|
||||
} else {
|
||||
open MANIFEST, "<$manifest"
|
||||
or die "cannot open '$manifest': $!";
|
||||
or die "cannot open ‘$manifest’: $!";
|
||||
}
|
||||
|
||||
my $inside = 0;
|
||||
@@ -287,7 +287,7 @@ sub parseNARInfo {
|
||||
# FIXME: might be useful to support multiple signatures per .narinfo.
|
||||
|
||||
if (!defined $sig) {
|
||||
warn "NAR info file '$location' lacks a signature; ignoring\n";
|
||||
warn "NAR info file ‘$location’ lacks a signature; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
my ($keyName, $sig64) = split ":", $sig;
|
||||
@@ -295,7 +295,7 @@ sub parseNARInfo {
|
||||
|
||||
my $publicKey = $Nix::Config::binaryCachePublicKeys{$keyName};
|
||||
if (!defined $publicKey) {
|
||||
warn "NAR info file '$location' is signed by unknown key '$keyName'; ignoring\n";
|
||||
warn "NAR info file ‘$location’ is signed by unknown key ‘$keyName’; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
@@ -306,12 +306,12 @@ sub parseNARInfo {
|
||||
[ map { "$Nix::Config::storeDir/$_" } @refs ]);
|
||||
};
|
||||
if ($@) {
|
||||
warn "cannot compute fingerprint of '$location'; ignoring\n";
|
||||
warn "cannot compute fingerprint of ‘$location’; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
if (!checkSignature($publicKey, decode_base64($sig64), $fingerprint)) {
|
||||
warn "NAR info file '$location' has an incorrect signature; ignoring\n";
|
||||
warn "NAR info file ‘$location’ has an incorrect signature; ignoring\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ sub connectToRemoteNix {
|
||||
syswrite($to, pack("L<x4L<x4", $SERVE_MAGIC_1, $clientVersion)) or die;
|
||||
$magic = readInt($from);
|
||||
};
|
||||
die "unable to connect to '$sshHost'\n" if $@;
|
||||
die "unable to connect to ‘$sshHost’\n" if $@;
|
||||
die "did not get valid handshake from remote host\n" if $magic != 0x5452eecb;
|
||||
|
||||
my $serverVersion = readInt($from);
|
||||
|
||||
@@ -81,7 +81,8 @@ SV * queryReferences(char * path)
|
||||
SV * queryPathHash(char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
auto s = store()->queryPathInfo(path)->narHash.to_string();
|
||||
auto hash = store()->queryPathInfo(path)->narHash;
|
||||
string s = "sha256:" + printHash32(hash);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -107,7 +108,7 @@ SV * queryPathInfo(char * path, int base32)
|
||||
XPUSHs(&PL_sv_undef);
|
||||
else
|
||||
XPUSHs(sv_2mortal(newSVpv(info->deriver.c_str(), 0)));
|
||||
auto s = info->narHash.to_string(base32 ? Base32 : Base16);
|
||||
string s = "sha256:" + (base32 ? printHash32(info->narHash) : printHash(info->narHash));
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
mXPUSHi(info->registrationTime);
|
||||
mXPUSHi(info->narSize);
|
||||
@@ -183,7 +184,7 @@ void importPaths(int fd, int dontCheckSigs)
|
||||
PPCODE:
|
||||
try {
|
||||
FdSource source(fd);
|
||||
store()->importPaths(source, nullptr, dontCheckSigs ? NoCheckSigs : CheckSigs);
|
||||
store()->importPaths(source, 0, dontCheckSigs);
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
}
|
||||
@@ -193,7 +194,7 @@ SV * hashPath(char * algo, int base32, char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashPath(parseHashType(algo), path).first;
|
||||
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||
string s = base32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -204,7 +205,7 @@ SV * hashFile(char * algo, int base32, char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashFile(parseHashType(algo), path);
|
||||
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||
string s = base32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -215,7 +216,7 @@ SV * hashString(char * algo, int base32, char * s)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashString(parseHashType(algo), s);
|
||||
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||
string s = base32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -225,8 +226,8 @@ SV * hashString(char * algo, int base32, char * s)
|
||||
SV * convertHash(char * algo, char * s, int toBase32)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h(s, parseHashType(algo));
|
||||
string s = h.to_string(toBase32 ? Base32 : Base16, false);
|
||||
Hash h = parseHash16or32(parseHashType(algo), s);
|
||||
string s = toBase32 ? printHash32(h) : printHash(h);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@@ -285,7 +286,8 @@ SV * addToStore(char * srcPath, int recursive, char * algo)
|
||||
SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h(hash, parseHashType(algo));
|
||||
HashType ht = parseHashType(algo);
|
||||
Hash h = parseHash16or32(ht, hash);
|
||||
Path path = store()->makeFixedOutputPath(recursive, h, name);
|
||||
XPUSHs(sv_2mortal(newSVpv(path.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
|
||||
@@ -10,7 +10,7 @@ $urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!
|
||||
|
||||
sub checkURL {
|
||||
my ($url) = @_;
|
||||
die "invalid URL '$url'\n" unless $url =~ /^ $urlRE $ /x;
|
||||
die "invalid URL ‘$url’\n" unless $url =~ /^ $urlRE $ /x;
|
||||
}
|
||||
|
||||
sub uniq {
|
||||
@@ -26,7 +26,7 @@ sub uniq {
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die "cannot create file '$fn': $!";
|
||||
open TMP, ">$fn" or die "cannot create file ‘$fn’: $!";
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ sub writeFile {
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die "cannot open file '$fn': $!";
|
||||
open TMP, "<$fn" or die "cannot open file ‘$fn’: $!";
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
|
||||
@@ -7,8 +7,8 @@ rec {
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_TEST y
|
||||
CONFIG_ASH_BUILTIN_ECHO y
|
||||
CONFIG_ASH_BUILTIN_TEST y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
'';
|
||||
};
|
||||
|
||||
77
release.nix
77
release.nix
@@ -1,18 +1,20 @@
|
||||
{ nix ? builtins.fetchGit ./.
|
||||
, nixpkgs ? fetchTarball channel:nixos-17.09
|
||||
{ nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
|
||||
, nixpkgs ? { outPath = <nixpkgs>; revCount = 1234; shortRev = "abcdef"; }
|
||||
, officialRelease ? false
|
||||
, systems ? [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ]
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
pkgs = import nixpkgs {};
|
||||
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ];
|
||||
|
||||
|
||||
jobs = rec {
|
||||
|
||||
|
||||
tarball =
|
||||
with pkgs;
|
||||
tarball = pkgs.lib.genAttrs systems (system:
|
||||
with import nixpkgs { inherit system; };
|
||||
|
||||
releaseTools.sourceTarball {
|
||||
name = "nix-tarball";
|
||||
@@ -27,13 +29,16 @@ let
|
||||
pkgconfig sqlite libsodium boehmgc
|
||||
docbook5 docbook5_xsl
|
||||
autoconf-archive
|
||||
git
|
||||
] ++ lib.optional stdenv.isLinux libseccomp;
|
||||
|
||||
configureFlags = "--enable-gc";
|
||||
|
||||
postUnpack = ''
|
||||
(cd source && find . -type f) | cut -c3- > source/.dist-files
|
||||
cat source/.dist-files
|
||||
# Clean up when building from a working tree.
|
||||
if [[ -d $sourceRoot/.git ]]; then
|
||||
git -C $sourceRoot clean -fd
|
||||
fi
|
||||
'';
|
||||
|
||||
preConfigure = ''
|
||||
@@ -54,7 +59,8 @@ let
|
||||
make install docdir=$out/share/doc/nix makefiles=doc/manual/local.mk
|
||||
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
build = pkgs.lib.genAttrs systems (system:
|
||||
@@ -65,16 +71,13 @@ let
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix";
|
||||
src = tarball;
|
||||
src = tarball."x86_64-linux";
|
||||
|
||||
buildInputs =
|
||||
[ curl
|
||||
bzip2 xz brotli
|
||||
openssl pkgconfig sqlite boehmgc
|
||||
|
||||
# Tests
|
||||
git
|
||||
mercurial
|
||||
]
|
||||
++ lib.optional stdenv.isLinux libseccomp
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
@@ -106,7 +109,7 @@ let
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix-perl";
|
||||
src = tarball;
|
||||
src = tarball."x86_64-linux";
|
||||
|
||||
buildInputs =
|
||||
[ (builtins.getAttr system jobs.build) curl bzip2 xz pkgconfig pkgs.perl ]
|
||||
@@ -137,7 +140,7 @@ let
|
||||
|
||||
runCommand "nix-binary-tarball-${version}"
|
||||
{ exportReferencesGraph = [ "closure1" toplevel "closure2" cacert ];
|
||||
buildInputs = [ perl shellcheck ];
|
||||
buildInputs = [ perl ];
|
||||
meta.description = "Distribution-independent Nix bootstrap binaries for ${system}";
|
||||
}
|
||||
''
|
||||
@@ -146,15 +149,7 @@ let
|
||||
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
|
||||
--subst-var-by nix ${toplevel} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user \
|
||||
--subst-var-by nix ${toplevel} \
|
||||
--subst-var-by cacert ${cacert}
|
||||
|
||||
shellcheck -e SC1090 $TMPDIR/install
|
||||
shellcheck -e SC1091,SC2002 $TMPDIR/install-darwin-multi-user
|
||||
|
||||
chmod +x $TMPDIR/install
|
||||
chmod +x $TMPDIR/install-darwin-multi-user
|
||||
dir=nix-${version}-${system}
|
||||
fn=$out/$dir.tar.bz2
|
||||
mkdir -p $out/nix-support
|
||||
@@ -166,7 +161,7 @@ let
|
||||
--transform "s,$TMPDIR/install,$dir/install," \
|
||||
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
|
||||
--transform "s,$NIX_STORE,$dir/store,S" \
|
||||
$TMPDIR/install $TMPDIR/install-darwin-multi-user $TMPDIR/reginfo $storePaths
|
||||
$TMPDIR/install $TMPDIR/reginfo $storePaths
|
||||
'');
|
||||
|
||||
|
||||
@@ -175,12 +170,12 @@ let
|
||||
|
||||
releaseTools.coverageAnalysis {
|
||||
name = "nix-build";
|
||||
src = tarball;
|
||||
src = tarball."x86_64-linux";
|
||||
|
||||
buildInputs =
|
||||
[ curl bzip2 openssl pkgconfig sqlite xz libsodium libseccomp
|
||||
# These are for "make check" only:
|
||||
graphviz libxml2 libxslt git mercurial
|
||||
graphviz libxml2 libxslt
|
||||
];
|
||||
|
||||
configureFlags = ''
|
||||
@@ -191,7 +186,7 @@ let
|
||||
|
||||
doInstallCheck = true;
|
||||
|
||||
lcovFilter = [ "*/boost/*" "*-tab.*" "*/nlohmann/*" "*/linenoise/*" ];
|
||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||
|
||||
# We call `dot', and even though we just use it to
|
||||
# syntax-check generated dot files, it still requires some
|
||||
@@ -204,9 +199,11 @@ let
|
||||
rpm_fedora25x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora25x86_64) [ "libsodium-devel" ];
|
||||
|
||||
|
||||
#deb_debian8i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian8i386) [ "libsodium-dev" ] [ "libsodium13" ];
|
||||
#deb_debian8x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian8x86_64) [ "libsodium-dev" ] [ "libsodium13" ];
|
||||
deb_debian8i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian8i386) [ "libsodium-dev" ] [ "libsodium13" ];
|
||||
deb_debian8x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian8x86_64) [ "libsodium-dev" ] [ "libsodium13" ];
|
||||
|
||||
deb_ubuntu1410i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1410i386) [] [];
|
||||
deb_ubuntu1410x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1410x86_64) [] [];
|
||||
deb_ubuntu1604i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1604i386) [ "libsodium-dev" ] [ "libsodium18" ];
|
||||
deb_ubuntu1604x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1604x86_64) [ "libsodium-dev" ] [ "libsodium18" ];
|
||||
deb_ubuntu1610i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1610i386) [ "libsodium-dev" ] [ "libsodium18" ];
|
||||
@@ -215,18 +212,15 @@ let
|
||||
|
||||
# System tests.
|
||||
tests.remoteBuilds = (import ./tests/remote-builds.nix rec {
|
||||
inherit nixpkgs;
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
});
|
||||
|
||||
tests.nix-copy-closure = (import ./tests/nix-copy-closure.nix rec {
|
||||
inherit nixpkgs;
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -250,7 +244,7 @@ let
|
||||
''); # */
|
||||
|
||||
tests.evalNixpkgs =
|
||||
import (nixpkgs + "/pkgs/top-level/make-tarball.nix") {
|
||||
import <nixpkgs/pkgs/top-level/make-tarball.nix> {
|
||||
inherit nixpkgs;
|
||||
inherit pkgs;
|
||||
nix = build.x86_64-linux;
|
||||
@@ -271,18 +265,20 @@ let
|
||||
|
||||
# Aggregate job containing the release-critical jobs.
|
||||
release = pkgs.releaseTools.aggregate {
|
||||
name = "nix-${tarball.version}";
|
||||
name = "nix-${tarball."x86_64-linux".version}";
|
||||
meta.description = "Release-critical builds";
|
||||
constituents =
|
||||
[ tarball
|
||||
[ tarball.x86_64-linux
|
||||
tarball.x86_64-darwin
|
||||
tarball.i686-linux
|
||||
build.i686-linux
|
||||
build.x86_64-darwin
|
||||
build.x86_64-linux
|
||||
binaryTarball.i686-linux
|
||||
binaryTarball.x86_64-darwin
|
||||
binaryTarball.x86_64-linux
|
||||
#deb_debian8i386
|
||||
#deb_debian8x86_64
|
||||
deb_debian8i386
|
||||
deb_debian8x86_64
|
||||
deb_ubuntu1604i386
|
||||
deb_ubuntu1604x86_64
|
||||
rpm_fedora25i386
|
||||
@@ -308,13 +304,12 @@ let
|
||||
|
||||
releaseTools.rpmBuild rec {
|
||||
name = "nix-rpm";
|
||||
src = jobs.tarball;
|
||||
src = jobs.tarball."x86_64-linux";
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages =
|
||||
[ "sqlite" "sqlite-devel" "bzip2-devel" "libcurl-devel" "openssl-devel" "xz-devel" "libseccomp-devel" ]
|
||||
[ "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "libcurl-devel" "openssl-devel" "xz-devel" "libseccomp-devel" ]
|
||||
++ extraPackages; };
|
||||
# At most 2047MB can be simulated in qemu-system-i386
|
||||
memSize = 2047;
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = 50;
|
||||
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
|
||||
#enableParallelBuilding = true;
|
||||
@@ -331,7 +326,7 @@ let
|
||||
|
||||
releaseTools.debBuild {
|
||||
name = "nix-deb";
|
||||
src = jobs.tarball;
|
||||
src = jobs.tarball."x86_64-linux";
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages =
|
||||
[ "libsqlite3-dev" "libbz2-dev" "libcurl-dev" "libcurl3-nss" "libssl-dev" "liblzma-dev" "libseccomp-dev" ]
|
||||
|
||||
@@ -1,819 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# Sourced from:
|
||||
# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh
|
||||
# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0
|
||||
# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh
|
||||
# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh
|
||||
# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a
|
||||
#
|
||||
# however tracking which bits came from which would be impossible.
|
||||
|
||||
readonly ESC='\033[0m'
|
||||
readonly BOLD='\033[38;1m'
|
||||
readonly BLUE='\033[38;34m'
|
||||
readonly BLUE_UL='\033[38;4;34m'
|
||||
readonly GREEN='\033[38;32m'
|
||||
readonly GREEN_UL='\033[38;4;32m'
|
||||
readonly RED='\033[38;31m'
|
||||
readonly RED_UL='\033[38;4;31m'
|
||||
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_BUILD_GROUP_ID="30000"
|
||||
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
||||
readonly NIX_FIRST_BUILD_UID="30001"
|
||||
# Please don't change this. We don't support it, because the
|
||||
# default shell profile that comes with Nix doesn't support it.
|
||||
readonly NIX_ROOT="/nix"
|
||||
readonly PLIST_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
|
||||
readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/zshrc")
|
||||
readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
|
||||
readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
|
||||
|
||||
readonly NIX_INSTALLED_NIX="@nix@"
|
||||
readonly NIX_INSTALLED_CACERT="@cacert@"
|
||||
readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
|
||||
|
||||
readonly ROOT_HOME="/var/root"
|
||||
|
||||
if [ -t 0 ]; then
|
||||
readonly IS_HEADLESS='no'
|
||||
else
|
||||
readonly IS_HEADLESS='yes'
|
||||
fi
|
||||
|
||||
headless() {
|
||||
if [ "$IS_HEADLESS" = "yes" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
contactme() {
|
||||
echo "We'd love to help if you need it."
|
||||
echo ""
|
||||
echo "If you can, open an issue at https://github.com/nixos/nix/issues"
|
||||
echo ""
|
||||
echo "Or feel free to contact the team,"
|
||||
echo " - on IRC #nixos on irc.freenode.net"
|
||||
echo " - on twitter @nixos_org"
|
||||
}
|
||||
|
||||
uninstall_directions() {
|
||||
subheader "Uninstalling nix:"
|
||||
local step=0
|
||||
|
||||
if [ -e "$PLIST_DEST" ]; then
|
||||
step=$((step + 1))
|
||||
cat <<EOF
|
||||
$step. Delete $PLIST_DEST
|
||||
|
||||
sudo launchctl unload $PLIST_DEST
|
||||
sudo rm $PLIST_DEST
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||
if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
|
||||
step=$((step + 1))
|
||||
cat <<EOF
|
||||
$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
|
||||
|
||||
sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
|
||||
|
||||
(after this one, you may need to re-open any terminals that were
|
||||
opened while it existed.)
|
||||
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
|
||||
step=$((step + 1))
|
||||
cat <<EOF
|
||||
$step. Delete the files Nix added to your system:
|
||||
|
||||
sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
|
||||
|
||||
and that is it.
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
nix_user_for_core() {
|
||||
printf "nixbld%d" "$1"
|
||||
}
|
||||
|
||||
nix_uid_for_core() {
|
||||
echo $((NIX_FIRST_BUILD_UID + $1 - 1))
|
||||
}
|
||||
|
||||
dsclattr() {
|
||||
/usr/bin/dscl . -read "$1" \
|
||||
| awk "/$2/ { print \$2 }"
|
||||
}
|
||||
|
||||
_textout() {
|
||||
echo -en "$1"
|
||||
shift
|
||||
if [ "$*" = "" ]; then
|
||||
cat
|
||||
else
|
||||
echo "$@"
|
||||
fi
|
||||
echo -en "$ESC"
|
||||
}
|
||||
|
||||
header() {
|
||||
follow="---------------------------------------------------------"
|
||||
header=$(echo "---- $* $follow$follow$follow" | head -c 80)
|
||||
echo ""
|
||||
_textout "$BLUE" "$header"
|
||||
}
|
||||
|
||||
warningheader() {
|
||||
follow="---------------------------------------------------------"
|
||||
header=$(echo "---- $* $follow$follow$follow" | head -c 80)
|
||||
echo ""
|
||||
_textout "$RED" "$header"
|
||||
}
|
||||
|
||||
subheader() {
|
||||
echo ""
|
||||
_textout "$BLUE_UL" "$*"
|
||||
}
|
||||
|
||||
row() {
|
||||
printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2"
|
||||
}
|
||||
|
||||
task() {
|
||||
echo ""
|
||||
ok "~~> $1"
|
||||
}
|
||||
|
||||
bold() {
|
||||
echo "$BOLD$*$ESC"
|
||||
}
|
||||
|
||||
ok() {
|
||||
_textout "$GREEN" "$@"
|
||||
}
|
||||
|
||||
warning() {
|
||||
warningheader "warning!"
|
||||
cat
|
||||
echo ""
|
||||
}
|
||||
|
||||
failure() {
|
||||
header "oh no!"
|
||||
_textout "$RED" "$@"
|
||||
echo ""
|
||||
_textout "$RED" "$(contactme)"
|
||||
trap finish_cleanup EXIT
|
||||
exit 1
|
||||
}
|
||||
|
||||
ui_confirm() {
|
||||
_textout "$GREEN$GREEN_UL" "$1"
|
||||
|
||||
if headless; then
|
||||
echo "No TTY, assuming you would say yes :)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local prompt="[y/n] "
|
||||
echo -n "$prompt"
|
||||
while read -r y; do
|
||||
if [ "$y" = "y" ]; then
|
||||
echo ""
|
||||
return 0
|
||||
elif [ "$y" = "n" ]; then
|
||||
echo ""
|
||||
return 1
|
||||
else
|
||||
_textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n"
|
||||
echo -n "$prompt"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
__sudo() {
|
||||
local expl="$1"
|
||||
local cmd="$2"
|
||||
shift
|
||||
header "sudo execution"
|
||||
|
||||
echo "I am executing:"
|
||||
echo ""
|
||||
printf " $ sudo %s\\n" "$cmd"
|
||||
echo ""
|
||||
echo "$expl"
|
||||
echo ""
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_sudo() {
|
||||
local expl="$1"
|
||||
shift
|
||||
if ! headless; then
|
||||
__sudo "$expl" "$*"
|
||||
fi
|
||||
sudo "$@"
|
||||
}
|
||||
|
||||
|
||||
readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
|
||||
function finish_cleanup {
|
||||
rm -rf "$SCRATCH"
|
||||
}
|
||||
|
||||
function finish_fail {
|
||||
finish_cleanup
|
||||
|
||||
failure <<EOF
|
||||
Jeeze, something went wrong. If you can take all the output and open
|
||||
an issue, we'd love to fix the problem so nobody else has this issue.
|
||||
|
||||
:(
|
||||
EOF
|
||||
}
|
||||
trap finish_fail EXIT
|
||||
|
||||
function finish_success {
|
||||
finish_cleanup
|
||||
|
||||
ok "Alright! We're done!"
|
||||
cat <<EOF
|
||||
|
||||
Before Nix will work in your existing shells, you'll need to close
|
||||
them and open them again. Other than that, you should be ready to go.
|
||||
|
||||
Try it! Open a new terminal, and type:
|
||||
|
||||
$ nix-shell -p nix-info --run "nix-info -m"
|
||||
|
||||
Thank you for using this installer. If you have any feedback, don't
|
||||
hesitate:
|
||||
|
||||
$(contactme)
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
validate_starting_assumptions() {
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
failure "This script is for use with macOS!"
|
||||
fi
|
||||
|
||||
if [ $EUID -eq 0 ]; then
|
||||
failure <<EOF
|
||||
Please do not run this script with root privileges. We will call sudo
|
||||
when we need to.
|
||||
EOF
|
||||
fi
|
||||
|
||||
if type nix-env 2> /dev/null >&2; then
|
||||
failure <<EOF
|
||||
Nix already appears to be installed, and this tool assumes it is
|
||||
_not_ yet installed.
|
||||
|
||||
$(uninstall_directions)
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ "${NIX_REMOTE:-}" != "" ]; then
|
||||
failure <<EOF
|
||||
For some reason, \$NIX_REMOTE is set. It really should not be set
|
||||
before this installer runs, and it hints that Nix is currently
|
||||
installed. Please delete the old Nix installation and start again.
|
||||
|
||||
Note: You might need to close your shell window and open a new shell
|
||||
to clear the variable.
|
||||
EOF
|
||||
fi
|
||||
|
||||
if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then
|
||||
failure <<EOF
|
||||
It looks like \$SSL_CERT_FILE is set to a path that used to be part of
|
||||
the old Nix installation. Please unset that variable and try again:
|
||||
|
||||
$ unset SSL_CERT_FILE
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
|
||||
if [ -f "$file" ]; then
|
||||
if grep -l "^[^#].*.nix-profile" "$file"; then
|
||||
failure <<EOF
|
||||
I found a reference to a ".nix-profile" in $file.
|
||||
This has a high chance of breaking a new nix installation. It was most
|
||||
likely put there by a previous Nix installer.
|
||||
|
||||
Please remove this reference and try running this again. You should
|
||||
also look for similar references in:
|
||||
|
||||
- ~/.bash_profile
|
||||
- ~/.bash_login
|
||||
- ~/.profile
|
||||
|
||||
or other shell init files that you may have.
|
||||
|
||||
$(uninstall_directions)
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d /nix ]; then
|
||||
failure <<EOF
|
||||
There are some relics of a previous installation of Nix at /nix, and
|
||||
this scripts assumes Nix is _not_ yet installed. Please delete the old
|
||||
Nix installation and start again.
|
||||
|
||||
$(uninstall_directions)
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ -d /etc/nix ]; then
|
||||
failure <<EOF
|
||||
There are some relics of a previous installation of Nix at /etc/nix, and
|
||||
this scripts assumes Nix is _not_ yet installed. Please delete the old
|
||||
Nix installation and start again.
|
||||
|
||||
$(uninstall_directions)
|
||||
EOF
|
||||
fi
|
||||
|
||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||
if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
|
||||
failure <<EOF
|
||||
When this script runs, it backs up the current $profile_target to
|
||||
$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
|
||||
|
||||
Please follow these instructions to clean up the old backup file:
|
||||
|
||||
1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
|
||||
in case.
|
||||
|
||||
2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
|
||||
it has anything nix-related in it. If it does, something is probably
|
||||
quite wrong. Please open an issue or get in touch immediately.
|
||||
|
||||
3. Take care to make sure that $profile_target doesn't look like it has
|
||||
anything nix-related in it. If it does, and $profile_target _did not_,
|
||||
run:
|
||||
|
||||
$ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
|
||||
|
||||
and try again.
|
||||
EOF
|
||||
fi
|
||||
|
||||
if grep -qi "nix" "$profile_target"; then
|
||||
failure <<EOF
|
||||
It looks like $profile_target already has some Nix configuration in
|
||||
there. There should be no reason to run this again. If you're having
|
||||
trouble, please open an issue.
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
|
||||
danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile")
|
||||
for danger_path in "${danger_paths[@]}"; do
|
||||
if _sudo "making sure that $danger_path doesn't exist" \
|
||||
test -e "$danger_path"; then
|
||||
failure <<EOF
|
||||
I found a file at $danger_path, which is a relic of a previous
|
||||
installation. You must first delete this file before continuing.
|
||||
|
||||
$(uninstall_directions)
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
setup_report() {
|
||||
header "hardware report"
|
||||
row " Cores" "$CORES"
|
||||
|
||||
header "Nix config report"
|
||||
row " Temp Dir" "$SCRATCH"
|
||||
row " Nix Root" "$NIX_ROOT"
|
||||
row " Build Users" "$NIX_USER_COUNT"
|
||||
row " Build Group ID" "$NIX_BUILD_GROUP_ID"
|
||||
row "Build Group Name" "$NIX_BUILD_GROUP_NAME"
|
||||
if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then
|
||||
row "Preexisting Install" "Allowed"
|
||||
fi
|
||||
|
||||
subheader "build users:"
|
||||
|
||||
row " Username" "UID"
|
||||
for i in $(seq 1 "$NIX_USER_COUNT"); do
|
||||
row " $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")"
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
create_build_group() {
|
||||
local primary_group_id
|
||||
|
||||
task "Setting up the build group $NIX_BUILD_GROUP_NAME"
|
||||
if ! /usr/bin/dscl . -read "/Groups/$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1; then
|
||||
_sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
|
||||
/usr/sbin/dseditgroup -o create \
|
||||
-r "Nix build group for nix-daemon" \
|
||||
-i "$NIX_BUILD_GROUP_ID" \
|
||||
"$NIX_BUILD_GROUP_NAME" >&2
|
||||
row " Created" "Yes"
|
||||
else
|
||||
primary_group_id=$(dsclattr "/Groups/$NIX_BUILD_GROUP_NAME" "PrimaryGroupID")
|
||||
if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then
|
||||
failure <<EOF
|
||||
It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
|
||||
with the UID $primary_group_id. This script can't really handle
|
||||
that right now, so I'm going to give up.
|
||||
|
||||
You can fix this by editing this script and changing the
|
||||
NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
|
||||
to $primary_group_id and re-run.
|
||||
EOF
|
||||
else
|
||||
row " Exists" "Yes"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
create_build_user_for_core() {
|
||||
local coreid
|
||||
local username
|
||||
local uid
|
||||
|
||||
coreid="$1"
|
||||
username=$(nix_user_for_core "$coreid")
|
||||
uid=$(nix_uid_for_core "$coreid")
|
||||
dsclpath="/Users/$username"
|
||||
|
||||
task "Setting up the build user $username"
|
||||
|
||||
if ! /usr/bin/dscl . -read "$dsclpath" > /dev/null 2>&1; then
|
||||
_sudo "Creating the Nix build user, $username" \
|
||||
/usr/bin/dscl . create "$dsclpath" \
|
||||
UniqueID "${uid}"
|
||||
row " Created" "Yes"
|
||||
else
|
||||
actual_uid=$(dsclattr "$dsclpath" "UniqueID")
|
||||
if [ "$actual_uid" -ne "$uid" ]; then
|
||||
failure <<EOF
|
||||
It seems the build user $username already exists, but with the UID
|
||||
with the UID $actual_uid. This script can't really handle that right
|
||||
now, so I'm going to give up.
|
||||
|
||||
If you already created the users and you know they start from
|
||||
$actual_uid and go up from there, you can edit this script and change
|
||||
NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try
|
||||
again.
|
||||
EOF
|
||||
else
|
||||
row " Exists" "Yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$(dsclattr "$dsclpath" "IsHidden")" = "1" ]; then
|
||||
row " IsHidden" "Yes"
|
||||
else
|
||||
_sudo "in order to make $username a hidden user" \
|
||||
/usr/bin/dscl . -create "$dsclpath" "IsHidden" "1"
|
||||
row " IsHidden" "Yes"
|
||||
fi
|
||||
|
||||
if [ "$(dsclattr "$dsclpath" "NFSHomeDirectory")" = "/var/empty" ]; then
|
||||
row " NFSHomeDirectory" "/var/empty"
|
||||
else
|
||||
_sudo "in order to give $username a safe home directory" \
|
||||
/usr/bin/dscl . -create "$dsclpath" "NFSHomeDirectory" "/var/empty"
|
||||
row " NFSHomeDirectory" "/var/empty"
|
||||
fi
|
||||
|
||||
if [ "$(dsclattr "$dsclpath" "RealName")" = "Nix build user $coreid" ]; then
|
||||
row " RealName" "Nix build user $coreid"
|
||||
else
|
||||
_sudo "in order to give $username a useful name" \
|
||||
/usr/bin/dscl . -create "$dsclpath" "RealName" "Nix build user $coreid"
|
||||
row " RealName" "Nix build user $coreid"
|
||||
fi
|
||||
|
||||
if [ "$(dsclattr "$dsclpath" "UserShell")" = "/sbin/nologin" ]; then
|
||||
row " Logins Disabled" "Yes"
|
||||
else
|
||||
_sudo "in order to prevent $username from logging in" \
|
||||
/usr/bin/dscl . -create "$dsclpath" "UserShell" "/sbin/nologin"
|
||||
row " Logins Disabled" "Yes"
|
||||
fi
|
||||
|
||||
if dseditgroup -o checkmember -m "$username" "$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1 ; then
|
||||
row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
|
||||
else
|
||||
_sudo "Add $username to the $NIX_BUILD_GROUP_NAME group"\
|
||||
/usr/sbin/dseditgroup -o edit -t user \
|
||||
-a "$username" "$NIX_BUILD_GROUP_NAME"
|
||||
row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
|
||||
fi
|
||||
|
||||
if [ "$(dsclattr "$dsclpath" "PrimaryGroupID")" = "$NIX_BUILD_GROUP_ID" ]; then
|
||||
row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
|
||||
else
|
||||
_sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
|
||||
/usr/bin/dscl . -create "$dsclpath" "PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
|
||||
row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
create_build_users() {
|
||||
for i in $(seq 1 "$NIX_USER_COUNT"); do
|
||||
create_build_user_for_core "$i"
|
||||
done
|
||||
}
|
||||
|
||||
create_directories() {
|
||||
_sudo "to make the basic directory structure of Nix (part 1)" \
|
||||
mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool}
|
||||
|
||||
_sudo "to make the basic directory structure of Nix (part 2)" \
|
||||
mkdir -pv -m 1777 /nix/var/nix/{gcroots,profiles}/per-user
|
||||
|
||||
_sudo "to make the basic directory structure of Nix (part 3)" \
|
||||
mkdir -pv -m 1775 /nix/store
|
||||
|
||||
_sudo "to make the basic directory structure of Nix (part 4)" \
|
||||
chgrp "$NIX_BUILD_GROUP_NAME" /nix/store
|
||||
|
||||
_sudo "to set up the root user's profile (part 1)" \
|
||||
mkdir -pv -m 0755 /nix/var/nix/profiles/per-user/root
|
||||
|
||||
_sudo "to set up the root user's profile (part 2)" \
|
||||
mkdir -pv -m 0700 "$ROOT_HOME/.nix-defexpr"
|
||||
|
||||
_sudo "to place the default nix daemon configuration (part 1)" \
|
||||
mkdir -pv -m 0555 /etc/nix
|
||||
}
|
||||
|
||||
place_channel_configuration() {
|
||||
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
|
||||
_sudo "to set up the default system channel (part 1)" \
|
||||
install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
|
||||
}
|
||||
|
||||
welcome_to_nix() {
|
||||
ok "Welcome to the Multi-User Nix Installation"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
This installation tool will set up your computer with the Nix package
|
||||
manager. This will happen in a few stages:
|
||||
|
||||
1. Make sure your computer doesn't already have Nix. If it does, I
|
||||
will show you instructions on how to clean up your old one.
|
||||
|
||||
2. Show you what we are going to install and where. Then we will ask
|
||||
if you are ready to continue.
|
||||
|
||||
3. Create the system users and groups that the Nix daemon uses to run
|
||||
builds.
|
||||
|
||||
4. Perform the basic installation of the Nix files daemon.
|
||||
|
||||
5. Configure your shell to import special Nix Profile files, so you
|
||||
can use Nix.
|
||||
|
||||
6. Start the Nix daemon.
|
||||
|
||||
EOF
|
||||
|
||||
if ui_confirm "Would you like to see a more detailed list of what we will do?"; then
|
||||
cat <<EOF
|
||||
|
||||
We will:
|
||||
|
||||
- make sure your computer doesn't already have Nix files
|
||||
(if it does, I will tell you how to clean them up.)
|
||||
- create local users (see the list above for the users we'll make)
|
||||
- create a local group ($NIX_BUILD_GROUP_NAME)
|
||||
- install Nix in to $NIX_ROOT
|
||||
- create a configuration file in /etc/nix
|
||||
- set up the "default profile" by creating some Nix-related files in
|
||||
$ROOT_HOME
|
||||
EOF
|
||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||
if [ -e "$profile_target" ]; then
|
||||
cat <<EOF
|
||||
- back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX
|
||||
- update $profile_target to include some Nix configuration
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
cat <<EOF
|
||||
- load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon
|
||||
|
||||
EOF
|
||||
if ! ui_confirm "Ready to continue?"; then
|
||||
failure <<EOF
|
||||
Okay, maybe you would like to talk to the team.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
chat_about_sudo() {
|
||||
header "let's talk about sudo"
|
||||
|
||||
if headless; then
|
||||
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:
|
||||
|
||||
$ curl https://nixos.org/nix/install | sh
|
||||
|
||||
or maybe in a CI pipeline. Because of that, we're going to skip the
|
||||
verbose output in the interest of brevity.
|
||||
|
||||
If you would like to
|
||||
see the output, try like this:
|
||||
|
||||
$ curl -o install-nix https://nixos.org/nix/install
|
||||
$ sh ./install-nix
|
||||
|
||||
EOF
|
||||
return 0
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
This script is going to call sudo a lot. Every time we do, it'll
|
||||
output exactly what it'll do, and why.
|
||||
|
||||
Just like this:
|
||||
EOF
|
||||
|
||||
__sudo "to demonstrate how our sudo prompts look" \
|
||||
echo "this is a sudo prompt"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
This might look scary, but everything can be undone by running just a
|
||||
few commands. We used to ask you to confirm each time sudo ran, but it
|
||||
was too many times. Instead, I'll just ask you this one time:
|
||||
|
||||
EOF
|
||||
if ui_confirm "Can we use sudo?"; then
|
||||
ok "Yay! Thanks! Let's get going!"
|
||||
else
|
||||
failure <<EOF
|
||||
That is okay, but we can't install.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
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/"
|
||||
|
||||
if [ -d "$NIX_INSTALLED_NIX" ]; then
|
||||
echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"
|
||||
else
|
||||
failure <<EOF
|
||||
Something went wrong, and I didn't find Nix installed at
|
||||
$NIX_INSTALLED_NIX.
|
||||
EOF
|
||||
fi
|
||||
|
||||
_sudo "to initialize the Nix Database" \
|
||||
$NIX_INSTALLED_NIX/bin/nix-store --init
|
||||
|
||||
cat ./.reginfo \
|
||||
| _sudo "to load data for the first time in to the Nix Database" \
|
||||
"$NIX_INSTALLED_NIX/bin/nix-store" --load-db
|
||||
|
||||
echo " Just finished getting the nix database ready."
|
||||
)
|
||||
}
|
||||
|
||||
shell_source_lines() {
|
||||
cat <<EOF
|
||||
|
||||
# Nix
|
||||
if [ -e '$PROFILE_NIX_FILE' ]; then
|
||||
. '$PROFILE_NIX_FILE'
|
||||
fi
|
||||
# End Nix
|
||||
|
||||
EOF
|
||||
}
|
||||
configure_shell_profile() {
|
||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||
if [ -e "$profile_target" ]; then
|
||||
_sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
|
||||
cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX"
|
||||
|
||||
shell_source_lines \
|
||||
| _sudo "extend your $profile_target with nix-daemon settings" \
|
||||
tee -a "$profile_target"
|
||||
fi
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
setup_default_profile() {
|
||||
_sudo "to installing a bootstrapping Nix in to the default Profile" \
|
||||
HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX"
|
||||
|
||||
_sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \
|
||||
HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT"
|
||||
|
||||
_sudo "to update the default channel in the default profile" \
|
||||
HOME=$ROOT_HOME NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs
|
||||
}
|
||||
|
||||
|
||||
place_nix_configuration() {
|
||||
cat <<EOF > "$SCRATCH/nix.conf"
|
||||
build-users-group = $NIX_BUILD_GROUP_NAME
|
||||
|
||||
max-jobs = $NIX_USER_COUNT
|
||||
cores = 1
|
||||
sandbox = false
|
||||
EOF
|
||||
_sudo "to place the default nix daemon configuration (part 2)" \
|
||||
install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
|
||||
}
|
||||
|
||||
configure_nix_daemon_plist() {
|
||||
_sudo "to set up the nix-daemon as a LaunchDaemon" \
|
||||
ln -sfn "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"
|
||||
|
||||
_sudo "to load the LaunchDaemon plist for nix-daemon" \
|
||||
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
|
||||
_sudo "to start the nix-daemon" \
|
||||
launchctl start org.nixos.nix-daemon
|
||||
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
welcome_to_nix
|
||||
chat_about_sudo
|
||||
|
||||
if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then
|
||||
validate_starting_assumptions
|
||||
fi
|
||||
|
||||
setup_report
|
||||
|
||||
if ! ui_confirm "Ready to continue?"; then
|
||||
ok "Alright, no changes have been made :)"
|
||||
contactme
|
||||
trap finish_cleanup EXIT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
create_build_group
|
||||
create_build_users
|
||||
create_directories
|
||||
place_channel_configuration
|
||||
install_from_extracted_nix
|
||||
|
||||
configure_shell_profile
|
||||
|
||||
set +eu
|
||||
. /etc/profile
|
||||
set -eu
|
||||
|
||||
setup_default_profile
|
||||
place_nix_configuration
|
||||
configure_nix_daemon_plist
|
||||
|
||||
trap finish_success EXIT
|
||||
}
|
||||
|
||||
|
||||
main
|
||||
@@ -8,8 +8,15 @@ nix="@nix@"
|
||||
cacert="@cacert@"
|
||||
|
||||
|
||||
# macOS support for 10.10 or higher
|
||||
if [[ "$(uname -s)" = "Darwin" && $(($(sw_vers -productVersion | cut -d '.' -f 2))) -lt 10 ]]; then
|
||||
echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.10 or higher"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -e "$self/.reginfo" ]; then
|
||||
echo "$0: incomplete installer (.reginfo is missing)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$USER" ]; then
|
||||
@@ -17,23 +24,6 @@ if [ -z "$USER" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$HOME" ]; then
|
||||
echo "$0: \$HOME is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# macOS support for 10.10 or higher
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
if [ $(($(sw_vers -productVersion | cut -d '.' -f 2))) -lt 10 ]; then
|
||||
echo "$0: macOS $(sw_vers -productVersion) is not supported, upgrade to 10.10 or higher"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '\e[1;31mSwitching to the Multi-User Darwin Installer\e[0m\n'
|
||||
exec "$self/install-darwin-multi-user"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
printf '\e[1;31mwarning: installing Nix as root is not supported by this script!\e[0m\n'
|
||||
fi
|
||||
@@ -44,13 +34,13 @@ if ! [ -e $dest ]; then
|
||||
cmd="mkdir -m 0755 $dest && chown $USER $dest"
|
||||
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
|
||||
if ! sudo sh -c "$cmd"; then
|
||||
echo "$0: please manually run '$cmd' as root to create $dest" >&2
|
||||
echo "$0: please manually run ‘$cmd’ as root to create $dest" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [ -w $dest ]; then
|
||||
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see http://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
|
||||
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see http://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run ‘chown -R $USER $dest’ as root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -8,6 +8,5 @@ noinst-scripts += $(nix_noinst_scripts)
|
||||
profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
|
||||
$(eval $(call install-file-as, $(d)/nix-profile-daemon.sh, $(profiledir)/nix-daemon.sh, 0644))
|
||||
|
||||
clean-files += $(nix_noinst_scripts)
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# Only execute this file once per shell.
|
||||
if [ -n "$__ETC_PROFILE_NIX_SOURCED" ]; then return; fi
|
||||
__ETC_PROFILE_NIX_SOURCED=1
|
||||
|
||||
# Set up secure multi-user builds: non-root users build through the
|
||||
# Nix daemon.
|
||||
if [ "$USER" != root -o ! -w @localstatedir@/nix/db ]; then
|
||||
export NIX_REMOTE=daemon
|
||||
fi
|
||||
|
||||
export NIX_USER_PROFILE_DIR="@localstatedir@/nix/profiles/per-user/$USER"
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
|
||||
# Set up the per-user profile.
|
||||
mkdir -m 0755 -p $NIX_USER_PROFILE_DIR
|
||||
if ! test -O "$NIX_USER_PROFILE_DIR"; then
|
||||
echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
|
||||
fi
|
||||
|
||||
if test -w $HOME; then
|
||||
if ! test -L $HOME/.nix-profile; then
|
||||
if test "$USER" != root; then
|
||||
ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
|
||||
else
|
||||
# Root installs in the system-wide profile by default.
|
||||
ln -s @localstatedir@/nix/profiles/default $HOME/.nix-profile
|
||||
fi
|
||||
fi
|
||||
|
||||
# Subscribe the root user to the NixOS channel by default.
|
||||
if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
|
||||
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
|
||||
fi
|
||||
|
||||
# Create the per-user garbage collector roots directory.
|
||||
NIX_USER_GCROOTS_DIR=@localstatedir@/nix/gcroots/per-user/$USER
|
||||
mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
|
||||
if ! test -O "$NIX_USER_GCROOTS_DIR"; then
|
||||
echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
|
||||
fi
|
||||
|
||||
# Set up a default Nix expression from which to install stuff.
|
||||
if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
|
||||
rm -f $HOME/.nix-defexpr
|
||||
mkdir -p $HOME/.nix-defexpr
|
||||
if [ "$USER" != root ]; then
|
||||
ln -s @localstatedir@/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
export NIX_SSL_CERT_FILE="@localstatedir@/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
|
||||
export NIX_PATH="@localstatedir@/nix/profiles/per-user/root/channels"
|
||||
export PATH="$HOME/.nix-profile/bin:$HOME/.nix-profile/lib/kde4/libexec:@localstatedir@/nix/profiles/default/bin:@localstatedir@/nix/profiles/default:@localstatedir@/nix/profiles/default/lib/kde4/libexec:$PATH"
|
||||
@@ -60,6 +60,12 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
||||
NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_USER_PROFILE_DIR"
|
||||
|
||||
for i in $NIX_PROFILES; do
|
||||
if [ -d "$i/lib/aspell" ]; then
|
||||
export ASPELL_CONF="dict-dir $i/lib/aspell"
|
||||
fi
|
||||
done
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
@@ -22,10 +22,6 @@ with import ./release-common.nix { inherit pkgs; };
|
||||
# For nix-perl
|
||||
perl
|
||||
perlPackages.DBDSQLite
|
||||
|
||||
# Tests
|
||||
git
|
||||
mercurial
|
||||
]
|
||||
++ lib.optional stdenv.isLinux libseccomp;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "serialise.hh"
|
||||
#include "store-api.hh"
|
||||
#include "derivations.hh"
|
||||
#include "local-store.hh"
|
||||
|
||||
using namespace nix;
|
||||
using std::cin;
|
||||
@@ -42,33 +41,24 @@ int main (int argc, char * * argv)
|
||||
return handleExceptions(argv[0], [&]() {
|
||||
initNix();
|
||||
|
||||
logger = makeJSONLogger(*logger);
|
||||
|
||||
/* Ensure we don't get any SSH passphrase or host key popups. */
|
||||
unsetenv("DISPLAY");
|
||||
unsetenv("SSH_ASKPASS");
|
||||
|
||||
if (argc != 2)
|
||||
if (argc != 6)
|
||||
throw UsageError("called without required arguments");
|
||||
|
||||
verbosity = (Verbosity) std::stoll(argv[1]);
|
||||
auto store = openStore();
|
||||
|
||||
FdSource source(STDIN_FILENO);
|
||||
|
||||
/* Read the parent's settings. */
|
||||
while (readInt(source)) {
|
||||
auto name = readString(source);
|
||||
auto value = readString(source);
|
||||
settings.set(name, value);
|
||||
}
|
||||
|
||||
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
||||
|
||||
auto store = openStore().cast<LocalStore>();
|
||||
auto localSystem = argv[1];
|
||||
settings.maxSilentTime = std::stoll(argv[2]);
|
||||
settings.buildTimeout = std::stoll(argv[3]);
|
||||
verbosity = (Verbosity) std::stoll(argv[4]);
|
||||
settings.builders = argv[5];
|
||||
|
||||
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
|
||||
that gets cleared on reboot, but it wouldn't work on macOS. */
|
||||
currentLoad = store->stateDir + "/current-load";
|
||||
that gets cleared on reboot, but it wouldn't work on OS X. */
|
||||
currentLoad = settings.nixStateDir + "/current-load";
|
||||
|
||||
std::shared_ptr<Store> sshStore;
|
||||
AutoCloseFD bestSlotLock;
|
||||
@@ -83,20 +73,18 @@ int main (int argc, char * * argv)
|
||||
|
||||
string drvPath;
|
||||
string storeUri;
|
||||
|
||||
while (true) {
|
||||
|
||||
try {
|
||||
auto s = readString(source);
|
||||
if (s != "try") return;
|
||||
} catch (EndOfFile &) { return; }
|
||||
|
||||
auto amWilling = readInt(source);
|
||||
auto neededSystem = readString(source);
|
||||
source >> drvPath;
|
||||
auto requiredFeatures = readStrings<std::set<std::string>>(source);
|
||||
|
||||
auto canBuildLocally = amWilling && (neededSystem == settings.thisSystem);
|
||||
for (string line; getline(cin, line);) {
|
||||
auto tokens = tokenizeString<std::vector<string>>(line);
|
||||
auto sz = tokens.size();
|
||||
if (sz != 3 && sz != 4)
|
||||
throw Error("invalid build hook line ‘%1%’", line);
|
||||
auto amWilling = tokens[0] == "1";
|
||||
auto neededSystem = tokens[1];
|
||||
drvPath = tokens[2];
|
||||
auto requiredFeatures = sz == 3 ?
|
||||
std::set<string>{} :
|
||||
tokenizeString<std::set<string>>(tokens[3], ",");
|
||||
auto canBuildLocally = amWilling && (neededSystem == localSystem);
|
||||
|
||||
/* Error ignored here, will be caught later */
|
||||
mkdir(currentLoad.c_str(), 0777);
|
||||
@@ -111,7 +99,7 @@ int main (int argc, char * * argv)
|
||||
Machine * bestMachine = nullptr;
|
||||
unsigned long long bestLoad = 0;
|
||||
for (auto & m : machines) {
|
||||
debug("considering building on remote machine '%s'", m.storeUri);
|
||||
debug("considering building on ‘%s’", m.storeUri);
|
||||
|
||||
if (m.enabled && std::find(m.systemTypes.begin(),
|
||||
m.systemTypes.end(),
|
||||
@@ -174,22 +162,16 @@ int main (int argc, char * * argv)
|
||||
|
||||
try {
|
||||
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri));
|
||||
|
||||
Store::Params storeParams;
|
||||
if (hasPrefix(bestMachine->storeUri, "ssh://")) {
|
||||
storeParams["max-connections"] ="1";
|
||||
storeParams["log-fd"] = "4";
|
||||
if (bestMachine->sshKey != "")
|
||||
storeParams["ssh-key"] = bestMachine->sshKey;
|
||||
}
|
||||
Store::Params storeParams{{"max-connections", "1"}, {"log-fd", "4"}};
|
||||
if (bestMachine->sshKey != "")
|
||||
storeParams["ssh-key"] = bestMachine->sshKey;
|
||||
|
||||
sshStore = openStore(bestMachine->storeUri, storeParams);
|
||||
sshStore->connect();
|
||||
storeUri = bestMachine->storeUri;
|
||||
|
||||
} catch (std::exception & e) {
|
||||
printError("unable to open SSH connection to '%s': %s; trying other available machines...",
|
||||
printError("unable to open SSH connection to ‘%s’: %s; trying other available machines...",
|
||||
bestMachine->storeUri, e.what());
|
||||
bestMachine->enabled = false;
|
||||
continue;
|
||||
@@ -200,49 +182,44 @@ int main (int argc, char * * argv)
|
||||
}
|
||||
|
||||
connected:
|
||||
std::cerr << "# accept\n" << storeUri << "\n";
|
||||
std::cerr << "# accept\n";
|
||||
string line;
|
||||
if (!getline(cin, line))
|
||||
throw Error("hook caller didn't send inputs");
|
||||
|
||||
auto inputs = readStrings<PathSet>(source);
|
||||
auto outputs = readStrings<PathSet>(source);
|
||||
auto inputs = tokenizeString<PathSet>(line);
|
||||
if (!getline(cin, line))
|
||||
throw Error("hook caller didn't send outputs");
|
||||
|
||||
auto outputs = tokenizeString<PathSet>(line);
|
||||
|
||||
AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true);
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri));
|
||||
|
||||
auto old = signal(SIGALRM, handleAlarm);
|
||||
alarm(15 * 60);
|
||||
if (!lockFile(uploadLock.get(), ltWrite, true))
|
||||
printError("somebody is hogging the upload lock for '%s', continuing...");
|
||||
alarm(0);
|
||||
signal(SIGALRM, old);
|
||||
}
|
||||
|
||||
auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
|
||||
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
|
||||
copyPaths(store, ref<Store>(sshStore), inputs, NoRepair, NoCheckSigs, substitute);
|
||||
}
|
||||
|
||||
auto old = signal(SIGALRM, handleAlarm);
|
||||
alarm(15 * 60);
|
||||
if (!lockFile(uploadLock.get(), ltWrite, true))
|
||||
printError("somebody is hogging the upload lock for ‘%s’, continuing...");
|
||||
alarm(0);
|
||||
signal(SIGALRM, old);
|
||||
copyPaths(store, ref<Store>(sshStore), inputs, false, true);
|
||||
uploadLock = -1;
|
||||
|
||||
BasicDerivation drv(readDerivation(store->realStoreDir + "/" + baseNameOf(drvPath)));
|
||||
BasicDerivation drv(readDerivation(drvPath));
|
||||
drv.inputSrcs = inputs;
|
||||
|
||||
printError("building ‘%s’ on ‘%s’", drvPath, storeUri);
|
||||
auto result = sshStore->buildDerivation(drvPath, drv);
|
||||
|
||||
if (!result.success())
|
||||
throw Error("build of '%s' on '%s' failed: %s", drvPath, storeUri, result.errorMsg);
|
||||
throw Error("build of ‘%s’ on ‘%s’ failed: %s", drvPath, storeUri, result.errorMsg);
|
||||
|
||||
PathSet missing;
|
||||
for (auto & path : outputs)
|
||||
if (!store->isValidPath(path)) missing.insert(path);
|
||||
|
||||
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 */
|
||||
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, substitute);
|
||||
copyPaths(ref<Store>(sshStore), store, missing, false, true);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,7 @@ static bool isDirectory (const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("getting status of '%1%'") % path);
|
||||
throw SysError(format("getting status of ‘%1%’") % path);
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
@@ -53,18 +53,18 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
|
||||
} else if (S_ISLNK(dstSt.st_mode)) {
|
||||
auto target = readLink(dstFile);
|
||||
if (!isDirectory(target))
|
||||
throw Error(format("collision between '%1%' and non-directory '%2%'")
|
||||
throw Error(format("collision between ‘%1%’ and non-directory ‘%2%’")
|
||||
% srcFile % target);
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
throw SysError(format("unlinking '%1%'") % dstFile);
|
||||
throw SysError(format("unlinking ‘%1%’") % dstFile);
|
||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
||||
throw SysError(format("creating directory '%1%'"));
|
||||
throw SysError(format("creating directory ‘%1%’"));
|
||||
createLinks(target, dstFile, priorities[dstFile]);
|
||||
createLinks(srcFile, dstFile, priority);
|
||||
continue;
|
||||
}
|
||||
} else if (errno != ENOENT)
|
||||
throw SysError(format("getting status of '%1%'") % dstFile);
|
||||
throw SysError(format("getting status of ‘%1%’") % dstFile);
|
||||
} else {
|
||||
struct stat dstSt;
|
||||
auto res = lstat(dstFile.c_str(), &dstSt);
|
||||
@@ -74,18 +74,17 @@ static void createLinks(const Path & srcDir, const Path & dstDir, int priority)
|
||||
auto prevPriority = priorities[dstFile];
|
||||
if (prevPriority == priority)
|
||||
throw Error(format(
|
||||
"Packages '%1%' and '%2%' have the same priority '%3%'"
|
||||
"use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' "
|
||||
"collision between ‘%1%’ and ‘%2%’; "
|
||||
"use ‘nix-env --set-flag priority NUMBER PKGNAME’ "
|
||||
"to change the priority of one of the conflicting packages"
|
||||
" ('0' being the highest priority)"
|
||||
) % srcFile % target % priority);
|
||||
) % srcFile % target);
|
||||
if (prevPriority < priority)
|
||||
continue;
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
throw SysError(format("unlinking '%1%'") % dstFile);
|
||||
throw SysError(format("unlinking ‘%1%’") % dstFile);
|
||||
}
|
||||
} else if (errno != ENOENT)
|
||||
throw SysError(format("getting status of '%1%'") % dstFile);
|
||||
throw SysError(format("getting status of ‘%1%’") % dstFile);
|
||||
}
|
||||
createSymlink(srcFile, dstFile);
|
||||
priorities[dstFile] = priority;
|
||||
@@ -113,11 +112,11 @@ static void addPkg(const Path & pkgDir, int priority)
|
||||
if (!fd) {
|
||||
if (errno == ENOENT)
|
||||
return;
|
||||
throw SysError(format("opening '%1%'") % propagatedFN);
|
||||
throw SysError(format("opening ‘%1%’") % propagatedFN);
|
||||
}
|
||||
propagated = readFile(fd.get());
|
||||
propagated = readLine(fd.get());
|
||||
}
|
||||
for (const auto & p : tokenizeString<std::vector<string>>(propagated, " \n"))
|
||||
for (const auto & p : tokenizeString<std::vector<string>>(propagated, " "))
|
||||
if (done.find(p) == done.end())
|
||||
postponed.insert(p);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ static Strings parseAttrPath(const string & s)
|
||||
++i;
|
||||
while (1) {
|
||||
if (i == s.end())
|
||||
throw Error(format("missing closing quote in selection path '%1%'") % s);
|
||||
throw Error(format("missing closing quote in selection path ‘%1%’") % s);
|
||||
if (*i == '"') break;
|
||||
cur.push_back(*i++);
|
||||
}
|
||||
@@ -38,7 +38,7 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Strings tokens = parseAttrPath(attrPath);
|
||||
|
||||
Error attrError =
|
||||
Error(format("attribute selection path '%1%' does not match expression") % attrPath);
|
||||
Error(format("attribute selection path ‘%1%’ does not match expression") % attrPath);
|
||||
|
||||
Value * v = &vIn;
|
||||
|
||||
@@ -62,15 +62,15 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
if (v->type != tAttrs)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path '%1%' should be a set but is %2%")
|
||||
format("the expression selected by the selection path ‘%1%’ should be a set but is %2%")
|
||||
% attrPath % showType(*v));
|
||||
|
||||
if (attr.empty())
|
||||
throw Error(format("empty attribute name in selection path '%1%'") % attrPath);
|
||||
throw Error(format("empty attribute name in selection path ‘%1%’") % attrPath);
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (a == v->attrs->end())
|
||||
throw Error(format("attribute '%1%' in selection path '%2%' not found") % attr % attrPath);
|
||||
throw Error(format("attribute ‘%1%’ in selection path ‘%2%’ not found") % attr % attrPath);
|
||||
v = &*a->value;
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
if (!v->isList())
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path '%1%' should be a list but is %2%")
|
||||
format("the expression selected by the selection path ‘%1%’ should be a list but is %2%")
|
||||
% attrPath % showType(*v));
|
||||
|
||||
if (attrIndex >= v->listSize())
|
||||
throw Error(format("list index %1% in selection path '%2%' is out of range") % attrIndex % attrPath);
|
||||
throw Error(format("list index %1% in selection path ‘%2%’ is out of range") % attrIndex % attrPath);
|
||||
|
||||
v = v->listElems()[attrIndex];
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#include "common-eval-args.hh"
|
||||
#include "shared.hh"
|
||||
#include "download.hh"
|
||||
#include "util.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
MixEvalArgs::MixEvalArgs()
|
||||
{
|
||||
mkFlag()
|
||||
.longName("arg")
|
||||
.description("argument to be passed to Nix functions")
|
||||
.labels({"name", "expr"})
|
||||
.handler([&](std::vector<std::string> ss) { autoArgs[ss[0]] = 'E' + ss[1]; });
|
||||
|
||||
mkFlag()
|
||||
.longName("argstr")
|
||||
.description("string-valued argument to be passed to Nix functions")
|
||||
.labels({"name", "string"})
|
||||
.handler([&](std::vector<std::string> ss) { autoArgs[ss[0]] = 'S' + ss[1]; });
|
||||
|
||||
mkFlag()
|
||||
.shortName('I')
|
||||
.longName("include")
|
||||
.description("add a path to the list of locations used to look up <...> file names")
|
||||
.label("path")
|
||||
.handler([&](std::string s) { searchPath.push_back(s); });
|
||||
}
|
||||
|
||||
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||
{
|
||||
Bindings * res = state.allocBindings(autoArgs.size());
|
||||
for (auto & i : autoArgs) {
|
||||
Value * v = state.allocValue();
|
||||
if (i.second[0] == 'E')
|
||||
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
|
||||
else
|
||||
mkString(*v, string(i.second, 1));
|
||||
res->push_back(Attr(state.symbols.create(i.first), v));
|
||||
}
|
||||
res->sort();
|
||||
return res;
|
||||
}
|
||||
|
||||
Path lookupFileArg(EvalState & state, string s)
|
||||
{
|
||||
if (isUri(s))
|
||||
return getDownloader()->downloadCached(state.store, s, true);
|
||||
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
|
||||
Path p = s.substr(1, s.size() - 2);
|
||||
return state.findFile(p);
|
||||
} else
|
||||
return absPath(s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "args.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Store;
|
||||
class EvalState;
|
||||
class Bindings;
|
||||
|
||||
struct MixEvalArgs : virtual Args
|
||||
{
|
||||
MixEvalArgs();
|
||||
|
||||
Bindings * getAutoArgs(EvalState & state);
|
||||
|
||||
Strings searchPath;
|
||||
|
||||
private:
|
||||
|
||||
std::map<std::string, std::string> autoArgs;
|
||||
};
|
||||
|
||||
Path lookupFileArg(EvalState & state, string s);
|
||||
|
||||
}
|
||||
67
src/libexpr/common-opts.cc
Normal file
67
src/libexpr/common-opts.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "common-opts.hh"
|
||||
#include "shared.hh"
|
||||
#include "download.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
bool parseAutoArgs(Strings::iterator & i,
|
||||
const Strings::iterator & argsEnd, std::map<string, string> & res)
|
||||
{
|
||||
string arg = *i;
|
||||
if (arg != "--arg" && arg != "--argstr") return false;
|
||||
|
||||
UsageError error(format("‘%1%’ requires two arguments") % arg);
|
||||
|
||||
if (++i == argsEnd) throw error;
|
||||
string name = *i;
|
||||
if (++i == argsEnd) throw error;
|
||||
string value = *i;
|
||||
|
||||
res[name] = (arg == "--arg" ? 'E' : 'S') + value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Bindings * evalAutoArgs(EvalState & state, std::map<string, string> & in)
|
||||
{
|
||||
Bindings * res = state.allocBindings(in.size());
|
||||
for (auto & i : in) {
|
||||
Value * v = state.allocValue();
|
||||
if (i.second[0] == 'E')
|
||||
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
|
||||
else
|
||||
mkString(*v, string(i.second, 1));
|
||||
res->push_back(Attr(state.symbols.create(i.first), v));
|
||||
}
|
||||
res->sort();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool parseSearchPathArg(Strings::iterator & i,
|
||||
const Strings::iterator & argsEnd, Strings & searchPath)
|
||||
{
|
||||
if (*i != "-I") return false;
|
||||
if (++i == argsEnd) throw UsageError("‘-I’ requires an argument");
|
||||
searchPath.push_back(*i);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Path lookupFileArg(EvalState & state, string s)
|
||||
{
|
||||
if (isUri(s))
|
||||
return getDownloader()->downloadCached(state.store, s, true);
|
||||
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
|
||||
Path p = s.substr(1, s.size() - 2);
|
||||
return state.findFile(p);
|
||||
} else
|
||||
return absPath(s);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
20
src/libexpr/common-opts.hh
Normal file
20
src/libexpr/common-opts.hh
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Store;
|
||||
|
||||
/* Some common option parsing between nix-env and nix-instantiate. */
|
||||
bool parseAutoArgs(Strings::iterator & i,
|
||||
const Strings::iterator & argsEnd, std::map<string, string> & res);
|
||||
|
||||
Bindings * evalAutoArgs(EvalState & state, std::map<string, string> & in);
|
||||
|
||||
bool parseSearchPathArg(Strings::iterator & i,
|
||||
const Strings::iterator & argsEnd, Strings & searchPath);
|
||||
|
||||
Path lookupFileArg(EvalState & state, string s);
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
|
||||
v.type = tBlackhole;
|
||||
//checkInterrupt();
|
||||
expr->eval(*this, *env, v);
|
||||
} catch (...) {
|
||||
} catch (Error & e) {
|
||||
v.type = tThunk;
|
||||
v.thunk.env = env;
|
||||
v.thunk.expr = expr;
|
||||
|
||||
@@ -149,7 +149,7 @@ string showType(const Value & v)
|
||||
switch (v.type) {
|
||||
case tInt: return "an integer";
|
||||
case tBool: return "a boolean";
|
||||
case tString: return v.string.context ? "a string with context" : "a string";
|
||||
case tString: return "a string";
|
||||
case tPath: return "a path";
|
||||
case tNull: return "null";
|
||||
case tAttrs: return "a set";
|
||||
@@ -293,7 +293,6 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||
, sWrong(symbols.create("wrong"))
|
||||
, sStructuredAttrs(symbols.create("__structuredAttrs"))
|
||||
, sBuilder(symbols.create("builder"))
|
||||
, repair(NoRepair)
|
||||
, store(store)
|
||||
, baseEnv(allocEnv(128))
|
||||
, staticBaseEnv(false, 0)
|
||||
@@ -329,7 +328,7 @@ Path EvalState::checkSourcePath(const Path & path_)
|
||||
if (!restricted) return path_;
|
||||
|
||||
/* Resolve symlinks. */
|
||||
debug(format("checking access to '%s'") % path_);
|
||||
debug(format("checking access to ‘%s’") % path_);
|
||||
Path path = canonPath(path_, true);
|
||||
|
||||
for (auto & i : searchPath) {
|
||||
@@ -351,27 +350,7 @@ Path EvalState::checkSourcePath(const Path & path_)
|
||||
return path;
|
||||
#endif
|
||||
|
||||
throw RestrictedPathError(format("access to path '%1%' is forbidden in restricted mode") % path_);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::checkURI(const std::string & uri)
|
||||
{
|
||||
if (!restricted) return;
|
||||
|
||||
/* 'uri' should be equal to a prefix, or in a subdirectory of a
|
||||
prefix. Thus, the prefix https://github.co does not permit
|
||||
access to https://github.com. Note: this allows 'http://' and
|
||||
'https://' as prefixes for any http/https URI. */
|
||||
for (auto & prefix : settings.allowedUris.get())
|
||||
if (uri == prefix ||
|
||||
(uri.size() > prefix.size()
|
||||
&& prefix.size() > 0
|
||||
&& hasPrefix(uri, prefix)
|
||||
&& (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
|
||||
return;
|
||||
|
||||
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
|
||||
throw RestrictedPathError(format("access to path ‘%1%’ is forbidden in restricted mode") % path_);
|
||||
}
|
||||
|
||||
|
||||
@@ -524,7 +503,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
return j->value;
|
||||
}
|
||||
if (!env->prevWith)
|
||||
throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos);
|
||||
throwUndefinedVarError("undefined variable ‘%1%’ at %2%", var.name, var.pos);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
@@ -663,12 +642,12 @@ void EvalState::evalFile(const Path & path, Value & v)
|
||||
return;
|
||||
}
|
||||
|
||||
printTalkative("evaluating file '%1%'", path2);
|
||||
printTalkative("evaluating file ‘%1%’", path2);
|
||||
Expr * e = parseExprFromFile(checkSourcePath(path2));
|
||||
try {
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);
|
||||
addErrorPrefix(e, "while evaluating the file ‘%1%’:\n", path2);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -818,7 +797,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
||||
Bindings::iterator j = v.attrs->find(nameSym);
|
||||
if (j != v.attrs->end())
|
||||
throwEvalError("dynamic attribute '%1%' at %2% already defined at %3%", nameSym, i.pos, *j->pos);
|
||||
throwEvalError("dynamic attribute ‘%1%’ at %2% already defined at %3%", nameSym, i.pos, *j->pos);
|
||||
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
@@ -906,7 +885,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
} else {
|
||||
state.forceAttrs(*vAttrs, pos);
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
throwEvalError("attribute '%1%' missing, at %2%", name, pos);
|
||||
throwEvalError("attribute ‘%1%’ missing, at %2%", name, pos);
|
||||
}
|
||||
vAttrs = j->value;
|
||||
pos2 = j->pos;
|
||||
@@ -917,7 +896,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
} catch (Error & e) {
|
||||
if (pos2 && pos2->file != state.sDerivationNix)
|
||||
addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n",
|
||||
addErrorPrefix(e, "while evaluating the attribute ‘%1%’ at %2%:\n",
|
||||
showAttrPath(state, env, attrPath), *pos2);
|
||||
throw;
|
||||
}
|
||||
@@ -1059,7 +1038,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
for (auto & i : lambda.formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i.name);
|
||||
if (j == arg.attrs->end()) {
|
||||
if (!i.def) throwTypeError("%1% called without required argument '%2%', at %3%",
|
||||
if (!i.def) throwTypeError("%1% called without required argument ‘%2%’, at %3%",
|
||||
lambda, i.name, pos);
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
@@ -1075,7 +1054,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
||||
user. */
|
||||
for (auto & i : *arg.attrs)
|
||||
if (lambda.formals->argNames.find(i.name) == lambda.formals->argNames.end())
|
||||
throwTypeError("%1% called with unexpected argument '%2%', at %3%", lambda, i.name, pos);
|
||||
throwTypeError("%1% called with unexpected argument ‘%2%’, at %3%", lambda, i.name, pos);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
@@ -1133,7 +1112,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||
if (j != args.end())
|
||||
actualArgs->attrs->push_back(*j);
|
||||
else if (!i.def)
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value ('%1%')", i.name);
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value (‘%1%’)", i.name);
|
||||
}
|
||||
|
||||
actualArgs->attrs->sort();
|
||||
@@ -1362,7 +1341,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||
try {
|
||||
recurse(*i.value);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the attribute '%1%' at %2%:\n", i.name, *i.pos);
|
||||
addErrorPrefix(e, "while evaluating the attribute ‘%1%’ at %2%:\n", i.name, *i.pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -1454,10 +1433,10 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
||||
string s = forceString(v, pos);
|
||||
if (v.string.context) {
|
||||
if (pos)
|
||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%'), at %3%",
|
||||
throwEvalError("the string ‘%1%’ is not allowed to refer to a store path (such as ‘%2%’), at %3%",
|
||||
v.string.s, v.string.context[0], pos);
|
||||
else
|
||||
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
throwEvalError("the string ‘%1%’ is not allowed to refer to a store path (such as ‘%2%’)",
|
||||
v.string.s, v.string.context[0]);
|
||||
}
|
||||
return s;
|
||||
@@ -1539,7 +1518,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||
{
|
||||
if (nix::isDerivation(path))
|
||||
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
|
||||
throwEvalError("file names are not allowed to end in ‘%1%’", drvExtension);
|
||||
|
||||
Path dstPath;
|
||||
if (srcToStore[path] != "")
|
||||
@@ -1549,7 +1528,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||
? store->computeStorePathForPath(checkSourcePath(path)).first
|
||||
: store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair);
|
||||
srcToStore[path] = dstPath;
|
||||
printMsg(lvlChatty, format("copied source '%1%' -> '%2%'")
|
||||
printMsg(lvlChatty, format("copied source ‘%1%’ -> ‘%2%’")
|
||||
% path % dstPath);
|
||||
}
|
||||
|
||||
@@ -1562,7 +1541,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||
{
|
||||
string path = coerceToString(pos, v, context, false, false);
|
||||
if (path == "" || path[0] != '/')
|
||||
throwEvalError("string '%1%' doesn't represent an absolute path, at %2%", path, pos);
|
||||
throwEvalError("string ‘%1%’ doesn't represent an absolute path, at %2%", path, pos);
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -1683,8 +1662,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;
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace nix {
|
||||
|
||||
class Store;
|
||||
class EvalState;
|
||||
enum RepairFlag : bool;
|
||||
|
||||
|
||||
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
@@ -74,7 +73,7 @@ public:
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
RepairFlag repair;
|
||||
bool repair = false;
|
||||
|
||||
/* If set, don't allow access to files outside of the Nix search
|
||||
path or to environment variables. */
|
||||
@@ -89,7 +88,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
|
||||
@@ -110,8 +109,6 @@ public:
|
||||
|
||||
Path checkSourcePath(const Path & path);
|
||||
|
||||
void checkURI(const std::string & uri);
|
||||
|
||||
/* Parse a Nix expression from the specified file. */
|
||||
Expr * parseExprFromFile(const Path & path);
|
||||
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
||||
@@ -120,8 +117,6 @@ public:
|
||||
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
|
||||
Expr * parseExprFromString(const string & s, const Path & basePath);
|
||||
|
||||
Expr * parseStdin();
|
||||
|
||||
/* Evaluate an expression read from the given file to normal
|
||||
form. */
|
||||
void evalFile(const Path & path, Value & v);
|
||||
@@ -269,8 +264,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 +282,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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "get-drvs.hh"
|
||||
#include "util.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
@@ -10,61 +9,7 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
DrvInfo::DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs)
|
||||
: state(&state), attrs(attrs), attrPath(attrPath)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
|
||||
: state(&state), attrs(nullptr), attrPath("")
|
||||
{
|
||||
auto spec = parseDrvPathWithOutputs(drvPathWithOutputs);
|
||||
|
||||
drvPath = spec.first;
|
||||
|
||||
auto drv = store->derivationFromPath(drvPath);
|
||||
|
||||
name = storePathToName(drvPath);
|
||||
|
||||
if (spec.second.size() > 1)
|
||||
throw Error("building more than one derivation output is not supported, in '%s'", drvPathWithOutputs);
|
||||
|
||||
outputName =
|
||||
spec.second.empty()
|
||||
? get(drv.env, "outputName", "out")
|
||||
: *spec.second.begin();
|
||||
|
||||
auto i = drv.outputs.find(outputName);
|
||||
if (i == drv.outputs.end())
|
||||
throw Error("derivation '%s' does not have output '%s'", drvPath, outputName);
|
||||
|
||||
outPath = i->second.path;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryName() const
|
||||
{
|
||||
if (name == "" && attrs) {
|
||||
auto i = attrs->find(state->sName);
|
||||
if (i == attrs->end()) throw TypeError("derivation name missing");
|
||||
name = state->forceStringNoCtx(*i->value);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::querySystem() const
|
||||
{
|
||||
if (system == "" && attrs) {
|
||||
auto i = attrs->find(state->sSystem);
|
||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos);
|
||||
}
|
||||
return system;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryDrvPath() const
|
||||
string DrvInfo::queryDrvPath()
|
||||
{
|
||||
if (drvPath == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->sDrvPath);
|
||||
@@ -75,7 +20,7 @@ string DrvInfo::queryDrvPath() const
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryOutPath() const
|
||||
string DrvInfo::queryOutPath()
|
||||
{
|
||||
if (outPath == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->sOutPath);
|
||||
@@ -117,7 +62,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
|
||||
const Value * outTI = queryMeta("outputsToInstall");
|
||||
if (!outTI) return outputs;
|
||||
const auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
|
||||
const auto errMsg = Error("this derivation has bad ‘meta.outputsToInstall’");
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw errMsg;
|
||||
Outputs result;
|
||||
@@ -131,7 +76,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryOutputName() const
|
||||
string DrvInfo::queryOutputName()
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->sOutputName);
|
||||
@@ -280,12 +225,17 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
if (done.find(v.attrs) != done.end()) return false;
|
||||
done.insert(v.attrs);
|
||||
|
||||
DrvInfo drv(state, attrPath, v.attrs);
|
||||
Bindings::iterator i = v.attrs->find(state.sName);
|
||||
/* !!! We really would like to have a decent back trace here. */
|
||||
if (i == v.attrs->end()) throw TypeError("derivation name missing");
|
||||
|
||||
drv.queryName();
|
||||
Bindings::iterator i2 = v.attrs->find(state.sSystem);
|
||||
|
||||
DrvInfo drv(state, state.forceStringNoCtx(*i->value), attrPath,
|
||||
i2 == v.attrs->end() ? "unknown" : state.forceStringNoCtx(*i2->value, *i2->pos),
|
||||
v.attrs);
|
||||
|
||||
drvs.push_back(drv);
|
||||
|
||||
return false;
|
||||
|
||||
} catch (AssertionError & e) {
|
||||
@@ -295,14 +245,15 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
}
|
||||
|
||||
|
||||
std::experimental::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return {};
|
||||
return std::move(drvs.front());
|
||||
if (drvs.size() != 1) return false;
|
||||
drv = drvs.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +289,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
bound to the attribute with the "lower" name should take
|
||||
precedence). */
|
||||
for (auto & i : v.attrs->lexicographicOrder()) {
|
||||
debug("evaluating attribute '%1%'", i->name);
|
||||
debug("evaluating attribute ‘%1%’", i->name);
|
||||
if (!std::regex_match(std::string(i->name), attrRegex))
|
||||
continue;
|
||||
string pathPrefix2 = addToPath(pathPrefix, i->name);
|
||||
|
||||
@@ -17,33 +17,31 @@ public:
|
||||
private:
|
||||
EvalState * state;
|
||||
|
||||
mutable string name;
|
||||
mutable string system;
|
||||
mutable string drvPath;
|
||||
mutable string outPath;
|
||||
mutable string outputName;
|
||||
string drvPath;
|
||||
string outPath;
|
||||
string outputName;
|
||||
Outputs outputs;
|
||||
|
||||
bool failed = false; // set if we get an AssertionError
|
||||
bool failed; // set if we get an AssertionError
|
||||
|
||||
Bindings * attrs = nullptr, * meta = nullptr;
|
||||
Bindings * attrs, * meta;
|
||||
|
||||
Bindings * getMeta();
|
||||
|
||||
bool checkMeta(Value & v);
|
||||
|
||||
public:
|
||||
string name;
|
||||
string attrPath; /* path towards the derivation */
|
||||
string system;
|
||||
|
||||
DrvInfo(EvalState & state) : state(&state) { };
|
||||
DrvInfo(EvalState & state, const string & attrPath, Bindings * attrs);
|
||||
DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
|
||||
DrvInfo(EvalState & state) : state(&state), failed(false), attrs(0), meta(0) { };
|
||||
DrvInfo(EvalState & state, const string & name, const string & attrPath, const string & system, Bindings * attrs)
|
||||
: state(&state), failed(false), attrs(attrs), meta(0), name(name), attrPath(attrPath), system(system) { };
|
||||
|
||||
string queryName() const;
|
||||
string querySystem() const;
|
||||
string queryDrvPath() const;
|
||||
string queryOutPath() const;
|
||||
string queryOutputName() const;
|
||||
string queryDrvPath();
|
||||
string queryOutPath();
|
||||
string queryOutputName();
|
||||
/** Return the list of outputs. The "outputs to install" are determined by `mesa.outputsToInstall`. */
|
||||
Outputs queryOutputs(bool onlyOutputsToInstall = false);
|
||||
|
||||
@@ -60,9 +58,15 @@ public:
|
||||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
*/
|
||||
|
||||
void setName(const string & s) { name = s; }
|
||||
void setDrvPath(const string & s) { drvPath = s; }
|
||||
void setOutPath(const string & s) { outPath = s; }
|
||||
void setDrvPath(const string & s)
|
||||
{
|
||||
drvPath = s;
|
||||
}
|
||||
|
||||
void setOutPath(const string & s)
|
||||
{
|
||||
outPath = s;
|
||||
}
|
||||
|
||||
void setFailed() { failed = true; };
|
||||
bool hasFailed() { return failed; };
|
||||
@@ -76,10 +80,10 @@ typedef list<DrvInfo> DrvInfos;
|
||||
#endif
|
||||
|
||||
|
||||
/* If value `v' denotes a derivation, return a DrvInfo object
|
||||
describing it. Otherwise return nothing. */
|
||||
std::experimental::optional<DrvInfo> getDerivation(EvalState & state,
|
||||
Value & v, bool ignoreAssertionFailures);
|
||||
/* If value `v' denotes a derivation, store information about the
|
||||
derivation in `drv' and return true. Otherwise, return false. */
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs,
|
||||
|
||||
@@ -57,7 +57,7 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
|
||||
values.push_back(v2);
|
||||
skipWhitespace(s);
|
||||
if (*s == ']') break;
|
||||
if (*s != ',') throw JSONParseError("expected ',' or ']' after JSON array element");
|
||||
if (*s != ',') throw JSONParseError("expected ‘,’ or ‘]’ after JSON array element");
|
||||
s++;
|
||||
}
|
||||
s++;
|
||||
@@ -74,14 +74,14 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
|
||||
if (attrs.empty() && *s == '}') break;
|
||||
string name = parseJSONString(s);
|
||||
skipWhitespace(s);
|
||||
if (*s != ':') throw JSONParseError("expected ':' in JSON object");
|
||||
if (*s != ':') throw JSONParseError("expected ‘:’ in JSON object");
|
||||
s++;
|
||||
Value * v2 = state.allocValue();
|
||||
parseJSON(state, s, *v2);
|
||||
attrs[state.symbols.create(name)] = v2;
|
||||
skipWhitespace(s);
|
||||
if (*s == '}') break;
|
||||
if (*s != ',') throw JSONParseError("expected ',' or '}' after JSON member");
|
||||
if (*s != ',') throw JSONParseError("expected ‘,’ or ‘}’ after JSON member");
|
||||
s++;
|
||||
}
|
||||
state.mkAttrs(v, attrs.size());
|
||||
@@ -106,16 +106,10 @@ static void parseJSON(EvalState & state, const char * & s, Value & v)
|
||||
tmp_number += *s++;
|
||||
}
|
||||
|
||||
try {
|
||||
if (number_type == tFloat)
|
||||
mkFloat(v, stod(tmp_number));
|
||||
else
|
||||
mkInt(v, stoi(tmp_number));
|
||||
} catch (std::invalid_argument e) {
|
||||
throw JSONParseError("invalid JSON number");
|
||||
} catch (std::out_of_range e) {
|
||||
throw JSONParseError("out-of-range JSON number");
|
||||
}
|
||||
if (number_type == tFloat)
|
||||
mkFloat(v, stod(tmp_number));
|
||||
else
|
||||
mkInt(v, stoi(tmp_number));
|
||||
}
|
||||
|
||||
else if (strncmp(s, "true", 4) == 0) {
|
||||
|
||||
@@ -124,13 +124,13 @@ or { return OR_KW; }
|
||||
{INT} { errno = 0;
|
||||
yylval->n = strtol(yytext, 0, 10);
|
||||
if (errno != 0)
|
||||
throw ParseError(format("invalid integer '%1%'") % yytext);
|
||||
throw ParseError(format("invalid integer ‘%1%’") % yytext);
|
||||
return INT;
|
||||
}
|
||||
{FLOAT} { errno = 0;
|
||||
yylval->nf = strtod(yytext, 0);
|
||||
if (errno != 0)
|
||||
throw ParseError(format("invalid float '%1%'") % yytext);
|
||||
throw ParseError(format("invalid float ‘%1%’") % yytext);
|
||||
return FLOAT;
|
||||
}
|
||||
|
||||
@@ -191,12 +191,12 @@ or { return OR_KW; }
|
||||
<INITIAL,INSIDE_DOLLAR_CURLY>{
|
||||
|
||||
{PATH} { if (yytext[yyleng-1] == '/')
|
||||
throw ParseError("path '%s' has a trailing slash", yytext);
|
||||
throw ParseError("path ‘%s’ has a trailing slash", yytext);
|
||||
yylval->path = strdup(yytext);
|
||||
return PATH;
|
||||
}
|
||||
{HPATH} { if (yytext[yyleng-1] == '/')
|
||||
throw ParseError("path '%s' has a trailing slash", yytext);
|
||||
throw ParseError("path ‘%s’ has a trailing slash", yytext);
|
||||
yylval->path = strdup(yytext);
|
||||
return HPATH;
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||
/* Otherwise, the variable must be obtained from the nearest
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1) throw UndefinedVarError(format("undefined variable '%1%' at %2%") % name % pos);
|
||||
if (withLevel == -1) throw UndefinedVarError(format("undefined variable ‘%1%’ at %2%") % name % pos);
|
||||
|
||||
fromWith = true;
|
||||
this->level = withLevel;
|
||||
@@ -419,7 +419,7 @@ void ExprLambda::setName(Symbol & name)
|
||||
|
||||
string ExprLambda::showNamePos() const
|
||||
{
|
||||
return (format("%1% at %2%") % (name.set() ? "'" + (string) name + "'" : "anonymous function") % pos).str();
|
||||
return (format("%1% at %2%") % (name.set() ? "‘" + (string) name + "’" : "anonymous function") % pos).str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ struct ExprLambda : Expr
|
||||
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
||||
{
|
||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
|
||||
throw ParseError(format("duplicate formal function argument ‘%1%’ at %2%")
|
||||
% arg % pos);
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
|
||||
@@ -65,14 +65,14 @@ namespace nix {
|
||||
|
||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
|
||||
throw ParseError(format("attribute ‘%1%’ at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
throw ParseError(format("attribute '%1%' at %2% already defined at %3%")
|
||||
throw ParseError(format("attribute ‘%1%’ at %2% already defined at %3%")
|
||||
% attr % pos % prevPos);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
{
|
||||
if (formals->argNames.find(formal.name) != formals->argNames.end())
|
||||
throw ParseError(format("duplicate formal function argument '%1%' at %2%")
|
||||
throw ParseError(format("duplicate formal function argument ‘%1%’ at %2%")
|
||||
% formal.name % pos);
|
||||
formals->formals.push_front(formal);
|
||||
formals->argNames.insert(formal.name);
|
||||
@@ -523,6 +523,7 @@ formal
|
||||
#include "eval.hh"
|
||||
#include "download.hh"
|
||||
#include "store-api.hh"
|
||||
#include "primops/fetchgit.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -563,7 +564,7 @@ Path resolveExprPath(Path path)
|
||||
struct stat st;
|
||||
while (true) {
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting status of '%1%'") % path);
|
||||
throw SysError(format("getting status of ‘%1%’") % path);
|
||||
if (!S_ISLNK(st.st_mode)) break;
|
||||
path = absPath(readLink(path), dirOf(path));
|
||||
}
|
||||
@@ -600,13 +601,6 @@ Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseStdin()
|
||||
{
|
||||
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
|
||||
return parseExprFromString(drainFD(0), absPath("."));
|
||||
}
|
||||
|
||||
|
||||
void EvalState::addToSearchPath(const string & s)
|
||||
{
|
||||
size_t pos = s.find('=');
|
||||
@@ -648,7 +642,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
|
||||
if (pathExists(res)) return canonPath(res);
|
||||
}
|
||||
format f = format(
|
||||
"file '%1%' was not found in the Nix search path (add it using $NIX_PATH or -I)"
|
||||
"file ‘%1%’ was not found in the Nix search path (add it using $NIX_PATH or -I)"
|
||||
+ string(pos ? ", at %2%" : ""));
|
||||
f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
|
||||
throw ThrownError(f % path % pos);
|
||||
@@ -664,9 +658,13 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||
|
||||
if (isUri(elem.second)) {
|
||||
try {
|
||||
res = { true, getDownloader()->downloadCached(store, elem.second, true) };
|
||||
if (hasPrefix(elem.second, "git://") || hasSuffix(elem.second, ".git"))
|
||||
// FIXME: support specifying revision/branch
|
||||
res = { true, exportGit(store, elem.second, "master") };
|
||||
else
|
||||
res = { true, getDownloader()->downloadCached(store, elem.second, true) };
|
||||
} catch (DownloadError & e) {
|
||||
printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second);
|
||||
printError(format("warning: Nix search path entry ‘%1%’ cannot be downloaded, ignoring") % elem.second);
|
||||
res = { false, "" };
|
||||
}
|
||||
} else {
|
||||
@@ -674,12 +672,12 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
|
||||
if (pathExists(path))
|
||||
res = { true, path };
|
||||
else {
|
||||
printError(format("warning: Nix search path entry '%1%' does not exist, ignoring") % elem.second);
|
||||
printError(format("warning: Nix search path entry ‘%1%’ does not exist, ignoring") % elem.second);
|
||||
res = { false, "" };
|
||||
}
|
||||
}
|
||||
|
||||
debug(format("resolved search path element '%s' to '%s'") % elem.second % res.second);
|
||||
debug(format("resolved search path element ‘%s’ to ‘%s’") % elem.second % res.second);
|
||||
|
||||
searchPathResolved[elem.second] = res;
|
||||
return res;
|
||||
|
||||
@@ -44,7 +44,7 @@ std::pair<string, string> decodeContext(const string & s)
|
||||
|
||||
|
||||
InvalidPathError::InvalidPathError(const Path & path) :
|
||||
EvalError(format("path '%1%' is not valid") % path), path(path) {}
|
||||
EvalError(format("path ‘%1%’ is not valid") % path), path(path) {}
|
||||
|
||||
void EvalState::realiseContext(const PathSet & context)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ void EvalState::realiseContext(const PathSet & context)
|
||||
}
|
||||
if (!drvs.empty()) {
|
||||
if (!settings.enableImportFromDerivation)
|
||||
throw EvalError(format("attempted to realize '%1%' during evaluation but 'allow-import-from-derivation' is false") % *(drvs.begin()));
|
||||
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;
|
||||
@@ -80,7 +80,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
|
||||
throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ static void prim_scopedImport(EvalState & state, const Pos & pos, Value * * args
|
||||
env->values[displ++] = attr.value;
|
||||
}
|
||||
|
||||
printTalkative("evaluating file '%1%'", path);
|
||||
printTalkative("evaluating file ‘%1%’", path);
|
||||
Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
|
||||
|
||||
e->eval(state, *env, v);
|
||||
@@ -149,7 +149,7 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError(format("cannot import '%1%', since path '%2%' is not valid, at %3%")
|
||||
throw EvalError(format("cannot import ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
@@ -159,16 +159,16 @@ static void prim_importNative(EvalState & state, const Pos & pos, Value * * args
|
||||
|
||||
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
throw EvalError(format("could not open '%1%': %2%") % path % dlerror());
|
||||
throw EvalError(format("could not open ‘%1%’: %2%") % path % dlerror());
|
||||
|
||||
dlerror();
|
||||
ValueInitializer func = (ValueInitializer) dlsym(handle, sym.c_str());
|
||||
if(!func) {
|
||||
char *message = dlerror();
|
||||
if (message)
|
||||
throw EvalError(format("could not load symbol '%1%' from '%2%': %3%") % sym % path % message);
|
||||
throw EvalError(format("could not load symbol ‘%1%’ from ‘%2%’: %3%") % sym % path % message);
|
||||
else
|
||||
throw EvalError(format("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected")
|
||||
throw EvalError(format("symbol ‘%1%’ from ‘%2%’ resolved to NULL when a function pointer was expected")
|
||||
% sym % path);
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ static void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError(format("cannot execute '%1%', since path '%2%' is not valid, at %3%")
|
||||
throw EvalError(format("cannot execute ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||
% program % e.path % pos);
|
||||
}
|
||||
|
||||
@@ -205,13 +205,13 @@ static void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
try {
|
||||
parsed = state.parseExprFromString(output, pos.file);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("While parsing the output from '%1%', at %2%\n") % program % pos);
|
||||
e.addPrefix(format("While parsing the output from ‘%1%’, at %2%\n") % program % pos);
|
||||
throw;
|
||||
}
|
||||
try {
|
||||
state.eval(parsed, v);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("While evaluating the output from '%1%', at %2%\n") % program % pos);
|
||||
e.addPrefix(format("While evaluating the output from ‘%1%’, at %2%\n") % program % pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +332,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
Bindings::iterator startSet =
|
||||
args[0]->attrs->find(state.symbols.create("startSet"));
|
||||
if (startSet == args[0]->attrs->end())
|
||||
throw EvalError(format("attribute 'startSet' required, at %1%") % pos);
|
||||
throw EvalError(format("attribute ‘startSet’ required, at %1%") % pos);
|
||||
state.forceList(*startSet->value, pos);
|
||||
|
||||
ValueList workSet;
|
||||
@@ -343,7 +343,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
Bindings::iterator op =
|
||||
args[0]->attrs->find(state.symbols.create("operator"));
|
||||
if (op == args[0]->attrs->end())
|
||||
throw EvalError(format("attribute 'operator' required, at %1%") % pos);
|
||||
throw EvalError(format("attribute ‘operator’ required, at %1%") % pos);
|
||||
state.forceValue(*op->value);
|
||||
|
||||
/* Construct the closure by applying the operator to element of
|
||||
@@ -362,7 +362,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
||||
Bindings::iterator key =
|
||||
e->attrs->find(state.symbols.create("key"));
|
||||
if (key == e->attrs->end())
|
||||
throw EvalError(format("attribute 'key' required, at %1%") % pos);
|
||||
throw EvalError(format("attribute ‘key’ required, at %1%") % pos);
|
||||
state.forceValue(*key->value);
|
||||
|
||||
if (doneKeys.find(key->value) != doneKeys.end()) continue;
|
||||
@@ -393,7 +393,7 @@ static void prim_abort(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
{
|
||||
PathSet context;
|
||||
string s = state.coerceToString(pos, *args[0], context);
|
||||
throw Abort(format("evaluation aborted with the following error message: '%1%'") % s);
|
||||
throw Abort(format("evaluation aborted with the following error message: ‘%1%’") % s);
|
||||
}
|
||||
|
||||
|
||||
@@ -502,13 +502,13 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
/* Figure out the name first (for stack backtraces). */
|
||||
Bindings::iterator attr = args[0]->attrs->find(state.sName);
|
||||
if (attr == args[0]->attrs->end())
|
||||
throw EvalError(format("required attribute 'name' missing, at %1%") % pos);
|
||||
throw EvalError(format("required attribute ‘name’ missing, at %1%") % pos);
|
||||
string drvName;
|
||||
Pos & posDrvName(*attr->pos);
|
||||
try {
|
||||
drvName = state.forceStringNoCtx(*attr->value, pos);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the derivation attribute 'name' at %1%:\n") % posDrvName);
|
||||
e.addPrefix(format("while evaluating the derivation attribute ‘name’ at %1%:\n") % posDrvName);
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -540,26 +540,26 @@ 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;
|
||||
vomit("processing attribute '%1%'", key);
|
||||
vomit("processing attribute ‘%1%’", key);
|
||||
|
||||
auto handleHashMode = [&](const std::string & s) {
|
||||
if (s == "recursive") outputHashRecursive = true;
|
||||
else if (s == "flat") outputHashRecursive = false;
|
||||
else throw EvalError("invalid value '%s' for 'outputHashMode' attribute, at %s", s, posDrvName);
|
||||
else throw EvalError("invalid value ‘%s’ for ‘outputHashMode’ attribute, at %s", s, posDrvName);
|
||||
};
|
||||
|
||||
auto handleOutputs = [&](const Strings & ss) {
|
||||
outputs.clear();
|
||||
for (auto & j : ss) {
|
||||
if (outputs.find(j) != outputs.end())
|
||||
throw EvalError(format("duplicate derivation output '%1%', at %2%") % j % posDrvName);
|
||||
throw EvalError(format("duplicate derivation output ‘%1%’, at %2%") % j % posDrvName);
|
||||
/* !!! Check whether j is a valid attribute
|
||||
name. */
|
||||
/* Derivations cannot be named ‘drv’, because
|
||||
then we'd have an attribute ‘drvPath’ in
|
||||
the resulting set. */
|
||||
if (j == "drv")
|
||||
throw EvalError(format("invalid derivation output name 'drv', at %1%") % posDrvName);
|
||||
throw EvalError(format("invalid derivation output name ‘drv’, at %1%") % posDrvName);
|
||||
outputs.insert(j);
|
||||
}
|
||||
if (outputs.empty())
|
||||
@@ -622,7 +622,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
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);
|
||||
printMsg(lvlVomit, format("derivation name is ‘%1%’") % drvName);
|
||||
}
|
||||
else if (key == "outputHash") outputHash = s;
|
||||
else if (key == "outputHashAlgo") outputHashAlgo = s;
|
||||
@@ -634,7 +634,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
}
|
||||
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the attribute '%1%' of the derivation '%2%' at %3%:\n")
|
||||
e.addPrefix(format("while evaluating the attribute ‘%1%’ of the derivation ‘%2%’ at %3%:\n")
|
||||
% key % drvName % posDrvName);
|
||||
throw;
|
||||
}
|
||||
@@ -690,14 +690,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
/* Do we have all required attributes? */
|
||||
if (drv.builder == "")
|
||||
throw EvalError(format("required attribute 'builder' missing, at %1%") % posDrvName);
|
||||
throw EvalError(format("required attribute ‘builder’ missing, at %1%") % posDrvName);
|
||||
if (drv.platform == "")
|
||||
throw EvalError(format("required attribute 'system' missing, at %1%") % posDrvName);
|
||||
throw EvalError(format("required attribute ‘system’ missing, at %1%") % posDrvName);
|
||||
|
||||
/* Check whether the derivation name is valid. */
|
||||
checkStoreName(drvName);
|
||||
if (isDerivation(drvName))
|
||||
throw EvalError(format("derivation names are not allowed to end in '%1%', at %2%")
|
||||
throw EvalError(format("derivation names are not allowed to end in ‘%1%’, at %2%")
|
||||
% drvExtension % posDrvName);
|
||||
|
||||
if (outputHash) {
|
||||
@@ -707,13 +707,13 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
HashType ht = parseHashType(outputHashAlgo);
|
||||
if (ht == htUnknown)
|
||||
throw EvalError(format("unknown hash algorithm '%1%', at %2%") % outputHashAlgo % posDrvName);
|
||||
Hash h(*outputHash, ht);
|
||||
outputHash = h.to_string(Base16, false);
|
||||
throw EvalError(format("unknown hash algorithm ‘%1%’, at %2%") % outputHashAlgo % posDrvName);
|
||||
Hash h = parseHash16or32(ht, *outputHash);
|
||||
outputHash = printHash(h);
|
||||
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
|
||||
|
||||
Path outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName);
|
||||
if (!jsonObject) drv.env["out"] = outPath;
|
||||
drv.env["out"] = outPath;
|
||||
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, *outputHash);
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
an empty value. This ensures that changes in the set of
|
||||
output names do get reflected in the hash. */
|
||||
for (auto & i : outputs) {
|
||||
if (!jsonObject) drv.env[i] = "";
|
||||
drv.env[i] = "";
|
||||
drv.outputs[i] = DerivationOutput("", "", "");
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
for (auto & i : drv.outputs)
|
||||
if (i.second.path == "") {
|
||||
Path outPath = state.store->makeOutputPath(i.first, h, drvName);
|
||||
if (!jsonObject) drv.env[i.first] = outPath;
|
||||
drv.env[i.first] = outPath;
|
||||
i.second.path = outPath;
|
||||
}
|
||||
}
|
||||
@@ -743,7 +743,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
/* Write the resulting term into the Nix store directory. */
|
||||
Path drvPath = writeDerivation(state.store, drv, drvName, state.repair);
|
||||
|
||||
printMsg(lvlChatty, format("instantiated '%1%' -> '%2%'")
|
||||
printMsg(lvlChatty, format("instantiated ‘%1%’ -> ‘%2%’")
|
||||
% drvName % drvPath);
|
||||
|
||||
/* Optimisation, but required in read-only mode! because in that
|
||||
@@ -763,7 +763,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||
|
||||
/* Return a placeholder string for the specified output that will be
|
||||
substituted by the corresponding output path at build time. For
|
||||
example, 'placeholder "out"' returns the string
|
||||
example, ‘placeholder "out"’ returns the string
|
||||
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
|
||||
time, any occurence of this string in an derivation attribute will
|
||||
be replaced with the concrete path in the Nix store of the output
|
||||
@@ -805,7 +805,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V
|
||||
e.g. nix-push does the right thing. */
|
||||
if (!state.store->isStorePath(path)) path = canonPath(path, true);
|
||||
if (!state.store->isInStore(path))
|
||||
throw EvalError(format("path '%1%' is not in the Nix store, at %2%") % path % pos);
|
||||
throw EvalError(format("path ‘%1%’ is not in the Nix store, at %2%") % path % pos);
|
||||
Path path2 = state.store->toStorePath(path);
|
||||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(path2);
|
||||
@@ -819,7 +819,7 @@ static void prim_pathExists(EvalState & state, const Pos & pos, Value * * args,
|
||||
PathSet context;
|
||||
Path path = state.coerceToPath(pos, *args[0], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string '%1%' cannot refer to other paths, at %2%") % path % pos);
|
||||
throw EvalError(format("string ‘%1%’ cannot refer to other paths, at %2%") % path % pos);
|
||||
try {
|
||||
mkBool(v, pathExists(state.checkSourcePath(path)));
|
||||
} catch (SysError & e) {
|
||||
@@ -860,18 +860,18 @@ static void prim_readFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
||||
throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
string s = readFile(state.checkSourcePath(path));
|
||||
if (s.find((char) 0) != string::npos)
|
||||
throw Error(format("the contents of the file '%1%' cannot be represented as a Nix string") % path);
|
||||
throw Error(format("the contents of the file ‘%1%’ cannot be represented as a Nix string") % path);
|
||||
mkString(v, s.c_str());
|
||||
}
|
||||
|
||||
|
||||
/* Find a file in the Nix search path. Used to implement <x> paths,
|
||||
which are desugared to 'findFile __nixPath "x"'. */
|
||||
which are desugared to ‘findFile __nixPath "x"’. */
|
||||
static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
@@ -889,7 +889,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
|
||||
i = v2.attrs->find(state.symbols.create("path"));
|
||||
if (i == v2.attrs->end())
|
||||
throw EvalError(format("attribute 'path' missing, at %1%") % pos);
|
||||
throw EvalError(format("attribute ‘path’ missing, at %1%") % pos);
|
||||
|
||||
PathSet context;
|
||||
string path = state.coerceToString(pos, *i->value, context, false, false);
|
||||
@@ -897,7 +897,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||
try {
|
||||
state.realiseContext(context);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError(format("cannot find '%1%', since path '%2%' is not valid, at %3%")
|
||||
throw EvalError(format("cannot find ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
@@ -917,7 +917,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
|
||||
try {
|
||||
state.realiseContext(ctx);
|
||||
} catch (InvalidPathError & e) {
|
||||
throw EvalError(format("cannot read '%1%', since path '%2%' is not valid, at %3%")
|
||||
throw EvalError(format("cannot read ‘%1%’, since path ‘%2%’ is not valid, at %3%")
|
||||
% path % e.path % pos);
|
||||
}
|
||||
|
||||
@@ -991,7 +991,7 @@ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Valu
|
||||
if (isDerivation(path)) {
|
||||
/* See prim_unsafeDiscardOutputDependency. */
|
||||
if (path.at(0) != '~')
|
||||
throw EvalError(format("in 'toFile': the file '%1%' cannot refer to derivation outputs, at %2%") % name % pos);
|
||||
throw EvalError(format("in ‘toFile’: the file ‘%1%’ cannot refer to derivation outputs, at %2%") % name % pos);
|
||||
path = string(path, 1);
|
||||
}
|
||||
refs.insert(path);
|
||||
@@ -1009,21 +1009,22 @@ 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)
|
||||
struct FilterFromExpr : PathFilter
|
||||
{
|
||||
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);
|
||||
EvalState & state;
|
||||
Value & filter;
|
||||
Pos 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);
|
||||
FilterFromExpr(EvalState & state, Value & filter, const Pos & pos)
|
||||
: state(state), filter(filter), pos(pos)
|
||||
{
|
||||
}
|
||||
|
||||
path = state.checkSourcePath(path);
|
||||
|
||||
PathFilter filter = [&](const Path & path) {
|
||||
auto st = lstat(path);
|
||||
bool operator () (const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path ‘%1%’") % path);
|
||||
|
||||
/* Call the filter function. The first argument is the path,
|
||||
the second is a string indicating the type of the file. */
|
||||
@@ -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(filter, arg1, fun2, noPos);
|
||||
|
||||
Value arg2;
|
||||
mkString(arg2,
|
||||
@@ -1044,7 +1045,24 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
||||
state.callFunction(fun2, arg2, res, noPos);
|
||||
|
||||
return state.forceBool(res, pos);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
|
||||
FilterFromExpr filter(state, *args[0], pos);
|
||||
|
||||
path = state.checkSourcePath(path);
|
||||
|
||||
Path dstPath = settings.readOnlyMode
|
||||
? state.store->computeStorePathForPath(path, true, htSHA256, filter).first
|
||||
@@ -1101,7 +1119,7 @@ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
// !!! Should we create a symbol here or just do a lookup?
|
||||
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
||||
if (i == args[1]->attrs->end())
|
||||
throw EvalError(format("attribute '%1%' missing, at %2%") % attr % pos);
|
||||
throw EvalError(format("attribute ‘%1%’ missing, at %2%") % attr % pos);
|
||||
// !!! add to stack trace?
|
||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||
state.forceValue(*i->value);
|
||||
@@ -1181,14 +1199,14 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||
|
||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||
if (j == v2.attrs->end())
|
||||
throw TypeError(format("'name' attribute missing in a call to 'listToAttrs', at %1%") % pos);
|
||||
throw TypeError(format("‘name’ attribute missing in a call to ‘listToAttrs’, at %1%") % pos);
|
||||
string name = state.forceStringNoCtx(*j->value, pos);
|
||||
|
||||
Symbol sym = state.symbols.create(name);
|
||||
if (seen.find(sym) == seen.end()) {
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create(state.sValue));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError(format("'value' attribute missing in a call to 'listToAttrs', at %1%") % pos);
|
||||
throw TypeError(format("‘value’ attribute missing in a call to ‘listToAttrs’, at %1%") % pos);
|
||||
|
||||
v.attrs->push_back(Attr(sym, j2->value, j2->pos));
|
||||
seen.insert(sym);
|
||||
@@ -1263,7 +1281,7 @@ static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args
|
||||
{
|
||||
state.forceValue(*args[0]);
|
||||
if (args[0]->type != tLambda)
|
||||
throw TypeError(format("'functionArgs' requires a function, at %1%") % pos);
|
||||
throw TypeError(format("‘functionArgs’ requires a function, at %1%") % pos);
|
||||
|
||||
if (!args[0]->lambda.fun->matchAttrs) {
|
||||
state.mkAttrs(v, 0);
|
||||
@@ -1322,7 +1340,7 @@ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
{
|
||||
state.forceList(*args[0], pos);
|
||||
if (args[0]->listSize() == 0)
|
||||
throw Error(format("'tail' called on an empty list, at %1%") % pos);
|
||||
throw Error(format("‘tail’ called on an empty list, at %1%") % pos);
|
||||
state.mkList(v, args[0]->listSize() - 1);
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
v.listElems()[n] = args[0]->listElems()[n + 1];
|
||||
@@ -1631,7 +1649,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
|
||||
PathSet context;
|
||||
string s = state.coerceToString(pos, *args[2], context);
|
||||
|
||||
if (start < 0) throw EvalError(format("negative start position in 'substring', at %1%") % pos);
|
||||
if (start < 0) throw EvalError(format("negative start position in ‘substring’, at %1%") % pos);
|
||||
|
||||
mkString(v, (unsigned int) start >= s.size() ? "" : string(s, start, len), context);
|
||||
}
|
||||
@@ -1653,14 +1671,6 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos,
|
||||
}
|
||||
|
||||
|
||||
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
state.forceString(*args[0], context, pos);
|
||||
mkBool(v, !context.empty());
|
||||
}
|
||||
|
||||
|
||||
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
|
||||
builder without causing the derivation to be built (for instance,
|
||||
in the derivation that builds NARs in nix-push, when doing
|
||||
@@ -1686,12 +1696,12 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
|
||||
string type = state.forceStringNoCtx(*args[0], pos);
|
||||
HashType ht = parseHashType(type);
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type '%1%', at %2%") % type % pos);
|
||||
throw Error(format("unknown hash type ‘%1%’, at %2%") % type % pos);
|
||||
|
||||
PathSet context; // discarded
|
||||
string s = state.forceString(*args[1], context, pos);
|
||||
|
||||
mkString(v, hashString(ht, s).to_string(Base16, false), context);
|
||||
mkString(v, printHash(hashString(ht, s)), context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1724,80 +1734,8 @@ static void prim_match(EvalState & state, const Pos & pos, Value * * args, Value
|
||||
mkString(*(v.listElems()[i] = state.allocValue()), match[i + 1].str().c_str());
|
||||
}
|
||||
|
||||
} catch (std::regex_error &e) {
|
||||
if (e.code() == std::regex_constants::error_space) {
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
|
||||
} else {
|
||||
throw EvalError("invalid regular expression '%s', at %s", re, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Split a string with a regular expression, and return a list of the
|
||||
non-matching parts interleaved by the lists of the matching groups. */
|
||||
static void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
auto re = state.forceStringNoCtx(*args[0], pos);
|
||||
|
||||
try {
|
||||
|
||||
std::regex regex(re, std::regex::extended);
|
||||
|
||||
PathSet context;
|
||||
const std::string str = state.forceString(*args[1], context, pos);
|
||||
|
||||
auto begin = std::sregex_iterator(str.begin(), str.end(), regex);
|
||||
auto end = std::sregex_iterator();
|
||||
|
||||
// Any matches results are surrounded by non-matching results.
|
||||
const size_t len = std::distance(begin, end);
|
||||
state.mkList(v, 2 * len + 1);
|
||||
size_t idx = 0;
|
||||
Value * elem;
|
||||
|
||||
if (len == 0) {
|
||||
v.listElems()[idx++] = args[1];
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::sregex_iterator i = begin; i != end; ++i) {
|
||||
assert(idx <= 2 * len + 1 - 3);
|
||||
std::smatch match = *i;
|
||||
|
||||
// Add a string for non-matched characters.
|
||||
elem = v.listElems()[idx++] = state.allocValue();
|
||||
mkString(*elem, match.prefix().str().c_str());
|
||||
|
||||
// Add a list for matched substrings.
|
||||
const size_t slen = match.size() - 1;
|
||||
elem = v.listElems()[idx++] = state.allocValue();
|
||||
|
||||
// Start at 1, beacause the first match is the whole string.
|
||||
state.mkList(*elem, slen);
|
||||
for (size_t si = 0; si < slen; ++si) {
|
||||
if (!match[si + 1].matched)
|
||||
mkNull(*(elem->listElems()[si] = state.allocValue()));
|
||||
else
|
||||
mkString(*(elem->listElems()[si] = state.allocValue()), match[si + 1].str().c_str());
|
||||
}
|
||||
|
||||
// Add a string for non-matched suffix characters.
|
||||
if (idx == 2 * len) {
|
||||
elem = v.listElems()[idx++] = state.allocValue();
|
||||
mkString(*elem, match.suffix().str().c_str());
|
||||
}
|
||||
}
|
||||
assert(idx == 2 * len + 1);
|
||||
|
||||
} catch (std::regex_error &e) {
|
||||
if (e.code() == std::regex_constants::error_space) {
|
||||
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
|
||||
throw EvalError("memory limit exceeded by regular expression '%s', at %s", re, pos);
|
||||
} else {
|
||||
throw EvalError("invalid regular expression '%s', at %s", re, pos);
|
||||
}
|
||||
} catch (std::regex_error &) {
|
||||
throw EvalError("invalid regular expression ‘%s’, at %s", re, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1827,7 +1765,7 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
|
||||
state.forceList(*args[0], pos);
|
||||
state.forceList(*args[1], pos);
|
||||
if (args[0]->listSize() != args[1]->listSize())
|
||||
throw EvalError(format("'from' and 'to' arguments to 'replaceStrings' have different lengths, at %1%") % pos);
|
||||
throw EvalError(format("‘from’ and ‘to’ arguments to ‘replaceStrings’ have different lengths, at %1%") % pos);
|
||||
|
||||
vector<string> from;
|
||||
from.reserve(args[0]->listSize());
|
||||
@@ -1897,11 +1835,11 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a
|
||||
|
||||
|
||||
void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
const string & who, bool unpack, const std::string & defaultName)
|
||||
const string & who, bool unpack)
|
||||
{
|
||||
string url;
|
||||
Hash expectedHash;
|
||||
string name = defaultName;
|
||||
string name;
|
||||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
@@ -1914,20 +1852,21 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
if (n == "url")
|
||||
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "sha256")
|
||||
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
||||
expectedHash = parseHash16or32(htSHA256, state.forceStringNoCtx(*attr.value, *attr.pos));
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError(format("unsupported argument '%1%' to '%2%', at %3%") % attr.name % who % attr.pos);
|
||||
throw EvalError(format("unsupported argument ‘%1%’ to ‘%2%’, at %3%") % attr.name % who % attr.pos);
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
||||
throw EvalError(format("‘url’ argument required, at %1%") % pos);
|
||||
|
||||
} else
|
||||
url = state.forceStringNoCtx(*args[0], pos);
|
||||
|
||||
state.checkURI(url);
|
||||
if (state.restricted && !expectedHash)
|
||||
throw Error(format("‘%1%’ is not allowed in restricted mode") % who);
|
||||
|
||||
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
|
||||
mkString(v, res, PathSet({res}));
|
||||
@@ -1936,13 +1875,13 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
|
||||
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
fetch(state, pos, args, v, "fetchurl", false, "");
|
||||
fetch(state, pos, args, v, "fetchurl", false);
|
||||
}
|
||||
|
||||
|
||||
static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
fetch(state, pos, args, v, "fetchTarball", true, "source");
|
||||
fetch(state, pos, args, v, "fetchTarball", true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1997,7 +1936,7 @@ void EvalState::createBaseEnv()
|
||||
language feature gets added. It's not necessary to increase it
|
||||
when primops get added, because you can just use `builtins ?
|
||||
primOp' to check. */
|
||||
mkInt(v, 5);
|
||||
mkInt(v, 4);
|
||||
addConstant("__langVersion", v);
|
||||
|
||||
// Miscellaneous
|
||||
@@ -2091,12 +2030,10 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("toString", 1, prim_toString);
|
||||
addPrimOp("__substring", 3, prim_substring);
|
||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||
addPrimOp("__hasContext", 1, prim_hasContext);
|
||||
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||
addPrimOp("__hashString", 2, prim_hashString);
|
||||
addPrimOp("__match", 2, prim_match);
|
||||
addPrimOp("__split", 2, prim_split);
|
||||
addPrimOp("__concatStringsSep", 2, prim_concatStringSep);
|
||||
addPrimOp("__replaceStrings", 3, prim_replaceStrings);
|
||||
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "download.hh"
|
||||
#include "store-api.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct GitInfo
|
||||
{
|
||||
Path storePath;
|
||||
std::string rev;
|
||||
std::string shortRev;
|
||||
uint64_t revCount = 0;
|
||||
};
|
||||
|
||||
GitInfo exportGit(ref<Store> store, const std::string & uri,
|
||||
std::experimental::optional<std::string> ref, std::string rev,
|
||||
const std::string & name)
|
||||
{
|
||||
if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) {
|
||||
|
||||
bool clean = true;
|
||||
|
||||
try {
|
||||
runProgram("git", true, { "-C", uri, "diff-index", "--quiet", "HEAD", "--" });
|
||||
} catch (ExecError e) {
|
||||
if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) throw;
|
||||
clean = false;
|
||||
}
|
||||
|
||||
if (!clean) {
|
||||
|
||||
/* This is an unclean working tree. So copy all tracked
|
||||
files. */
|
||||
|
||||
GitInfo gitInfo;
|
||||
gitInfo.rev = "0000000000000000000000000000000000000000";
|
||||
gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
|
||||
|
||||
auto files = tokenizeString<std::set<std::string>>(
|
||||
runProgram("git", true, { "-C", uri, "ls-files", "-z" }), "\0"s);
|
||||
|
||||
PathFilter filter = [&](const Path & p) -> bool {
|
||||
assert(hasPrefix(p, uri));
|
||||
std::string file(p, uri.size() + 1);
|
||||
|
||||
auto st = lstat(p);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
auto prefix = file + "/";
|
||||
auto i = files.lower_bound(prefix);
|
||||
return i != files.end() && hasPrefix(*i, prefix);
|
||||
}
|
||||
|
||||
return files.count(file);
|
||||
};
|
||||
|
||||
gitInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter);
|
||||
|
||||
return gitInfo;
|
||||
}
|
||||
|
||||
// clean working tree, but no ref or rev specified. Use 'HEAD'.
|
||||
rev = chomp(runProgram("git", true, { "-C", uri, "rev-parse", "HEAD" }));
|
||||
ref = "HEAD"s;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Path cacheDir = getCacheDir() + "/nix/git";
|
||||
|
||||
if (!pathExists(cacheDir)) {
|
||||
runProgram("git", true, { "init", "--bare", cacheDir });
|
||||
}
|
||||
|
||||
std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, *ref)).to_string(Base32, false);
|
||||
|
||||
Path localRefFile = cacheDir + "/refs/heads/" + localRef;
|
||||
|
||||
bool doFetch;
|
||||
time_t now = time(0);
|
||||
/* If a rev was specified, we need to fetch if it's not in the
|
||||
repo. */
|
||||
if (rev != "") {
|
||||
try {
|
||||
runProgram("git", true, { "-C", cacheDir, "cat-file", "-e", rev });
|
||||
doFetch = false;
|
||||
} catch (ExecError & e) {
|
||||
if (WIFEXITED(e.status)) {
|
||||
doFetch = true;
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* If the local ref is older than ‘tarball-ttl’ seconds, do a
|
||||
git fetch to update the local ref to the remote ref. */
|
||||
struct stat st;
|
||||
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
|
||||
st.st_mtime <= now - settings.tarballTtl;
|
||||
}
|
||||
if (doFetch)
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Git repository '%s'", uri));
|
||||
|
||||
// FIXME: git stderr messes up our progress indicator, so
|
||||
// we're using --quiet for now. Should process its stderr.
|
||||
runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, *ref + ":" + localRef });
|
||||
|
||||
struct timeval times[2];
|
||||
times[0].tv_sec = now;
|
||||
times[0].tv_usec = 0;
|
||||
times[1].tv_sec = now;
|
||||
times[1].tv_usec = 0;
|
||||
|
||||
utimes(localRefFile.c_str(), times);
|
||||
}
|
||||
|
||||
// FIXME: check whether rev is an ancestor of ref.
|
||||
GitInfo gitInfo;
|
||||
gitInfo.rev = rev != "" ? rev : chomp(readFile(localRefFile));
|
||||
gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
|
||||
|
||||
printTalkative("using revision %s of repo '%s'", uri, gitInfo.rev);
|
||||
|
||||
std::string storeLinkName = hashString(htSHA512, name + std::string("\0"s) + gitInfo.rev).to_string(Base32, false);
|
||||
Path storeLink = cacheDir + "/" + storeLinkName + ".link";
|
||||
PathLocks storeLinkLock({storeLink}, fmt("waiting for lock on '%1%'...", storeLink)); // FIXME: broken
|
||||
|
||||
try {
|
||||
auto json = nlohmann::json::parse(readFile(storeLink));
|
||||
|
||||
assert(json["name"] == name && json["rev"] == gitInfo.rev);
|
||||
|
||||
gitInfo.storePath = json["storePath"];
|
||||
|
||||
if (store->isValidPath(gitInfo.storePath)) {
|
||||
gitInfo.revCount = json["revCount"];
|
||||
return gitInfo;
|
||||
}
|
||||
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo != ENOENT) throw;
|
||||
}
|
||||
|
||||
// FIXME: should pipe this, or find some better way to extract a
|
||||
// revision.
|
||||
auto tar = runProgram("git", true, { "-C", cacheDir, "archive", gitInfo.rev });
|
||||
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete delTmpDir(tmpDir, true);
|
||||
|
||||
runProgram("tar", true, { "x", "-C", tmpDir }, tar);
|
||||
|
||||
gitInfo.storePath = store->addToStore(name, tmpDir);
|
||||
|
||||
gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", cacheDir, "rev-list", "--count", gitInfo.rev }));
|
||||
|
||||
nlohmann::json json;
|
||||
json["storePath"] = gitInfo.storePath;
|
||||
json["uri"] = uri;
|
||||
json["name"] = name;
|
||||
json["rev"] = gitInfo.rev;
|
||||
json["revCount"] = gitInfo.revCount;
|
||||
|
||||
writeFile(storeLink, json.dump());
|
||||
|
||||
return gitInfo;
|
||||
}
|
||||
|
||||
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
std::string url;
|
||||
std::experimental::optional<std::string> ref;
|
||||
std::string rev;
|
||||
std::string name = "source";
|
||||
PathSet context;
|
||||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type == tAttrs) {
|
||||
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
string n(attr.name);
|
||||
if (n == "url")
|
||||
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
||||
else if (n == "ref")
|
||||
ref = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "rev")
|
||||
rev = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError("unsupported argument '%s' to 'fetchGit', at %s", attr.name, *attr.pos);
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
||||
|
||||
} else
|
||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||
|
||||
if (!isUri(url)) url = absPath(url);
|
||||
|
||||
// FIXME: git externals probably can be used to bypass the URI
|
||||
// whitelist. Ah well.
|
||||
state.checkURI(url);
|
||||
|
||||
auto gitInfo = exportGit(state.store, url, ref, rev, name);
|
||||
|
||||
state.mkAttrs(v, 8);
|
||||
mkString(*state.allocAttr(v, state.sOutPath), gitInfo.storePath, PathSet({gitInfo.storePath}));
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), gitInfo.rev);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
||||
static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "download.hh"
|
||||
#include "store-api.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct HgInfo
|
||||
{
|
||||
Path storePath;
|
||||
std::string branch;
|
||||
std::string rev;
|
||||
uint64_t revCount = 0;
|
||||
};
|
||||
|
||||
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 (rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.hg")) {
|
||||
|
||||
bool clean = runProgram("hg", true, { "status", "-R", uri, "--modified", "--added", "--removed" }) == "";
|
||||
|
||||
if (!clean) {
|
||||
|
||||
/* This is an unclean working tree. So copy all tracked
|
||||
files. */
|
||||
|
||||
printTalkative("copying unclean Mercurial working tree '%s'", uri);
|
||||
|
||||
HgInfo hgInfo;
|
||||
hgInfo.rev = "0000000000000000000000000000000000000000";
|
||||
hgInfo.branch = chomp(runProgram("hg", true, { "branch", "-R", uri }));
|
||||
|
||||
auto files = tokenizeString<std::set<std::string>>(
|
||||
runProgram("hg", true, { "status", "-R", uri, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
|
||||
|
||||
PathFilter filter = [&](const Path & p) -> bool {
|
||||
assert(hasPrefix(p, uri));
|
||||
std::string file(p, uri.size() + 1);
|
||||
|
||||
auto st = lstat(p);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
auto prefix = file + "/";
|
||||
auto i = files.lower_bound(prefix);
|
||||
return i != files.end() && hasPrefix(*i, prefix);
|
||||
}
|
||||
|
||||
return files.count(file);
|
||||
};
|
||||
|
||||
hgInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter);
|
||||
|
||||
return hgInfo;
|
||||
}
|
||||
}
|
||||
|
||||
if (rev == "") rev = "default";
|
||||
|
||||
Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(htSHA256, uri).to_string(Base32, false));
|
||||
|
||||
Path stampFile = fmt("%s/.hg/%s.stamp", cacheDir, hashString(htSHA512, rev).to_string(Base32, false));
|
||||
|
||||
/* If we haven't pulled this repo less than ‘tarball-ttl’ seconds,
|
||||
do so now. */
|
||||
time_t now = time(0);
|
||||
struct stat st;
|
||||
if (stat(stampFile.c_str(), &st) != 0 ||
|
||||
st.st_mtime <= now - settings.tarballTtl)
|
||||
{
|
||||
/* Except that if this is a commit hash that we already have,
|
||||
we don't have to pull again. */
|
||||
if (!(std::regex_match(rev, commitHashRegex)
|
||||
&& pathExists(cacheDir)
|
||||
&& runProgram(
|
||||
RunOptions("hg", { "log", "-R", cacheDir, "-r", rev, "--template", "1" })
|
||||
.killStderr(true)).second == "1"))
|
||||
{
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", uri));
|
||||
|
||||
if (pathExists(cacheDir)) {
|
||||
runProgram("hg", true, { "pull", "-R", cacheDir, "--", uri });
|
||||
} else {
|
||||
createDirs(dirOf(cacheDir));
|
||||
runProgram("hg", true, { "clone", "--noupdate", "--", uri, cacheDir });
|
||||
}
|
||||
}
|
||||
|
||||
writeFile(stampFile, "");
|
||||
}
|
||||
|
||||
auto tokens = tokenizeString<std::vector<std::string>>(
|
||||
runProgram("hg", true, { "log", "-R", cacheDir, "-r", rev, "--template", "{node} {rev} {branch}" }));
|
||||
assert(tokens.size() == 3);
|
||||
|
||||
HgInfo hgInfo;
|
||||
hgInfo.rev = tokens[0];
|
||||
hgInfo.revCount = std::stoull(tokens[1]);
|
||||
hgInfo.branch = tokens[2];
|
||||
|
||||
std::string storeLinkName = hashString(htSHA512, name + std::string("\0"s) + hgInfo.rev).to_string(Base32, false);
|
||||
Path storeLink = fmt("%s/.hg/%s.link", cacheDir, storeLinkName);
|
||||
|
||||
try {
|
||||
auto json = nlohmann::json::parse(readFile(storeLink));
|
||||
|
||||
assert(json["name"] == name && json["rev"] == hgInfo.rev);
|
||||
|
||||
hgInfo.storePath = json["storePath"];
|
||||
|
||||
if (store->isValidPath(hgInfo.storePath)) {
|
||||
printTalkative("using cached Mercurial store path '%s'", hgInfo.storePath);
|
||||
return hgInfo;
|
||||
}
|
||||
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo != ENOENT) throw;
|
||||
}
|
||||
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete delTmpDir(tmpDir, true);
|
||||
|
||||
runProgram("hg", true, { "archive", "-R", cacheDir, "-r", rev, tmpDir });
|
||||
|
||||
deletePath(tmpDir + "/.hg_archival.txt");
|
||||
|
||||
hgInfo.storePath = store->addToStore(name, tmpDir);
|
||||
|
||||
nlohmann::json json;
|
||||
json["storePath"] = hgInfo.storePath;
|
||||
json["uri"] = uri;
|
||||
json["name"] = name;
|
||||
json["branch"] = hgInfo.branch;
|
||||
json["rev"] = hgInfo.rev;
|
||||
json["revCount"] = hgInfo.revCount;
|
||||
|
||||
writeFile(storeLink, json.dump());
|
||||
|
||||
return hgInfo;
|
||||
}
|
||||
|
||||
static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
std::string url;
|
||||
std::string rev;
|
||||
std::string name = "source";
|
||||
PathSet context;
|
||||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type == tAttrs) {
|
||||
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
string n(attr.name);
|
||||
if (n == "url")
|
||||
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
||||
else if (n == "rev")
|
||||
rev = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError("unsupported argument '%s' to 'fetchMercurial', at %s", attr.name, *attr.pos);
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
throw EvalError(format("'url' argument required, at %1%") % pos);
|
||||
|
||||
} else
|
||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
||||
|
||||
if (!isUri(url)) url = absPath(url);
|
||||
|
||||
// FIXME: git externals probably can be used to bypass the URI
|
||||
// whitelist. Ah well.
|
||||
state.checkURI(url);
|
||||
|
||||
auto hgInfo = exportMercurial(state.store, url, rev, name);
|
||||
|
||||
state.mkAttrs(v, 8);
|
||||
mkString(*state.allocAttr(v, state.sOutPath), hgInfo.storePath, PathSet({hgInfo.storePath}));
|
||||
mkString(*state.allocAttr(v, state.symbols.create("branch")), hgInfo.branch);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), hgInfo.rev);
|
||||
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();
|
||||
}
|
||||
|
||||
static RegisterPrimOp r("fetchMercurial", 1, prim_fetchMercurial);
|
||||
|
||||
}
|
||||
84
src/libexpr/primops/fetchgit.cc
Normal file
84
src/libexpr/primops/fetchgit.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "download.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
Path exportGit(ref<Store> store, const std::string & uri, const std::string & rev)
|
||||
{
|
||||
if (!isUri(uri))
|
||||
throw EvalError(format("‘%s’ is not a valid URI") % uri);
|
||||
|
||||
Path cacheDir = getCacheDir() + "/nix/git";
|
||||
|
||||
if (!pathExists(cacheDir)) {
|
||||
createDirs(cacheDir);
|
||||
runProgram("git", true, { "init", "--bare", cacheDir });
|
||||
}
|
||||
|
||||
//Activity act(*logger, lvlInfo, format("fetching Git repository ‘%s’") % uri);
|
||||
|
||||
std::string localRef = "pid-" + std::to_string(getpid());
|
||||
Path localRefFile = cacheDir + "/refs/heads/" + localRef;
|
||||
|
||||
runProgram("git", true, { "-C", cacheDir, "fetch", uri, rev + ":" + localRef });
|
||||
|
||||
std::string commitHash = chomp(readFile(localRefFile));
|
||||
|
||||
unlink(localRefFile.c_str());
|
||||
|
||||
debug(format("got revision ‘%s’") % commitHash);
|
||||
|
||||
// FIXME: should pipe this, or find some better way to extract a
|
||||
// revision.
|
||||
auto tar = runProgram("git", true, { "-C", cacheDir, "archive", commitHash });
|
||||
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete delTmpDir(tmpDir, true);
|
||||
|
||||
runProgram("tar", true, { "x", "-C", tmpDir }, tar);
|
||||
|
||||
return store->addToStore("git-export", tmpDir);
|
||||
}
|
||||
|
||||
static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
// FIXME: cut&paste from fetch().
|
||||
if (state.restricted) throw Error("‘fetchgit’ is not allowed in restricted mode");
|
||||
|
||||
std::string url;
|
||||
std::string rev = "master";
|
||||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type == tAttrs) {
|
||||
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
string name(attr.name);
|
||||
if (name == "url") {
|
||||
PathSet context;
|
||||
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
||||
if (hasPrefix(url, "/")) url = "file://" + url;
|
||||
} else if (name == "rev")
|
||||
rev = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
else
|
||||
throw EvalError("unsupported argument ‘%s’ to ‘fetchgit’, at %s", attr.name, *attr.pos);
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
throw EvalError(format("‘url’ argument required, at %1%") % pos);
|
||||
|
||||
} else
|
||||
url = state.forceStringNoCtx(*args[0], pos);
|
||||
|
||||
Path storePath = exportGit(state.store, url, rev);
|
||||
|
||||
mkString(v, storePath, PathSet({storePath}));
|
||||
}
|
||||
|
||||
static RegisterPrimOp r("__fetchgit", 1, prim_fetchgit);
|
||||
|
||||
}
|
||||
14
src/libexpr/primops/fetchgit.hh
Normal file
14
src/libexpr/primops/fetchgit.hh
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ref.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Store;
|
||||
|
||||
Path exportGit(ref<Store> store,
|
||||
const std::string & uri, const std::string & rev);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -6,38 +6,32 @@ namespace nix {
|
||||
MixCommonArgs::MixCommonArgs(const string & programName)
|
||||
: programName(programName)
|
||||
{
|
||||
mkFlag()
|
||||
.longName("verbose")
|
||||
.shortName('v')
|
||||
.description("increase verbosity level")
|
||||
.handler([]() { verbosity = (Verbosity) (verbosity + 1); });
|
||||
mkFlag('v', "verbose", "increase verbosity level", []() {
|
||||
verbosity = (Verbosity) (verbosity + 1);
|
||||
});
|
||||
|
||||
mkFlag()
|
||||
.longName("quiet")
|
||||
.description("decrease verbosity level")
|
||||
.handler([]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; });
|
||||
mkFlag(0, "quiet", "decrease verbosity level", []() {
|
||||
verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
|
||||
});
|
||||
|
||||
mkFlag()
|
||||
.longName("debug")
|
||||
.description("enable debug output")
|
||||
.handler([]() { verbosity = lvlDebug; });
|
||||
mkFlag(0, "debug", "enable debug output", []() {
|
||||
verbosity = lvlDebug;
|
||||
});
|
||||
|
||||
mkFlag()
|
||||
.longName("option")
|
||||
.labels({"name", "value"})
|
||||
.description("set a Nix configuration option (overriding nix.conf)")
|
||||
.arity(2)
|
||||
.handler([](std::vector<std::string> ss) {
|
||||
.handler([](Strings ss) {
|
||||
auto name = ss.front(); ss.pop_front();
|
||||
auto value = ss.front();
|
||||
try {
|
||||
settings.set(ss[0], ss[1]);
|
||||
settings.set(name, value);
|
||||
} catch (UsageError & e) {
|
||||
warn(e.what());
|
||||
}
|
||||
});
|
||||
|
||||
std::string cat = "config";
|
||||
settings.convertToArgs(*this, cat);
|
||||
hiddenCategories.insert(cat);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "common-args.hh"
|
||||
#include "globals.hh"
|
||||
#include "shared.hh"
|
||||
#include "store-api.hh"
|
||||
@@ -28,45 +29,45 @@ void printGCWarning()
|
||||
if (!gcWarning) return;
|
||||
static bool haveWarned = false;
|
||||
warnOnce(haveWarned,
|
||||
"you did not specify '--add-root'; "
|
||||
"you did not specify ‘--add-root’; "
|
||||
"the result might be removed by the garbage collector");
|
||||
}
|
||||
|
||||
|
||||
void printMissing(ref<Store> store, const PathSet & paths, Verbosity lvl)
|
||||
void printMissing(ref<Store> store, const PathSet & paths)
|
||||
{
|
||||
unsigned long long downloadSize, narSize;
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
store->queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize, lvl);
|
||||
printMissing(store, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
}
|
||||
|
||||
|
||||
void printMissing(ref<Store> store, const PathSet & willBuild,
|
||||
const PathSet & willSubstitute, const PathSet & unknown,
|
||||
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl)
|
||||
unsigned long long downloadSize, unsigned long long narSize)
|
||||
{
|
||||
if (!willBuild.empty()) {
|
||||
printMsg(lvl, "these derivations will be built:");
|
||||
printInfo(format("these derivations will be built:"));
|
||||
Paths sorted = store->topoSortPaths(willBuild);
|
||||
reverse(sorted.begin(), sorted.end());
|
||||
for (auto & i : sorted)
|
||||
printMsg(lvl, fmt(" %s", i));
|
||||
printInfo(format(" %1%") % i);
|
||||
}
|
||||
|
||||
if (!willSubstitute.empty()) {
|
||||
printMsg(lvl, fmt("these paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||
downloadSize / (1024.0 * 1024.0),
|
||||
narSize / (1024.0 * 1024.0)));
|
||||
printInfo(format("these paths will be fetched (%.2f MiB download, %.2f MiB unpacked):")
|
||||
% (downloadSize / (1024.0 * 1024.0))
|
||||
% (narSize / (1024.0 * 1024.0)));
|
||||
for (auto & i : willSubstitute)
|
||||
printMsg(lvl, fmt(" %s", i));
|
||||
printInfo(format(" %1%") % i);
|
||||
}
|
||||
|
||||
if (!unknown.empty()) {
|
||||
printMsg(lvl, fmt("don't know how to build these paths%s:",
|
||||
(settings.readOnlyMode ? " (may be caused by read-only store access)" : "")));
|
||||
printInfo(format("don't know how to build these paths%1%:")
|
||||
% (settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||
for (auto & i : unknown)
|
||||
printMsg(lvl, fmt(" %s", i));
|
||||
printInfo(format(" %1%") % i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ string getArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end)
|
||||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
|
||||
if (i == end) throw UsageError(format("‘%1%’ requires an argument") % opt);
|
||||
return *i;
|
||||
}
|
||||
|
||||
@@ -148,97 +149,80 @@ void initNix()
|
||||
}
|
||||
|
||||
|
||||
LegacyArgs::LegacyArgs(const std::string & programName,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
|
||||
: MixCommonArgs(programName), parseArg(parseArg)
|
||||
struct LegacyArgs : public MixCommonArgs
|
||||
{
|
||||
mkFlag()
|
||||
.longName("no-build-output")
|
||||
.shortName('Q')
|
||||
.description("do not show build output")
|
||||
.set(&settings.verboseBuild, false);
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg;
|
||||
|
||||
mkFlag()
|
||||
.longName("keep-failed")
|
||||
.shortName('K')
|
||||
.description("keep temporary directories of failed builds")
|
||||
.set(&(bool&) settings.keepFailed, true);
|
||||
LegacyArgs(const std::string & programName,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
|
||||
: MixCommonArgs(programName), parseArg(parseArg)
|
||||
{
|
||||
mkFlag('Q', "no-build-output", "do not show build output",
|
||||
&settings.verboseBuild, false);
|
||||
|
||||
mkFlag()
|
||||
.longName("keep-going")
|
||||
.shortName('k')
|
||||
.description("keep going after a build fails")
|
||||
.set(&(bool&) settings.keepGoing, true);
|
||||
mkFlag('K', "keep-failed", "keep temporary directories of failed builds",
|
||||
&(bool&) settings.keepFailed);
|
||||
|
||||
mkFlag()
|
||||
.longName("fallback")
|
||||
.description("build from source if substitution fails")
|
||||
.set(&(bool&) settings.tryFallback, true);
|
||||
mkFlag('k', "keep-going", "keep going after a build fails",
|
||||
&(bool&) settings.keepGoing);
|
||||
|
||||
mkFlag1('j', "max-jobs", "jobs", "maximum number of parallel builds", [=](std::string s) {
|
||||
settings.set("max-jobs", s);
|
||||
});
|
||||
|
||||
auto intSettingAlias = [&](char shortName, const std::string & longName,
|
||||
const std::string & description, const std::string & dest) {
|
||||
mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
|
||||
settings.set(dest, std::to_string(n));
|
||||
mkFlag(0, "fallback", "build from source if substitution fails", []() {
|
||||
settings.tryFallback = true;
|
||||
});
|
||||
};
|
||||
|
||||
intSettingAlias(0, "cores", "maximum number of CPU cores to use inside a build", "cores");
|
||||
intSettingAlias(0, "max-silent-time", "number of seconds of silence before a build is killed", "max-silent-time");
|
||||
intSettingAlias(0, "timeout", "number of seconds before a build is killed", "timeout");
|
||||
mkFlag1('j', "max-jobs", "jobs", "maximum number of parallel builds", [=](std::string s) {
|
||||
settings.set("build-max-jobs", s);
|
||||
});
|
||||
|
||||
mkFlag(0, "readonly-mode", "do not write to the Nix store",
|
||||
&settings.readOnlyMode);
|
||||
auto intSettingAlias = [&](char shortName, const std::string & longName,
|
||||
const std::string & description, const std::string & dest) {
|
||||
mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
|
||||
settings.set(dest, std::to_string(n));
|
||||
});
|
||||
};
|
||||
|
||||
mkFlag(0, "show-trace", "show Nix expression stack trace in evaluation errors",
|
||||
&settings.showTrace);
|
||||
intSettingAlias(0, "cores", "maximum number of CPU cores to use inside a build", "build-cores");
|
||||
intSettingAlias(0, "max-silent-time", "number of seconds of silence before a build is killed", "build-max-silent-time");
|
||||
intSettingAlias(0, "timeout", "number of seconds before a build is killed", "build-timeout");
|
||||
|
||||
mkFlag(0, "no-gc-warning", "disable warning about not using '--add-root'",
|
||||
&gcWarning, false);
|
||||
mkFlag(0, "readonly-mode", "do not write to the Nix store",
|
||||
&settings.readOnlyMode);
|
||||
|
||||
mkFlag()
|
||||
.longName("store")
|
||||
.label("store-uri")
|
||||
.description("URI of the Nix store to use")
|
||||
.dest(&(std::string&) settings.storeUri);
|
||||
}
|
||||
mkFlag(0, "no-build-hook", "disable use of the build hook mechanism",
|
||||
&(bool&) settings.useBuildHook, false);
|
||||
|
||||
mkFlag(0, "show-trace", "show Nix expression stack trace in evaluation errors",
|
||||
&settings.showTrace);
|
||||
|
||||
bool LegacyArgs::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
{
|
||||
if (MixCommonArgs::processFlag(pos, end)) return true;
|
||||
bool res = parseArg(pos, end);
|
||||
if (res) ++pos;
|
||||
return res;
|
||||
}
|
||||
mkFlag(0, "no-gc-warning", "disable warning about not using ‘--add-root’",
|
||||
&gcWarning, false);
|
||||
}
|
||||
|
||||
bool processFlag(Strings::iterator & pos, Strings::iterator end) override
|
||||
{
|
||||
if (MixCommonArgs::processFlag(pos, end)) return true;
|
||||
bool res = parseArg(pos, end);
|
||||
if (res) ++pos;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool LegacyArgs::processArgs(const Strings & args, bool finish)
|
||||
{
|
||||
if (args.empty()) return true;
|
||||
assert(args.size() == 1);
|
||||
Strings ss(args);
|
||||
auto pos = ss.begin();
|
||||
if (!parseArg(pos, ss.end()))
|
||||
throw UsageError(format("unexpected argument '%1%'") % args.front());
|
||||
return true;
|
||||
}
|
||||
bool processArgs(const Strings & args, bool finish) override
|
||||
{
|
||||
if (args.empty()) return true;
|
||||
assert(args.size() == 1);
|
||||
Strings ss(args);
|
||||
auto pos = ss.begin();
|
||||
if (!parseArg(pos, ss.end()))
|
||||
throw UsageError(format("unexpected argument ‘%1%’") % args.front());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void parseCmdLine(int argc, char * * argv,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
|
||||
{
|
||||
parseCmdLine(baseNameOf(argv[0]), argvToStrings(argc, argv), parseArg);
|
||||
}
|
||||
|
||||
|
||||
void parseCmdLine(const string & programName, const Strings & args,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
|
||||
{
|
||||
LegacyArgs(programName, parseArg).parseCmdline(args);
|
||||
LegacyArgs(baseNameOf(argv[0]), parseArg).parseCmdline(argvToStrings(argc, argv));
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +250,7 @@ void showManPage(const string & name)
|
||||
{
|
||||
restoreSignals();
|
||||
execlp("man", "man", name.c_str(), NULL);
|
||||
throw SysError(format("command 'man %1%' failed") % name.c_str());
|
||||
throw SysError(format("command ‘man %1%’ failed") % name.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -290,13 +274,13 @@ int handleExceptions(const string & programName, std::function<void()> fun)
|
||||
return e.status;
|
||||
} catch (UsageError & e) {
|
||||
printError(
|
||||
format(error + "%1%\nTry '%2% --help' for more information.")
|
||||
format(error + "%1%\nTry ‘%2% --help’ for more information.")
|
||||
% e.what() % programName);
|
||||
return 1;
|
||||
} catch (BaseError & e) {
|
||||
printError(format(error + "%1%%2%") % (settings.showTrace ? e.prefix() : "") % e.msg());
|
||||
if (e.prefix() != "" && !settings.showTrace)
|
||||
printError("(use '--show-trace' to show detailed location information)");
|
||||
printError("(use ‘--show-trace’ to show detailed location information)");
|
||||
return e.status;
|
||||
} catch (std::bad_alloc & e) {
|
||||
printError(error + "out of memory");
|
||||
@@ -331,7 +315,7 @@ RunPager::RunPager()
|
||||
execlp("pager", "pager", NULL);
|
||||
execlp("less", "less", NULL);
|
||||
execlp("more", "more", NULL);
|
||||
throw SysError(format("executing '%1%'") % pager);
|
||||
throw SysError(format("executing ‘%1%’") % pager);
|
||||
});
|
||||
|
||||
pid.setKillSignal(SIGINT);
|
||||
@@ -369,6 +353,5 @@ PrintFreed::~PrintFreed()
|
||||
% showBytes(results.bytesFreed);
|
||||
}
|
||||
|
||||
Exit::~Exit() { }
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "util.hh"
|
||||
#include "args.hh"
|
||||
#include "common-args.hh"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
@@ -17,7 +16,6 @@ public:
|
||||
int status;
|
||||
Exit() : status(0) { }
|
||||
Exit(int status) : status(status) { }
|
||||
virtual ~Exit();
|
||||
};
|
||||
|
||||
int handleExceptions(const string & programName, std::function<void()> fun);
|
||||
@@ -27,9 +25,6 @@ void initNix();
|
||||
void parseCmdLine(int argc, char * * argv,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);
|
||||
|
||||
void parseCmdLine(const string & programName, const Strings & args,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);
|
||||
|
||||
void printVersion(const string & programName);
|
||||
|
||||
/* Ugh. No better place to put this. */
|
||||
@@ -37,11 +32,11 @@ void printGCWarning();
|
||||
|
||||
class Store;
|
||||
|
||||
void printMissing(ref<Store> store, const PathSet & paths, Verbosity lvl = lvlInfo);
|
||||
void printMissing(ref<Store> store, const PathSet & paths);
|
||||
|
||||
void printMissing(ref<Store> store, const PathSet & willBuild,
|
||||
const PathSet & willSubstitute, const PathSet & unknown,
|
||||
unsigned long long downloadSize, unsigned long long narSize, Verbosity lvl = lvlInfo);
|
||||
unsigned long long downloadSize, unsigned long long narSize);
|
||||
|
||||
string getArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end);
|
||||
@@ -50,7 +45,7 @@ template<class N> N getIntArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end, bool allowUnit)
|
||||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError(format("'%1%' requires an argument") % opt);
|
||||
if (i == end) throw UsageError(format("‘%1%’ requires an argument") % opt);
|
||||
string s = *i;
|
||||
N multiplier = 1;
|
||||
if (allowUnit && !s.empty()) {
|
||||
@@ -60,30 +55,17 @@ template<class N> N getIntArg(const string & opt,
|
||||
else if (u == 'M') multiplier = 1ULL << 20;
|
||||
else if (u == 'G') multiplier = 1ULL << 30;
|
||||
else if (u == 'T') multiplier = 1ULL << 40;
|
||||
else throw UsageError(format("invalid unit specifier '%1%'") % u);
|
||||
else throw UsageError(format("invalid unit specifier ‘%1%’") % u);
|
||||
s.resize(s.size() - 1);
|
||||
}
|
||||
}
|
||||
N n;
|
||||
if (!string2Int(s, n))
|
||||
throw UsageError(format("'%1%' requires an integer argument") % opt);
|
||||
throw UsageError(format("‘%1%’ requires an integer argument") % opt);
|
||||
return n * multiplier;
|
||||
}
|
||||
|
||||
|
||||
struct LegacyArgs : public MixCommonArgs
|
||||
{
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg;
|
||||
|
||||
LegacyArgs(const std::string & programName,
|
||||
std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg);
|
||||
|
||||
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
|
||||
|
||||
bool processArgs(const Strings & args, bool finish) override;
|
||||
};
|
||||
|
||||
|
||||
/* Show the manual page for the specified program. */
|
||||
void showManPage(const string & name);
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
|
||||
bool haveSP = true;
|
||||
char * sp = 0;
|
||||
#if defined(__x86_64__) && defined(REG_RSP)
|
||||
sp = (char *) ((ucontext_t *) ctx)->uc_mcontext.gregs[REG_RSP];
|
||||
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_RSP];
|
||||
#elif defined(REG_ESP)
|
||||
sp = (char *) ((ucontext_t *) ctx)->uc_mcontext.gregs[REG_ESP];
|
||||
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_ESP];
|
||||
#else
|
||||
haveSP = false;
|
||||
#endif
|
||||
@@ -52,8 +52,7 @@ void detectStackOverflow()
|
||||
delivered when we're out of stack space. */
|
||||
stack_t stack;
|
||||
stack.ss_size = 4096 * 4 + MINSIGSTKSZ;
|
||||
static auto stackBuf = std::make_unique<std::vector<char>>(stack.ss_size);
|
||||
stack.ss_sp = stackBuf->data();
|
||||
stack.ss_sp = new char[stack.ss_size];
|
||||
if (!stack.ss_sp) throw Error("cannot allocate alternative stack");
|
||||
stack.ss_flags = 0;
|
||||
if (sigaltstack(&stack, 0) == -1) throw SysError("cannot set alternative stack");
|
||||
|
||||
@@ -17,6 +17,66 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Given requests for a path /nix/store/<x>/<y>, this accessor will
|
||||
first download the NAR for /nix/store/<x> from the binary cache,
|
||||
build a NAR accessor for that NAR, and use that to access <y>. */
|
||||
struct BinaryCacheStoreAccessor : public FSAccessor
|
||||
{
|
||||
ref<BinaryCacheStore> store;
|
||||
|
||||
std::map<Path, ref<FSAccessor>> nars;
|
||||
|
||||
BinaryCacheStoreAccessor(ref<BinaryCacheStore> store)
|
||||
: store(store)
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<ref<FSAccessor>, Path> fetch(const Path & path_)
|
||||
{
|
||||
auto path = canonPath(path_);
|
||||
|
||||
auto storePath = store->toStorePath(path);
|
||||
std::string restPath = std::string(path, storePath.size());
|
||||
|
||||
if (!store->isValidPath(storePath))
|
||||
throw InvalidPath(format("path ‘%1%’ is not a valid store path") % storePath);
|
||||
|
||||
auto i = nars.find(storePath);
|
||||
if (i != nars.end()) return {i->second, restPath};
|
||||
|
||||
StringSink sink;
|
||||
store->narFromPath(storePath, sink);
|
||||
|
||||
auto accessor = makeNarAccessor(sink.s);
|
||||
nars.emplace(storePath, accessor);
|
||||
return {accessor, restPath};
|
||||
}
|
||||
|
||||
Stat stat(const Path & path) override
|
||||
{
|
||||
auto res = fetch(path);
|
||||
return res.first->stat(res.second);
|
||||
}
|
||||
|
||||
StringSet readDirectory(const Path & path) override
|
||||
{
|
||||
auto res = fetch(path);
|
||||
return res.first->readDirectory(res.second);
|
||||
}
|
||||
|
||||
std::string readFile(const Path & path) override
|
||||
{
|
||||
auto res = fetch(path);
|
||||
return res.first->readFile(res.second);
|
||||
}
|
||||
|
||||
std::string readLink(const Path & path) override
|
||||
{
|
||||
auto res = fetch(path);
|
||||
return res.first->readLink(res.second);
|
||||
}
|
||||
};
|
||||
|
||||
BinaryCacheStore::BinaryCacheStore(const Params & params)
|
||||
: Store(params)
|
||||
{
|
||||
@@ -43,7 +103,7 @@ void BinaryCacheStore::init()
|
||||
auto value = trim(line.substr(colon + 1, std::string::npos));
|
||||
if (name == "StoreDir") {
|
||||
if (value != storeDir)
|
||||
throw Error(format("binary cache '%s' is for Nix stores with prefix '%s', not '%s'")
|
||||
throw Error(format("binary cache ‘%s’ is for Nix stores with prefix ‘%s’, not ‘%s’")
|
||||
% getUri() % value % storeDir);
|
||||
} else if (name == "WantMassQuery") {
|
||||
wantMassQuery_ = value == "1";
|
||||
@@ -73,25 +133,8 @@ Path BinaryCacheStore::narInfoFileFor(const Path & storePath)
|
||||
return storePathToHash(storePath) + ".narinfo";
|
||||
}
|
||||
|
||||
void BinaryCacheStore::writeNarInfo(ref<NarInfo> narInfo)
|
||||
{
|
||||
auto narInfoFile = narInfoFileFor(narInfo->path);
|
||||
|
||||
upsertFile(narInfoFile, narInfo->to_string(), "text/x-nix-narinfo");
|
||||
|
||||
auto hashPart = storePathToHash(narInfo->path);
|
||||
|
||||
{
|
||||
auto state_(state.lock());
|
||||
state_->pathInfoCache.upsert(hashPart, std::shared_ptr<NarInfo>(narInfo));
|
||||
}
|
||||
|
||||
if (diskCache)
|
||||
diskCache->upsertNarInfo(getUri(), hashPart, std::shared_ptr<NarInfo>(narInfo));
|
||||
}
|
||||
|
||||
void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
if (!repair && isValidPath(info.path)) return;
|
||||
|
||||
@@ -102,10 +145,12 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
if (ref != info.path)
|
||||
queryPathInfo(ref);
|
||||
} catch (InvalidPath &) {
|
||||
throw Error(format("cannot add '%s' to the binary cache because the reference '%s' is not valid")
|
||||
throw Error(format("cannot add ‘%s’ to the binary cache because the reference ‘%s’ is not valid")
|
||||
% info.path % ref);
|
||||
}
|
||||
|
||||
auto narInfoFile = narInfoFileFor(info.path);
|
||||
|
||||
assert(nar->compare(0, narMagic.size(), narMagic) == 0);
|
||||
|
||||
auto narInfo = make_ref<NarInfo>(info);
|
||||
@@ -114,9 +159,9 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
narInfo->narHash = hashString(htSHA256, *nar);
|
||||
|
||||
if (info.narHash && info.narHash != narInfo->narHash)
|
||||
throw Error(format("refusing to copy corrupted path '%1%' to binary cache") % info.path);
|
||||
throw Error(format("refusing to copy corrupted path ‘%1%’ to binary cache") % info.path);
|
||||
|
||||
auto accessor_ = std::dynamic_pointer_cast<RemoteFSAccessor>(accessor);
|
||||
auto accessor_ = std::dynamic_pointer_cast<BinaryCacheStoreAccessor>(accessor);
|
||||
|
||||
/* Optionally write a JSON file containing a listing of the
|
||||
contents of the NAR. */
|
||||
@@ -130,11 +175,44 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
auto narAccessor = makeNarAccessor(nar);
|
||||
|
||||
if (accessor_)
|
||||
accessor_->addToCache(info.path, *nar, narAccessor);
|
||||
accessor_->nars.emplace(info.path, narAccessor);
|
||||
|
||||
std::function<void(const Path &, JSONPlaceholder &)> recurse;
|
||||
|
||||
recurse = [&](const Path & path, JSONPlaceholder & res) {
|
||||
auto st = narAccessor->stat(path);
|
||||
|
||||
auto obj = res.object();
|
||||
|
||||
switch (st.type) {
|
||||
case FSAccessor::Type::tRegular:
|
||||
obj.attr("type", "regular");
|
||||
obj.attr("size", st.fileSize);
|
||||
if (st.isExecutable)
|
||||
obj.attr("executable", true);
|
||||
break;
|
||||
case FSAccessor::Type::tDirectory:
|
||||
obj.attr("type", "directory");
|
||||
{
|
||||
auto res2 = obj.object("entries");
|
||||
for (auto & name : narAccessor->readDirectory(path)) {
|
||||
auto res3 = res2.placeholder(name);
|
||||
recurse(path + "/" + name, res3);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FSAccessor::Type::tSymlink:
|
||||
obj.attr("type", "symlink");
|
||||
obj.attr("target", narAccessor->readLink(path));
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
auto res = jsonRoot.placeholder("root");
|
||||
listNar(res, narAccessor, "", true);
|
||||
recurse("", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +221,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
|
||||
else {
|
||||
if (accessor_)
|
||||
accessor_->addToCache(info.path, *nar, makeNarAccessor(nar));
|
||||
accessor_->nars.emplace(info.path, makeNarAccessor(nar));
|
||||
}
|
||||
|
||||
/* Compress the NAR. */
|
||||
@@ -155,13 +233,13 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
narInfo->fileSize = narCompressed->size();
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
|
||||
printMsg(lvlTalkative, format("copying path '%1%' (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache")
|
||||
printMsg(lvlTalkative, format("copying path ‘%1%’ (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache")
|
||||
% narInfo->path % narInfo->narSize
|
||||
% ((1.0 - (double) narCompressed->size() / nar->size()) * 100.0)
|
||||
% duration);
|
||||
|
||||
/* Atomically write the NAR file. */
|
||||
narInfo->url = "nar/" + narInfo->fileHash.to_string(Base32, false) + ".nar"
|
||||
narInfo->url = "nar/" + printHash32(narInfo->fileHash) + ".nar"
|
||||
+ (compression == "xz" ? ".xz" :
|
||||
compression == "bzip2" ? ".bz2" :
|
||||
compression == "br" ? ".br" :
|
||||
@@ -179,7 +257,17 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, const ref<std::str
|
||||
/* Atomically write the NAR info file.*/
|
||||
if (secretKey) narInfo->sign(*secretKey);
|
||||
|
||||
writeNarInfo(narInfo);
|
||||
upsertFile(narInfoFile, narInfo->to_string(), "text/x-nix-narinfo");
|
||||
|
||||
auto hashPart = storePathToHash(narInfo->path);
|
||||
|
||||
{
|
||||
auto state_(state.lock());
|
||||
state_->pathInfoCache.upsert(hashPart, std::shared_ptr<NarInfo>(narInfo));
|
||||
}
|
||||
|
||||
if (diskCache)
|
||||
diskCache->upsertNarInfo(getUri(), hashPart, std::shared_ptr<NarInfo>(narInfo));
|
||||
|
||||
stats.narInfoWrite++;
|
||||
}
|
||||
@@ -198,7 +286,7 @@ void BinaryCacheStore::narFromPath(const Path & storePath, Sink & sink)
|
||||
|
||||
auto nar = getFile(info->url);
|
||||
|
||||
if (!nar) throw Error(format("file '%s' missing from binary cache") % info->url);
|
||||
if (!nar) throw Error(format("file ‘%s’ missing from binary cache") % info->url);
|
||||
|
||||
stats.narRead++;
|
||||
stats.narReadCompressedBytes += nar->size();
|
||||
@@ -208,13 +296,13 @@ void BinaryCacheStore::narFromPath(const Path & storePath, Sink & sink)
|
||||
try {
|
||||
nar = decompress(info->compression, *nar);
|
||||
} catch (UnknownCompressionMethod &) {
|
||||
throw Error(format("binary cache path '%s' uses unknown compression method '%s'")
|
||||
throw Error(format("binary cache path ‘%s’ uses unknown compression method ‘%s’")
|
||||
% storePath % info->compression);
|
||||
}
|
||||
|
||||
stats.narReadBytes += nar->size();
|
||||
|
||||
printMsg(lvlTalkative, format("exporting path '%1%' (%2% bytes)") % storePath % nar->size());
|
||||
printMsg(lvlTalkative, format("exporting path ‘%1%’ (%2% bytes)") % storePath % nar->size());
|
||||
|
||||
assert(nar->size() % 8 == 0);
|
||||
|
||||
@@ -225,11 +313,6 @@ void BinaryCacheStore::queryPathInfoUncached(const Path & storePath,
|
||||
std::function<void(std::shared_ptr<ValidPathInfo>)> success,
|
||||
std::function<void(std::exception_ptr exc)> failure)
|
||||
{
|
||||
auto uri = getUri();
|
||||
auto act = std::make_shared<Activity>(*logger, lvlTalkative, actQueryPathInfo,
|
||||
fmt("querying info about '%s' on '%s'", storePath, uri), Logger::Fields{storePath, uri});
|
||||
PushActivity pact(act->id);
|
||||
|
||||
auto narInfoFile = narInfoFileFor(storePath);
|
||||
|
||||
getFile(narInfoFile,
|
||||
@@ -240,14 +323,12 @@ void BinaryCacheStore::queryPathInfoUncached(const Path & storePath,
|
||||
|
||||
callSuccess(success, failure, (std::shared_ptr<ValidPathInfo>)
|
||||
std::make_shared<NarInfo>(*this, *data, narInfoFile));
|
||||
|
||||
(void) act; // force Activity into this lambda to ensure it stays alive
|
||||
},
|
||||
failure);
|
||||
}
|
||||
|
||||
Path BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
|
||||
{
|
||||
// FIXME: some cut&paste from LocalStore::addToStore().
|
||||
|
||||
@@ -268,13 +349,13 @@ Path BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
|
||||
ValidPathInfo info;
|
||||
info.path = makeFixedOutputPath(recursive, h, name);
|
||||
|
||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||
addToStore(info, sink.s, repair, false, 0);
|
||||
|
||||
return info.path;
|
||||
}
|
||||
|
||||
Path BinaryCacheStore::addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references, RepairFlag repair)
|
||||
const PathSet & references, bool repair)
|
||||
{
|
||||
ValidPathInfo info;
|
||||
info.path = computeStorePathForText(name, s, references);
|
||||
@@ -283,7 +364,7 @@ Path BinaryCacheStore::addTextToStore(const string & name, const string & s,
|
||||
if (repair || !isValidPath(info.path)) {
|
||||
StringSink sink;
|
||||
dumpString(s, sink);
|
||||
addToStore(info, sink.s, repair, CheckSigs, nullptr);
|
||||
addToStore(info, sink.s, repair, false, 0);
|
||||
}
|
||||
|
||||
return info.path;
|
||||
@@ -291,23 +372,7 @@ Path BinaryCacheStore::addTextToStore(const string & name, const string & s,
|
||||
|
||||
ref<FSAccessor> BinaryCacheStore::getFSAccessor()
|
||||
{
|
||||
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()), localNarCache);
|
||||
}
|
||||
|
||||
void BinaryCacheStore::addSignatures(const Path & storePath, const StringSet & sigs)
|
||||
{
|
||||
/* Note: this is inherently racy since there is no locking on
|
||||
binary caches. In particular, with S3 this unreliable, even
|
||||
when addSignatures() is called sequentially on a path, because
|
||||
S3 might return an outdated cached version. */
|
||||
|
||||
auto narInfo = make_ref<NarInfo>((NarInfo &) *queryPathInfo(storePath));
|
||||
|
||||
narInfo->sigs.insert(sigs.begin(), sigs.end());
|
||||
|
||||
auto narInfoFile = narInfoFileFor(narInfo->path);
|
||||
|
||||
writeNarInfo(narInfo);
|
||||
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()));
|
||||
}
|
||||
|
||||
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const Path & path)
|
||||
@@ -329,7 +394,7 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const Path & path)
|
||||
|
||||
auto logPath = "log/" + baseNameOf(drvPath);
|
||||
|
||||
debug("fetching build log from binary cache '%s/%s'", getUri(), logPath);
|
||||
debug("fetching build log from binary cache ‘%s/%s’", getUri(), logPath);
|
||||
|
||||
return getFile(logPath);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
const Setting<std::string> compression{this, "xz", "compression", "NAR compression method ('xz', 'bzip2', or 'none')"};
|
||||
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"};
|
||||
|
||||
private:
|
||||
|
||||
@@ -59,8 +58,6 @@ private:
|
||||
|
||||
std::string narInfoFileFor(const Path & storePath);
|
||||
|
||||
void writeNarInfo(ref<NarInfo> narInfo);
|
||||
|
||||
public:
|
||||
|
||||
bool isValidPathUncached(const Path & path) override;
|
||||
@@ -88,15 +85,15 @@ public:
|
||||
bool wantMassQuery() override { return wantMassQuery_; }
|
||||
|
||||
void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
bool repair, bool dontCheckSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override;
|
||||
|
||||
Path addToStore(const string & name, const Path & srcPath,
|
||||
bool recursive, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override;
|
||||
PathFilter & filter, bool repair) override;
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references, RepairFlag repair) override;
|
||||
const PathSet & references, bool repair) override;
|
||||
|
||||
void narFromPath(const Path & path, Sink & sink) override;
|
||||
|
||||
@@ -121,12 +118,11 @@ public:
|
||||
|
||||
ref<FSAccessor> getFSAccessor() override;
|
||||
|
||||
void addSignatures(const Path & storePath, const StringSet & sigs) override;
|
||||
void addSignatures(const Path & storePath, const StringSet & sigs) override
|
||||
{ unsupported(); }
|
||||
|
||||
std::shared_ptr<std::string> getBuildLog(const Path & path) override;
|
||||
|
||||
int getPriority() override { return priority; }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
|
||||
auto getAttr = [&](const string & name) {
|
||||
auto i = drv.env.find(name);
|
||||
if (i == drv.env.end()) throw Error(format("attribute '%s' missing") % name);
|
||||
if (i == drv.env.end()) throw Error(format("attribute ‘%s’ missing") % name);
|
||||
return i->second;
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
the result anyway. */
|
||||
DownloadRequest request(url);
|
||||
request.verifyTLS = false;
|
||||
request.decompress = false;
|
||||
|
||||
/* Note: have to use a fresh downloader here because we're in
|
||||
a forked process. */
|
||||
@@ -39,16 +38,12 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
|
||||
std::shared_ptr<std::string> data;
|
||||
|
||||
if (getAttr("outputHashMode") == "flat")
|
||||
for (auto hashedMirror : settings.hashedMirrors.get())
|
||||
try {
|
||||
if (!hasSuffix(hashedMirror, "/")) hashedMirror += '/';
|
||||
auto ht = parseHashType(getAttr("outputHashAlgo"));
|
||||
data = fetch(hashedMirror + printHashType(ht) + "/" + Hash(getAttr("outputHash"), ht).to_string(Base16, false));
|
||||
break;
|
||||
} catch (Error & e) {
|
||||
debug(e.what());
|
||||
}
|
||||
try {
|
||||
if (getAttr("outputHashMode") == "flat")
|
||||
data = fetch("http://tarballs.nixos.org/" + getAttr("outputHashAlgo") + "/" + getAttr("outputHash"));
|
||||
} catch (Error & e) {
|
||||
debug(e.what());
|
||||
}
|
||||
|
||||
if (!data) data = fetch(getAttr("url"));
|
||||
|
||||
@@ -66,7 +61,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
|
||||
auto executable = drv.env.find("executable");
|
||||
if (executable != drv.env.end() && executable->second == "1") {
|
||||
if (chmod(storePath.c_str(), 0755) == -1)
|
||||
throw SysError(format("making '%1%' executable") % storePath);
|
||||
throw SysError(format("making ‘%1%’ executable") % storePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ PublicKeys getDefaultPublicKeys()
|
||||
|
||||
// FIXME: filter duplicates
|
||||
|
||||
for (auto s : settings.trustedPublicKeys.get()) {
|
||||
for (auto s : settings.binaryCachePublicKeys.get()) {
|
||||
PublicKey key(s);
|
||||
publicKeys.emplace(key.name, key);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ void DerivationOutput::parseHashInfo(bool & recursive, Hash & hash) const
|
||||
|
||||
HashType hashType = parseHashType(algo);
|
||||
if (hashType == htUnknown)
|
||||
throw Error(format("unknown hash algorithm '%1%'") % algo);
|
||||
throw Error(format("unknown hash algorithm ‘%1%’") % algo);
|
||||
|
||||
hash = Hash(this->hash, hashType);
|
||||
hash = parseHash(hashType, this->hash);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Path BasicDerivation::findOutput(const string & id) const
|
||||
{
|
||||
auto i = outputs.find(id);
|
||||
if (i == outputs.end())
|
||||
throw Error(format("derivation has no output '%1%'") % id);
|
||||
throw Error(format("derivation has no output ‘%1%’") % id);
|
||||
return i->second.path;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ bool BasicDerivation::canBuildLocally() const
|
||||
|
||||
|
||||
Path writeDerivation(ref<Store> store,
|
||||
const Derivation & drv, const string & name, RepairFlag repair)
|
||||
const Derivation & drv, const string & name, bool repair)
|
||||
{
|
||||
PathSet references;
|
||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
@@ -94,7 +94,7 @@ static void expect(std::istream & str, const string & s)
|
||||
char s2[s.size()];
|
||||
str.read(s2, s.size());
|
||||
if (string(s2, s.size()) != s)
|
||||
throw FormatError(format("expected string '%1%'") % s);
|
||||
throw FormatError(format("expected string ‘%1%’") % s);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ static Path parsePath(std::istream & str)
|
||||
{
|
||||
string s = parseString(str);
|
||||
if (s.size() == 0 || s[0] != '/')
|
||||
throw FormatError(format("bad path '%1%' in derivation") % s);
|
||||
throw FormatError(format("bad path ‘%1%’ in derivation") % s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ Derivation readDerivation(const Path & drvPath)
|
||||
try {
|
||||
return parseDerivation(readFile(drvPath));
|
||||
} catch (FormatError & e) {
|
||||
throw Error(format("error parsing derivation '%1%': %2%") % drvPath % e.msg());
|
||||
throw Error(format("error parsing derivation ‘%1%’: %2%") % drvPath % e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ Derivation Store::derivationFromPath(const Path & drvPath)
|
||||
try {
|
||||
return parseDerivation(accessor->readFile(drvPath));
|
||||
} catch (FormatError & e) {
|
||||
throw Error(format("error parsing derivation '%1%': %2%") % drvPath % e.msg());
|
||||
throw Error(format("error parsing derivation ‘%1%’: %2%") % drvPath % e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ Hash hashDerivationModulo(Store & store, Derivation drv)
|
||||
h = hashDerivationModulo(store, drv2);
|
||||
drvHashes[i.first] = h;
|
||||
}
|
||||
inputs2[h.to_string(Base16, false)] = i.second;
|
||||
inputs2[printHash(h)] = i.second;
|
||||
}
|
||||
drv.inputDrvs = inputs2;
|
||||
|
||||
@@ -437,7 +437,7 @@ Sink & operator << (Sink & out, const BasicDerivation & drv)
|
||||
std::string hashPlaceholder(const std::string & outputName)
|
||||
{
|
||||
// FIXME: memoize?
|
||||
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false);
|
||||
return "/" + printHash32(hashString(htSHA256, "nix-output:" + outputName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -86,7 +85,7 @@ class Store;
|
||||
|
||||
/* Write a derivation to the Nix store, and return its path. */
|
||||
Path writeDerivation(ref<Store> store,
|
||||
const Derivation & drv, const string & name, RepairFlag repair = NoRepair);
|
||||
const Derivation & drv, const string & name, bool repair = false);
|
||||
|
||||
/* Read a derivation from a file. */
|
||||
Derivation readDerivation(const Path & drvPath);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "archive.hh"
|
||||
#include "s3.hh"
|
||||
#include "compression.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
#ifdef ENABLE_S3
|
||||
#include <aws/core/client/ClientConfiguration.h>
|
||||
@@ -23,8 +22,6 @@
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace nix {
|
||||
|
||||
double getTime()
|
||||
@@ -49,7 +46,7 @@ ref<std::string> decodeContent(const std::string & encoding, ref<std::string> da
|
||||
else if (encoding == "br")
|
||||
return decompress(encoding, *data);
|
||||
else
|
||||
throw Error("unsupported Content-Encoding '%s'", encoding);
|
||||
throw Error("unsupported Content-Encoding ‘%s’", encoding);
|
||||
}
|
||||
|
||||
struct CurlDownloader : public Downloader
|
||||
@@ -85,12 +82,12 @@ struct CurlDownloader : public Downloader
|
||||
std::string encoding;
|
||||
|
||||
DownloadItem(CurlDownloader & downloader, const DownloadRequest & request)
|
||||
: downloader(downloader)
|
||||
, request(request)
|
||||
, act(*logger, lvlTalkative, actDownload, fmt("downloading '%s'", request.uri), {request.uri}, request.parentAct)
|
||||
: downloader(downloader), request(request)
|
||||
{
|
||||
if (!request.expectedETag.empty())
|
||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
||||
|
||||
logger->event(evDownloadCreated, act, request.uri);
|
||||
}
|
||||
|
||||
~DownloadItem()
|
||||
@@ -103,10 +100,11 @@ struct CurlDownloader : public Downloader
|
||||
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
||||
try {
|
||||
if (!done)
|
||||
fail(DownloadError(Interrupted, format("download of '%s' was interrupted") % request.uri));
|
||||
fail(DownloadError(Interrupted, format("download of ‘%s’ was interrupted") % request.uri));
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
logger->event(evDownloadDestroyed, act);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -133,7 +131,7 @@ struct CurlDownloader : public Downloader
|
||||
{
|
||||
size_t realSize = size * nmemb;
|
||||
std::string line((char *) contents, realSize);
|
||||
printMsg(lvlVomit, format("got header for '%s': %s") % request.uri % trim(line));
|
||||
printMsg(lvlVomit, format("got header for ‘%s’: %s") % request.uri % trim(line));
|
||||
if (line.compare(0, 5, "HTTP/") == 0) { // new response starts
|
||||
result.etag = "";
|
||||
auto ss = tokenizeString<vector<string>>(line, " ");
|
||||
@@ -169,7 +167,7 @@ struct CurlDownloader : public Downloader
|
||||
|
||||
int progressCallback(double dltotal, double dlnow)
|
||||
{
|
||||
act.progress(dlnow, dltotal);
|
||||
logger->event(evDownloadProgress, act, dltotal, dlnow);
|
||||
return _isInterrupted;
|
||||
}
|
||||
|
||||
@@ -185,8 +183,6 @@ struct CurlDownloader : public Downloader
|
||||
return 0;
|
||||
}
|
||||
|
||||
long lowSpeedTimeout = 300;
|
||||
|
||||
void init()
|
||||
{
|
||||
if (!req) req = curl_easy_init();
|
||||
@@ -235,9 +231,6 @@ struct CurlDownloader : public Downloader
|
||||
|
||||
curl_easy_setopt(req, CURLOPT_CONNECTTIMEOUT, settings.connectTimeout.get());
|
||||
|
||||
curl_easy_setopt(req, CURLOPT_LOW_SPEED_LIMIT, 1L);
|
||||
curl_easy_setopt(req, CURLOPT_LOW_SPEED_TIME, lowSpeedTimeout);
|
||||
|
||||
/* If no file exist in the specified path, curl continues to work
|
||||
anyway as if netrc support was disabled. */
|
||||
curl_easy_setopt(req, CURLOPT_NETRC_FILE, settings.netrcFile.get().c_str());
|
||||
@@ -256,7 +249,7 @@ struct CurlDownloader : public Downloader
|
||||
if (effectiveUrlCStr)
|
||||
result.effectiveUrl = effectiveUrlCStr;
|
||||
|
||||
debug(format("finished download of '%s'; curl status = %d, HTTP status = %d, body = %d bytes")
|
||||
debug(format("finished download of ‘%s’; curl status = %d, HTTP status = %d, body = %d bytes")
|
||||
% request.uri % code % httpStatus % (result.data ? result.data->size() : 0));
|
||||
|
||||
if (code == CURLE_WRITE_ERROR && result.etag == request.expectedETag) {
|
||||
@@ -271,61 +264,43 @@ struct CurlDownloader : public Downloader
|
||||
done = true;
|
||||
|
||||
try {
|
||||
if (request.decompress)
|
||||
result.data = decodeContent(encoding, ref<std::string>(result.data));
|
||||
result.data = decodeContent(encoding, ref<std::string>(result.data));
|
||||
callSuccess(success, failure, const_cast<const DownloadResult &>(result));
|
||||
act.progress(result.data->size(), result.data->size());
|
||||
logger->event(evDownloadSucceeded, act, result.data->size());
|
||||
} catch (...) {
|
||||
done = true;
|
||||
callFailure(failure, std::current_exception());
|
||||
}
|
||||
} else {
|
||||
// We treat most errors as transient, but won't retry when hopeless
|
||||
Error err = Transient;
|
||||
Error err =
|
||||
(httpStatus == 404 || code == CURLE_FILE_COULDNT_READ_FILE) ? NotFound :
|
||||
httpStatus == 403 ? Forbidden :
|
||||
(httpStatus == 408 || httpStatus == 500 || httpStatus == 503
|
||||
|| httpStatus == 504 || httpStatus == 522 || httpStatus == 524
|
||||
|| code == CURLE_COULDNT_RESOLVE_HOST
|
||||
|| code == CURLE_RECV_ERROR
|
||||
|
||||
if (httpStatus == 404 || code == CURLE_FILE_COULDNT_READ_FILE) {
|
||||
// The file is definitely not there
|
||||
err = NotFound;
|
||||
} else if (httpStatus == 401 || httpStatus == 403 || httpStatus == 407) {
|
||||
// Don't retry on authentication/authorization failures
|
||||
err = Forbidden;
|
||||
} else if (httpStatus >= 400 && httpStatus < 500 && httpStatus != 408) {
|
||||
// Most 4xx errors are client errors and are probably not worth retrying:
|
||||
// * 408 means the server timed out waiting for us, so we try again
|
||||
err = Misc;
|
||||
} else if (httpStatus == 501 || httpStatus == 505 || httpStatus == 511) {
|
||||
// Let's treat most 5xx (server) errors as transient, except for a handful:
|
||||
// * 501 not implemented
|
||||
// * 505 http version not supported
|
||||
// * 511 we're behind a captive portal
|
||||
err = Misc;
|
||||
} else {
|
||||
// Don't bother retrying on certain cURL errors either
|
||||
switch (code) {
|
||||
case CURLE_FAILED_INIT:
|
||||
case CURLE_NOT_BUILT_IN:
|
||||
case CURLE_REMOTE_ACCESS_DENIED:
|
||||
case CURLE_FILE_COULDNT_READ_FILE:
|
||||
case CURLE_FUNCTION_NOT_FOUND:
|
||||
case CURLE_ABORTED_BY_CALLBACK:
|
||||
case CURLE_BAD_FUNCTION_ARGUMENT:
|
||||
case CURLE_INTERFACE_FAILED:
|
||||
case CURLE_UNKNOWN_OPTION:
|
||||
err = Misc;
|
||||
break;
|
||||
default: // Shut up warnings
|
||||
break;
|
||||
}
|
||||
}
|
||||
// this seems to occur occasionally for retriable reasons, and shows up in an error like this:
|
||||
// curl: (23) Failed writing body (315 != 16366)
|
||||
|| code == CURLE_WRITE_ERROR
|
||||
|
||||
// this is a generic SSL failure that in some cases (e.g., certificate error) is permanent but also appears in transient cases, so we consider it retryable
|
||||
|| code == CURLE_SSL_CONNECT_ERROR
|
||||
#if LIBCURL_VERSION_NUM >= 0x073200
|
||||
|| code == CURLE_HTTP2
|
||||
|| code == CURLE_HTTP2_STREAM
|
||||
#endif
|
||||
) ? Transient :
|
||||
Misc;
|
||||
|
||||
attempt++;
|
||||
|
||||
auto exc =
|
||||
code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted
|
||||
? DownloadError(Interrupted, format("download of '%s' was interrupted") % request.uri)
|
||||
? DownloadError(Interrupted, format("download of ‘%s’ was interrupted") % request.uri)
|
||||
: httpStatus != 0
|
||||
? DownloadError(err, format("unable to download '%s': HTTP error %d (curl error: %s)") % request.uri % httpStatus % curl_easy_strerror(code))
|
||||
: DownloadError(err, format("unable to download '%s': %s (%d)") % request.uri % curl_easy_strerror(code) % code);
|
||||
? DownloadError(err, format("unable to download ‘%s’: HTTP error %d (curl error: %s)") % request.uri % httpStatus % curl_easy_strerror(code))
|
||||
: DownloadError(err, format("unable to download ‘%s’: %s (%d)") % request.uri % curl_easy_strerror(code) % code);
|
||||
|
||||
/* If this is a transient error, then maybe retry the
|
||||
download after a while. */
|
||||
@@ -369,13 +344,11 @@ struct CurlDownloader : public Downloader
|
||||
|
||||
curlm = curl_multi_init();
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x072b00 // Multiplex requires >= 7.43.0
|
||||
#if LIBCURL_VERSION_NUM >= 0x072b00 // correct?
|
||||
curl_multi_setopt(curlm, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x071e00 // Max connections requires >= 7.30.0
|
||||
curl_multi_setopt(curlm, CURLMOPT_MAX_TOTAL_CONNECTIONS,
|
||||
settings.binaryCachesParallelConnections.get());
|
||||
#endif
|
||||
|
||||
enableHttp2 = settings.enableHttp2;
|
||||
|
||||
@@ -449,7 +422,7 @@ struct CurlDownloader : public Downloader
|
||||
auto sleepTimeMs =
|
||||
nextWakeup != std::chrono::steady_clock::time_point()
|
||||
? std::max(0, (int) std::chrono::duration_cast<std::chrono::milliseconds>(nextWakeup - std::chrono::steady_clock::now()).count())
|
||||
: 10000;
|
||||
: 1000000000;
|
||||
vomit("download thread waiting for %d ms", sleepTimeMs);
|
||||
mc = curl_multi_wait(curlm, extraFDs, 1, sleepTimeMs, &numfds);
|
||||
if (mc != CURLM_OK)
|
||||
@@ -535,21 +508,21 @@ struct CurlDownloader : public Downloader
|
||||
// FIXME: do this on a worker thread
|
||||
sync2async<DownloadResult>(success, failure, [&]() -> DownloadResult {
|
||||
#ifdef ENABLE_S3
|
||||
S3Helper s3Helper("", Aws::Region::US_EAST_1); // FIXME: make configurable
|
||||
S3Helper s3Helper(Aws::Region::US_EAST_1); // FIXME: make configurable
|
||||
auto slash = request.uri.find('/', 5);
|
||||
if (slash == std::string::npos)
|
||||
throw nix::Error("bad S3 URI '%s'", request.uri);
|
||||
throw nix::Error("bad S3 URI ‘%s’", request.uri);
|
||||
std::string bucketName(request.uri, 5, slash - 5);
|
||||
std::string key(request.uri, slash + 1);
|
||||
// FIXME: implement ETag
|
||||
auto s3Res = s3Helper.getObject(bucketName, key);
|
||||
DownloadResult res;
|
||||
if (!s3Res.data)
|
||||
throw DownloadError(NotFound, fmt("S3 object '%s' does not exist", request.uri));
|
||||
throw DownloadError(NotFound, fmt("S3 object ‘%s’ does not exist", request.uri));
|
||||
res.data = s3Res.data;
|
||||
return res;
|
||||
#else
|
||||
throw nix::Error("cannot download '%s' because Nix is not built with S3 support", request.uri);
|
||||
throw nix::Error("cannot download ‘%s’ because Nix is not built with S3 support", request.uri);
|
||||
#endif
|
||||
});
|
||||
return;
|
||||
@@ -608,13 +581,11 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
|
||||
Path cacheDir = getCacheDir() + "/nix/tarballs";
|
||||
createDirs(cacheDir);
|
||||
|
||||
string urlHash = hashString(htSHA256, name + std::string("\0"s) + url).to_string(Base32, false);
|
||||
string urlHash = printHash32(hashString(htSHA256, url));
|
||||
|
||||
Path dataFile = cacheDir + "/" + urlHash + ".info";
|
||||
Path fileLink = cacheDir + "/" + urlHash + "-file";
|
||||
|
||||
PathLocks lock({fileLink}, fmt("waiting for lock on '%1%'...", fileLink));
|
||||
|
||||
Path storePath;
|
||||
|
||||
string expectedETag;
|
||||
@@ -634,7 +605,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
|
||||
if (effectiveUrl)
|
||||
*effectiveUrl = url_;
|
||||
} else if (!ss[1].empty()) {
|
||||
debug(format("verifying previous ETag '%1%'") % ss[1]);
|
||||
debug(format("verifying previous ETag ‘%1%’") % ss[1]);
|
||||
expectedETag = ss[1];
|
||||
}
|
||||
}
|
||||
@@ -660,7 +631,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
|
||||
info.narHash = hashString(htSHA256, *sink.s);
|
||||
info.narSize = sink.s->size();
|
||||
info.ca = makeFixedOutputCA(false, hash);
|
||||
store->addToStore(info, sink.s, NoRepair, NoCheckSigs);
|
||||
store->addToStore(info, sink.s, false, true);
|
||||
storePath = info.path;
|
||||
}
|
||||
|
||||
@@ -676,7 +647,6 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
|
||||
|
||||
if (unpack) {
|
||||
Path unpackedLink = cacheDir + "/" + baseNameOf(storePath) + "-unpacked";
|
||||
PathLocks lock2({unpackedLink}, fmt("waiting for lock on '%1%'...", unpackedLink));
|
||||
Path unpackedStorePath;
|
||||
if (pathExists(unpackedLink)) {
|
||||
unpackedStorePath = readLink(unpackedLink);
|
||||
@@ -685,19 +655,19 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
|
||||
unpackedStorePath = "";
|
||||
}
|
||||
if (unpackedStorePath.empty()) {
|
||||
printInfo(format("unpacking '%1%'...") % url);
|
||||
printInfo(format("unpacking ‘%1%’...") % url);
|
||||
Path tmpDir = createTempDir();
|
||||
AutoDelete autoDelete(tmpDir, true);
|
||||
// FIXME: this requires GNU tar for decompression.
|
||||
runProgram("tar", true, {"xf", storePath, "-C", tmpDir, "--strip-components", "1"});
|
||||
unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, NoRepair);
|
||||
unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false);
|
||||
}
|
||||
replaceSymlink(unpackedStorePath, unpackedLink);
|
||||
storePath = unpackedStorePath;
|
||||
}
|
||||
|
||||
if (expectedStorePath != "" && storePath != expectedStorePath)
|
||||
throw nix::Error("store path mismatch in file downloaded from '%s'", url);
|
||||
throw nix::Error("store path mismatch in file downloaded from ‘%s’", url);
|
||||
|
||||
return storePath;
|
||||
}
|
||||
@@ -709,7 +679,7 @@ bool isUri(const string & s)
|
||||
size_t pos = s.find("://");
|
||||
if (pos == string::npos) return false;
|
||||
string scheme(s, 0, pos);
|
||||
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3" || scheme == "ssh";
|
||||
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,11 +16,8 @@ struct DownloadRequest
|
||||
bool head = false;
|
||||
size_t tries = 5;
|
||||
unsigned int baseRetryTimeMs = 250;
|
||||
ActivityId parentAct;
|
||||
bool decompress = true;
|
||||
|
||||
DownloadRequest(const std::string & uri)
|
||||
: uri(uri), parentAct(curActivity) { }
|
||||
DownloadRequest(const std::string & uri) : uri(uri) { }
|
||||
};
|
||||
|
||||
struct DownloadResult
|
||||
|
||||
@@ -33,7 +33,7 @@ void Store::exportPaths(const Paths & paths, Sink & sink)
|
||||
//logger->incExpected(doneLabel, sorted.size());
|
||||
|
||||
for (auto & path : sorted) {
|
||||
//Activity act(*logger, lvlInfo, format("exporting path '%s'") % path);
|
||||
//Activity act(*logger, lvlInfo, format("exporting path ‘%s’") % path);
|
||||
sink << 1;
|
||||
exportPath(path, sink);
|
||||
//logger->incProgress(doneLabel);
|
||||
@@ -55,19 +55,19 @@ void Store::exportPath(const Path & path, Sink & sink)
|
||||
Don't complain if the stored hash is zero (unknown). */
|
||||
Hash hash = hashAndWriteSink.currentHash();
|
||||
if (hash != info->narHash && info->narHash != Hash(info->narHash.type))
|
||||
throw Error(format("hash of path '%1%' has changed from '%2%' to '%3%'!") % path
|
||||
% info->narHash.to_string() % hash.to_string());
|
||||
throw Error(format("hash of path ‘%1%’ has changed from ‘%2%’ to ‘%3%’!") % path
|
||||
% printHash(info->narHash) % printHash(hash));
|
||||
|
||||
hashAndWriteSink << exportMagic << path << info->references << info->deriver << 0;
|
||||
}
|
||||
|
||||
Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, CheckSigsFlag checkSigs)
|
||||
Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor, bool dontCheckSigs)
|
||||
{
|
||||
Paths res;
|
||||
while (true) {
|
||||
auto n = readNum<uint64_t>(source);
|
||||
if (n == 0) break;
|
||||
if (n != 1) throw Error("input doesn't look like something created by 'nix-store --export'");
|
||||
if (n != 1) throw Error("input doesn't look like something created by ‘nix-store --export’");
|
||||
|
||||
/* Extract the NAR from the source. */
|
||||
TeeSink tee(source);
|
||||
@@ -81,7 +81,7 @@ Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor,
|
||||
|
||||
info.path = readStorePath(*this, source);
|
||||
|
||||
//Activity act(*logger, lvlInfo, format("importing path '%s'") % info.path);
|
||||
//Activity act(*logger, lvlInfo, format("importing path ‘%s’") % info.path);
|
||||
|
||||
info.references = readStorePaths<PathSet>(*this, source);
|
||||
|
||||
@@ -95,7 +95,7 @@ Paths Store::importPaths(Source & source, std::shared_ptr<FSAccessor> accessor,
|
||||
if (readInt(source) == 1)
|
||||
readString(source);
|
||||
|
||||
addToStore(info, tee.source.data, NoRepair, checkSigs, accessor);
|
||||
addToStore(info, tee.source.data, false, dontCheckSigs, accessor);
|
||||
|
||||
res.push_back(info.path);
|
||||
}
|
||||
|
||||
@@ -13,10 +13,9 @@ public:
|
||||
|
||||
struct Stat
|
||||
{
|
||||
Type type = tMissing;
|
||||
uint64_t fileSize = 0; // regular files only
|
||||
bool isExecutable = false; // regular files only
|
||||
uint64_t narOffset = 0; // regular files only
|
||||
Type type;
|
||||
uint64_t fileSize; // regular files only
|
||||
bool isExecutable; // regular files only
|
||||
};
|
||||
|
||||
virtual Stat stat(const Path & path) = 0;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "finally.hh"
|
||||
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
@@ -10,7 +9,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
@@ -20,6 +18,7 @@ namespace nix {
|
||||
|
||||
|
||||
static string gcLockName = "gc.lock";
|
||||
static string tempRootsDir = "temproots";
|
||||
static string gcRootsDir = "gcroots";
|
||||
|
||||
|
||||
@@ -33,11 +32,11 @@ int LocalStore::openGCLock(LockType lockType)
|
||||
Path fnGCLock = (format("%1%/%2%")
|
||||
% stateDir % gcLockName).str();
|
||||
|
||||
debug(format("acquiring global GC lock '%1%'") % fnGCLock);
|
||||
debug(format("acquiring global GC lock ‘%1%’") % fnGCLock);
|
||||
|
||||
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
|
||||
if (!fdGCLock)
|
||||
throw SysError(format("opening global GC lock '%1%'") % fnGCLock);
|
||||
throw SysError(format("opening global GC lock ‘%1%’") % fnGCLock);
|
||||
|
||||
if (!lockFile(fdGCLock.get(), lockType, false)) {
|
||||
printError(format("waiting for the big garbage collector lock..."));
|
||||
@@ -64,7 +63,7 @@ static void makeSymlink(const Path & link, const Path & target)
|
||||
|
||||
/* Atomically replace the old one. */
|
||||
if (rename(tempLink.c_str(), link.c_str()) == -1)
|
||||
throw SysError(format("cannot rename '%1%' to '%2%'")
|
||||
throw SysError(format("cannot rename ‘%1%’ to ‘%2%’")
|
||||
% tempLink % link);
|
||||
}
|
||||
|
||||
@@ -77,7 +76,7 @@ void LocalStore::syncWithGC()
|
||||
|
||||
void LocalStore::addIndirectRoot(const Path & path)
|
||||
{
|
||||
string hash = hashString(htSHA1, path).to_string(Base32, false);
|
||||
string hash = printHash32(hashString(htSHA1, path));
|
||||
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
|
||||
% stateDir % gcRootsDir % hash).str());
|
||||
makeSymlink(realRoot, path);
|
||||
@@ -100,7 +99,7 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
|
||||
/* Don't clobber the link if it already exists and doesn't
|
||||
point to the Nix store. */
|
||||
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
|
||||
throw Error(format("cannot create symlink '%1%'; already exists") % gcRoot);
|
||||
throw Error(format("cannot create symlink ‘%1%’; already exists") % gcRoot);
|
||||
makeSymlink(gcRoot, storePath);
|
||||
addIndirectRoot(gcRoot);
|
||||
}
|
||||
@@ -111,8 +110,8 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
|
||||
|
||||
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
||||
throw Error(format(
|
||||
"path '%1%' is not a valid garbage collector root; "
|
||||
"it's not in the directory '%2%'")
|
||||
"path ‘%1%’ is not a valid garbage collector root; "
|
||||
"it's not in the directory ‘%2%’")
|
||||
% gcRoot % rootsDir);
|
||||
}
|
||||
|
||||
@@ -132,8 +131,8 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
|
||||
if (roots.find(gcRoot) == roots.end())
|
||||
printError(
|
||||
format(
|
||||
"warning: '%1%' is not in a directory where the garbage collector looks for roots; "
|
||||
"therefore, '%2%' might be removed by the garbage collector")
|
||||
"warning: ‘%1%’ is not in a directory where the garbage collector looks for roots; "
|
||||
"therefore, ‘%2%’ might be removed by the garbage collector")
|
||||
% gcRoot % storePath);
|
||||
}
|
||||
|
||||
@@ -154,25 +153,30 @@ void LocalStore::addTempRoot(const Path & path)
|
||||
if (!state->fdTempRoots) {
|
||||
|
||||
while (1) {
|
||||
Path dir = (format("%1%/%2%") % stateDir % tempRootsDir).str();
|
||||
createDirs(dir);
|
||||
|
||||
state->fnTempRoots = (format("%1%/%2%") % dir % getpid()).str();
|
||||
|
||||
AutoCloseFD fdGCLock = openGCLock(ltRead);
|
||||
|
||||
if (pathExists(fnTempRoots))
|
||||
if (pathExists(state->fnTempRoots))
|
||||
/* It *must* be stale, since there can be no two
|
||||
processes with the same pid. */
|
||||
unlink(fnTempRoots.c_str());
|
||||
unlink(state->fnTempRoots.c_str());
|
||||
|
||||
state->fdTempRoots = openLockFile(fnTempRoots, true);
|
||||
state->fdTempRoots = openLockFile(state->fnTempRoots, true);
|
||||
|
||||
fdGCLock = -1;
|
||||
|
||||
debug(format("acquiring read lock on '%1%'") % fnTempRoots);
|
||||
debug(format("acquiring read lock on ‘%1%’") % state->fnTempRoots);
|
||||
lockFile(state->fdTempRoots.get(), ltRead, true);
|
||||
|
||||
/* Check whether the garbage collector didn't get in our
|
||||
way. */
|
||||
struct stat st;
|
||||
if (fstat(state->fdTempRoots.get(), &st) == -1)
|
||||
throw SysError(format("statting '%1%'") % fnTempRoots);
|
||||
throw SysError(format("statting ‘%1%’") % state->fnTempRoots);
|
||||
if (st.st_size == 0) break;
|
||||
|
||||
/* The garbage collector deleted this file before we could
|
||||
@@ -184,61 +188,56 @@ void LocalStore::addTempRoot(const Path & path)
|
||||
|
||||
/* Upgrade the lock to a write lock. This will cause us to block
|
||||
if the garbage collector is holding our lock. */
|
||||
debug(format("acquiring write lock on '%1%'") % fnTempRoots);
|
||||
debug(format("acquiring write lock on ‘%1%’") % state->fnTempRoots);
|
||||
lockFile(state->fdTempRoots.get(), ltWrite, true);
|
||||
|
||||
string s = path + '\0';
|
||||
writeFull(state->fdTempRoots.get(), s);
|
||||
|
||||
/* Downgrade to a read lock. */
|
||||
debug(format("downgrading to read lock on '%1%'") % fnTempRoots);
|
||||
debug(format("downgrading to read lock on ‘%1%’") % state->fnTempRoots);
|
||||
lockFile(state->fdTempRoots.get(), ltRead, true);
|
||||
}
|
||||
|
||||
|
||||
std::set<std::pair<pid_t, Path>> LocalStore::readTempRoots(FDs & fds)
|
||||
void LocalStore::readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
{
|
||||
std::set<std::pair<pid_t, Path>> tempRoots;
|
||||
|
||||
/* Read the `temproots' directory for per-process temporary root
|
||||
files. */
|
||||
for (auto & i : readDirectory(tempRootsDir)) {
|
||||
Path path = tempRootsDir + "/" + i.name;
|
||||
DirEntries tempRootFiles = readDirectory(
|
||||
(format("%1%/%2%") % stateDir % tempRootsDir).str());
|
||||
|
||||
pid_t pid = std::stoi(i.name);
|
||||
for (auto & i : tempRootFiles) {
|
||||
Path path = (format("%1%/%2%/%3%") % stateDir % tempRootsDir % i.name).str();
|
||||
|
||||
debug(format("reading temporary root file '%1%'") % path);
|
||||
debug(format("reading temporary root file ‘%1%’") % path);
|
||||
FDPtr fd(new AutoCloseFD(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)));
|
||||
if (!*fd) {
|
||||
/* It's okay if the file has disappeared. */
|
||||
if (errno == ENOENT) continue;
|
||||
throw SysError(format("opening temporary roots file '%1%'") % path);
|
||||
throw SysError(format("opening temporary roots file ‘%1%’") % path);
|
||||
}
|
||||
|
||||
/* This should work, but doesn't, for some reason. */
|
||||
//FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
|
||||
//if (*fd == -1) continue;
|
||||
|
||||
if (path != fnTempRoots) {
|
||||
|
||||
/* Try to acquire a write lock without blocking. This can
|
||||
only succeed if the owning process has died. In that case
|
||||
we don't care about its temporary roots. */
|
||||
if (lockFile(fd->get(), ltWrite, false)) {
|
||||
printError(format("removing stale temporary roots file '%1%'") % path);
|
||||
unlink(path.c_str());
|
||||
writeFull(fd->get(), "d");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Acquire a read lock. This will prevent the owning process
|
||||
from upgrading to a write lock, therefore it will block in
|
||||
addTempRoot(). */
|
||||
debug(format("waiting for read lock on '%1%'") % path);
|
||||
lockFile(fd->get(), ltRead, true);
|
||||
|
||||
/* Try to acquire a write lock without blocking. This can
|
||||
only succeed if the owning process has died. In that case
|
||||
we don't care about its temporary roots. */
|
||||
if (lockFile(fd->get(), ltWrite, false)) {
|
||||
printError(format("removing stale temporary roots file ‘%1%’") % path);
|
||||
unlink(path.c_str());
|
||||
writeFull(fd->get(), "d");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Acquire a read lock. This will prevent the owning process
|
||||
from upgrading to a write lock, therefore it will block in
|
||||
addTempRoot(). */
|
||||
debug(format("waiting for read lock on ‘%1%’") % path);
|
||||
lockFile(fd->get(), ltRead, true);
|
||||
|
||||
/* Read the entire file. */
|
||||
string contents = readFile(fd->get());
|
||||
|
||||
@@ -247,16 +246,14 @@ std::set<std::pair<pid_t, Path>> LocalStore::readTempRoots(FDs & fds)
|
||||
|
||||
while ((end = contents.find((char) 0, pos)) != string::npos) {
|
||||
Path root(contents, pos, end - pos);
|
||||
debug("got temporary root '%s'", root);
|
||||
debug(format("got temporary root ‘%1%’") % root);
|
||||
assertStorePath(root);
|
||||
tempRoots.emplace(pid, root);
|
||||
tempRoots.insert(root);
|
||||
pos = end + 1;
|
||||
}
|
||||
|
||||
fds.push_back(fd); /* keep open */
|
||||
}
|
||||
|
||||
return tempRoots;
|
||||
}
|
||||
|
||||
|
||||
@@ -267,7 +264,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
|
||||
if (isStorePath(storePath) && isValidPath(storePath))
|
||||
roots[path] = storePath;
|
||||
else
|
||||
printInfo(format("skipping invalid root from '%1%' to '%2%'") % path % storePath);
|
||||
printInfo(format("skipping invalid root from ‘%1%’ to ‘%2%’") % path % storePath);
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -290,7 +287,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
|
||||
target = absPath(target, dirOf(path));
|
||||
if (!pathExists(target)) {
|
||||
if (isInDir(path, stateDir + "/" + gcRootsDir + "/auto")) {
|
||||
printInfo(format("removing stale link from '%1%' to '%2%'") % path % target);
|
||||
printInfo(format("removing stale link from ‘%1%’ to ‘%2%’") % path % target);
|
||||
unlink(path.c_str());
|
||||
}
|
||||
} else {
|
||||
@@ -313,14 +310,14 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
|
||||
catch (SysError & e) {
|
||||
/* We only ignore permanent failures. */
|
||||
if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
|
||||
printInfo(format("cannot read potential root '%1%'") % path);
|
||||
printInfo(format("cannot read potential root ‘%1%’") % path);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Roots LocalStore::findRootsNoTemp()
|
||||
Roots LocalStore::findRoots()
|
||||
{
|
||||
Roots roots;
|
||||
|
||||
@@ -330,31 +327,6 @@ Roots LocalStore::findRootsNoTemp()
|
||||
findRoots(stateDir + "/manifests", DT_UNKNOWN, roots);
|
||||
findRoots(stateDir + "/profiles", DT_UNKNOWN, roots);
|
||||
|
||||
/* Add additional roots returned by the program specified by the
|
||||
NIX_ROOT_FINDER environment variable. This is typically used
|
||||
to add running programs to the set of roots (to prevent them
|
||||
from being garbage collected). */
|
||||
size_t n = 0;
|
||||
for (auto & root : findRuntimeRoots())
|
||||
roots[fmt("{memory:%d}", n++)] = root;
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
Roots LocalStore::findRoots()
|
||||
{
|
||||
Roots roots = findRootsNoTemp();
|
||||
|
||||
FDs fds;
|
||||
pid_t prev = -1;
|
||||
size_t n = 0;
|
||||
for (auto & root : readTempRoots(fds)) {
|
||||
if (prev != root.first) n = 0;
|
||||
prev = root.first;
|
||||
roots[fmt("{temp:%d:%d}", root.first, n++)] = root.second;
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
@@ -398,9 +370,8 @@ static void readFileRoots(const char * path, StringSet & paths)
|
||||
}
|
||||
}
|
||||
|
||||
PathSet LocalStore::findRuntimeRoots()
|
||||
void LocalStore::findRuntimeRoots(PathSet & roots)
|
||||
{
|
||||
PathSet roots;
|
||||
StringSet paths;
|
||||
auto procDir = AutoCloseDir{opendir("/proc")};
|
||||
if (procDir) {
|
||||
@@ -480,12 +451,10 @@ PathSet LocalStore::findRuntimeRoots()
|
||||
if (isInStore(i)) {
|
||||
Path path = toStorePath(i);
|
||||
if (roots.find(path) == roots.end() && isStorePath(path) && isValidPath(path)) {
|
||||
debug(format("got additional root '%1%'") % path);
|
||||
debug(format("got additional root ‘%1%’") % path);
|
||||
roots.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
@@ -548,7 +517,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
throw SysError(format("getting status of %1%") % realPath);
|
||||
}
|
||||
|
||||
printInfo(format("deleting '%1%'") % path);
|
||||
printInfo(format("deleting ‘%1%’") % path);
|
||||
|
||||
state.results.paths.insert(path);
|
||||
|
||||
@@ -563,14 +532,14 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
// size.
|
||||
try {
|
||||
if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making '%1%' writable") % realPath);
|
||||
throw SysError(format("making ‘%1%’ writable") % realPath);
|
||||
Path tmp = trashDir + "/" + baseNameOf(path);
|
||||
if (rename(realPath.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename '%1%' to '%2%'") % realPath % tmp);
|
||||
throw SysError(format("unable to rename ‘%1%’ to ‘%2%’") % realPath % tmp);
|
||||
state.bytesInvalidated += size;
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOSPC) {
|
||||
printInfo(format("note: can't create move '%1%': %2%") % realPath % e.msg());
|
||||
printInfo(format("note: can't create move ‘%1%’: %2%") % realPath % e.msg());
|
||||
deleteGarbage(state, realPath);
|
||||
}
|
||||
}
|
||||
@@ -586,14 +555,18 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
|
||||
bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
|
||||
{
|
||||
if (visited.count(path)) return false;
|
||||
if (visited.find(path) != visited.end()) return false;
|
||||
|
||||
if (state.alive.count(path)) return true;
|
||||
if (state.alive.find(path) != state.alive.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.dead.count(path)) return false;
|
||||
if (state.dead.find(path) != state.dead.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.roots.count(path)) {
|
||||
debug(format("cannot delete '%1%' because it's a root") % path);
|
||||
if (state.roots.find(path) != state.roots.end()) {
|
||||
debug(format("cannot delete ‘%1%’ because it's a root") % path);
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
@@ -607,7 +580,7 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p
|
||||
/* Don't delete this path if any of its referrers are alive. */
|
||||
queryReferrers(path, incoming);
|
||||
|
||||
/* If keep-derivations is set and this is a derivation, then
|
||||
/* If gc-keep-derivations is set and this is a derivation, then
|
||||
don't delete the derivation if any of the outputs are alive. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
@@ -616,7 +589,7 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p
|
||||
incoming.insert(i);
|
||||
}
|
||||
|
||||
/* If keep-outputs is set, then don't delete this path if there
|
||||
/* If gc-keep-outputs is set, then don't delete this path if there
|
||||
are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
@@ -642,7 +615,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
auto realPath = realStoreDir + "/" + baseNameOf(path);
|
||||
if (realPath == linksDir || realPath == trashDir) return;
|
||||
|
||||
//Activity act(*logger, lvlDebug, format("considering whether to delete '%1%'") % path);
|
||||
//Activity act(*logger, lvlDebug, format("considering whether to delete ‘%1%’") % path);
|
||||
|
||||
if (!isStorePath(path) || !isValidPath(path)) {
|
||||
/* A lock file belonging to a path that we're building right
|
||||
@@ -662,7 +635,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
PathSet visited;
|
||||
|
||||
if (canReachRoot(state, visited, path)) {
|
||||
debug(format("cannot delete '%1%' because it's still reachable") % path);
|
||||
debug(format("cannot delete ‘%1%’ because it's still reachable") % path);
|
||||
} else {
|
||||
/* No path we visited was a root, so everything is garbage.
|
||||
But we only delete ‘path’ and its referrers here so that
|
||||
@@ -683,7 +656,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
{
|
||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||
if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
|
||||
if (!dir) throw SysError(format("opening directory ‘%1%’") % linksDir);
|
||||
|
||||
long long actualSize = 0, unsharedSize = 0;
|
||||
|
||||
@@ -696,7 +669,7 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting '%1%'") % path);
|
||||
throw SysError(format("statting ‘%1%’") % path);
|
||||
|
||||
if (st.st_nlink != 1) {
|
||||
unsigned long long size = st.st_blocks * 512ULL;
|
||||
@@ -705,17 +678,17 @@ void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
continue;
|
||||
}
|
||||
|
||||
printMsg(lvlTalkative, format("deleting unused link '%1%'") % path);
|
||||
printMsg(lvlTalkative, format("deleting unused link ‘%1%’") % path);
|
||||
|
||||
if (unlink(path.c_str()) == -1)
|
||||
throw SysError(format("deleting '%1%'") % path);
|
||||
throw SysError(format("deleting ‘%1%’") % path);
|
||||
|
||||
state.results.bytesFreed += st.st_blocks * 512ULL;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(linksDir.c_str(), &st) == -1)
|
||||
throw SysError(format("statting '%1%'") % linksDir);
|
||||
throw SysError(format("statting ‘%1%’") % linksDir);
|
||||
long long overhead = st.st_blocks * 512ULL;
|
||||
|
||||
printInfo(format("note: currently hard linking saves %.2f MiB")
|
||||
@@ -731,9 +704,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
state.gcKeepDerivations = settings.gcKeepDerivations;
|
||||
|
||||
/* Using `--ignore-liveness' with `--delete' can have unintended
|
||||
consequences if `keep-outputs' or `keep-derivations' are true
|
||||
(the garbage collector will recurse into deleting the outputs
|
||||
or derivers, respectively). So disable them. */
|
||||
consequences if `gc-keep-outputs' or `gc-keep-derivations' are
|
||||
true (the garbage collector will recurse into deleting the
|
||||
outputs or derivers, respectively). So disable them. */
|
||||
if (options.action == GCOptions::gcDeleteSpecific && options.ignoreLiveness) {
|
||||
state.gcKeepOutputs = false;
|
||||
state.gcKeepDerivations = false;
|
||||
@@ -752,16 +725,22 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
/* Find the roots. Since we've grabbed the GC lock, the set of
|
||||
permanent roots cannot increase now. */
|
||||
printError(format("finding garbage collector roots..."));
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : findRootsNoTemp();
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
|
||||
|
||||
for (auto & i : rootMap) state.roots.insert(i.second);
|
||||
|
||||
/* Add additional roots returned by the program specified by the
|
||||
NIX_ROOT_FINDER environment variable. This is typically used
|
||||
to add running programs to the set of roots (to prevent them
|
||||
from being garbage collected). */
|
||||
if (!options.ignoreLiveness)
|
||||
findRuntimeRoots(state.roots);
|
||||
|
||||
/* Read the temporary roots. This acquires read locks on all
|
||||
per-process temporary root files. So after this point no paths
|
||||
can be added to the set of temporary roots. */
|
||||
FDs fds;
|
||||
for (auto & root : readTempRoots(fds))
|
||||
state.tempRoots.insert(root.second);
|
||||
readTempRoots(state.tempRoots, fds);
|
||||
state.roots.insert(state.tempRoots.begin(), state.tempRoots.end());
|
||||
|
||||
/* After this point the set of roots or temporary roots cannot
|
||||
@@ -789,7 +768,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
assertStorePath(i);
|
||||
tryToDelete(state, i);
|
||||
if (state.dead.find(i) == state.dead.end())
|
||||
throw Error(format("cannot delete path '%1%' since it is still alive") % i);
|
||||
throw Error(format("cannot delete path ‘%1%’ since it is still alive") % i);
|
||||
}
|
||||
|
||||
} else if (options.maxFreed > 0) {
|
||||
@@ -802,7 +781,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
try {
|
||||
|
||||
AutoCloseDir dir(opendir(realStoreDir.c_str()));
|
||||
if (!dir) throw SysError(format("opening directory '%1%'") % realStoreDir);
|
||||
if (!dir) throw SysError(format("opening directory ‘%1%’") % realStoreDir);
|
||||
|
||||
/* Read the store and immediately delete all paths that
|
||||
aren't valid. When using --max-freed etc., deleting
|
||||
@@ -855,7 +834,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
fds.clear();
|
||||
|
||||
/* Delete the trash directory. */
|
||||
printInfo(format("deleting '%1%'") % trashDir);
|
||||
printInfo(format("deleting ‘%1%’") % trashDir);
|
||||
deleteGarbage(state, trashDir);
|
||||
|
||||
/* Clean up the links directory. */
|
||||
@@ -869,80 +848,4 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::autoGC(bool sync)
|
||||
{
|
||||
auto getAvail = [this]() {
|
||||
struct statvfs st;
|
||||
if (statvfs(realStoreDir.c_str(), &st))
|
||||
throw SysError("getting filesystem info about '%s'", realStoreDir);
|
||||
|
||||
return (uint64_t) st.f_bavail * st.f_bsize;
|
||||
};
|
||||
|
||||
std::shared_future<void> future;
|
||||
|
||||
{
|
||||
auto state(_state.lock());
|
||||
|
||||
if (state->gcRunning) {
|
||||
future = state->gcFuture;
|
||||
debug("waiting for auto-GC to finish");
|
||||
goto sync;
|
||||
}
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (now < state->lastGCCheck + std::chrono::seconds(5)) return;
|
||||
|
||||
auto avail = getAvail();
|
||||
|
||||
state->lastGCCheck = now;
|
||||
|
||||
if (avail >= settings.minFree || avail >= settings.maxFree) return;
|
||||
|
||||
if (avail > state->availAfterGC * 0.97) return;
|
||||
|
||||
state->gcRunning = true;
|
||||
|
||||
std::promise<void> promise;
|
||||
future = state->gcFuture = promise.get_future().share();
|
||||
|
||||
std::thread([promise{std::move(promise)}, this, avail, getAvail]() mutable {
|
||||
|
||||
try {
|
||||
|
||||
/* Wake up any threads waiting for the auto-GC to finish. */
|
||||
Finally wakeup([&]() {
|
||||
auto state(_state.lock());
|
||||
state->gcRunning = false;
|
||||
state->lastGCCheck = std::chrono::steady_clock::now();
|
||||
promise.set_value();
|
||||
});
|
||||
|
||||
printInfo("running auto-GC to free %d bytes", settings.maxFree - avail);
|
||||
|
||||
GCOptions options;
|
||||
options.maxFreed = settings.maxFree - avail;
|
||||
|
||||
GCResults results;
|
||||
|
||||
collectGarbage(options, results);
|
||||
|
||||
_state.lock()->availAfterGC = getAvail();
|
||||
|
||||
} catch (...) {
|
||||
// FIXME: we could propagate the exception to the
|
||||
// future, but we don't really care.
|
||||
ignoreException();
|
||||
}
|
||||
|
||||
}).detach();
|
||||
}
|
||||
|
||||
sync:
|
||||
// Wait for the future outside of the state lock.
|
||||
if (sync) future.get();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -53,12 +53,7 @@ Settings::Settings()
|
||||
|
||||
/* Backwards compatibility. */
|
||||
auto s = getEnv("NIX_REMOTE_SYSTEMS");
|
||||
if (s != "") {
|
||||
Strings ss;
|
||||
for (auto & p : tokenizeString<Strings>(s, ":"))
|
||||
ss.push_back("@" + p);
|
||||
builders = concatStringsSep(" ", ss);
|
||||
}
|
||||
if (s != "") builderFiles = tokenizeString<Strings>(s, ":");
|
||||
|
||||
#if defined(__linux__) && defined(SANDBOX_SHELL)
|
||||
sandboxPaths = tokenizeString<StringSet>("/bin/sh=" SANDBOX_SHELL);
|
||||
@@ -116,17 +111,17 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
|
||||
args.mkFlag()
|
||||
.longName(name)
|
||||
.description("Enable sandboxing.")
|
||||
.handler([=](std::vector<std::string> ss) { override(smEnabled); })
|
||||
.handler([=](Strings ss) { value = smEnabled; })
|
||||
.category(category);
|
||||
args.mkFlag()
|
||||
.longName("no-" + name)
|
||||
.description("Disable sandboxing.")
|
||||
.handler([=](std::vector<std::string> ss) { override(smDisabled); })
|
||||
.handler([=](Strings ss) { value = smDisabled; })
|
||||
.category(category);
|
||||
args.mkFlag()
|
||||
.longName("relaxed-" + name)
|
||||
.description("Enable sandboxing, but allow builds to disable it.")
|
||||
.handler([=](std::vector<std::string> ss) { override(smRelaxed); })
|
||||
.handler([=](Strings ss) { value = smRelaxed; })
|
||||
.category(category);
|
||||
}
|
||||
|
||||
@@ -134,7 +129,7 @@ void MaxBuildJobsSetting::set(const std::string & str)
|
||||
{
|
||||
if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency());
|
||||
else if (!string2Int(str, value))
|
||||
throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
|
||||
throw UsageError("configuration setting ‘%s’ should be ‘auto’ or an integer", name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
|
||||
#include "types.hh"
|
||||
#include "config.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <map>
|
||||
#include <limits>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode;
|
||||
@@ -85,18 +83,14 @@ public:
|
||||
/* File name of the socket the daemon listens to. */
|
||||
Path nixDaemonSocketFile;
|
||||
|
||||
Setting<std::string> storeUri{this, getEnv("NIX_REMOTE", "auto"), "store",
|
||||
"The default Nix store to use."};
|
||||
|
||||
Setting<bool> keepFailed{this, false, "keep-failed",
|
||||
"Whether to keep temporary directories of failed builds."};
|
||||
|
||||
Setting<bool> keepGoing{this, false, "keep-going",
|
||||
"Whether to keep building derivations when another build fails."};
|
||||
|
||||
Setting<bool> tryFallback{this, false, "fallback",
|
||||
"Whether to fall back to building when substitution fails.",
|
||||
{"build-fallback"}};
|
||||
Setting<bool> tryFallback{this, false, "build-fallback",
|
||||
"Whether to fall back to building when substitution fails."};
|
||||
|
||||
/* Whether to show build log output in real time. */
|
||||
bool verboseBuild = true;
|
||||
@@ -105,15 +99,14 @@ public:
|
||||
the log to show if a build fails. */
|
||||
size_t logLines = 10;
|
||||
|
||||
MaxBuildJobsSetting maxBuildJobs{this, 1, "max-jobs",
|
||||
"Maximum number of parallel build jobs. \"auto\" means use number of cores.",
|
||||
{"build-max-jobs"}};
|
||||
MaxBuildJobsSetting maxBuildJobs{this, 1, "build-max-jobs",
|
||||
"Maximum number of parallel build jobs. \"auto\" means use number of cores."};
|
||||
|
||||
Setting<unsigned int> buildCores{this, getDefaultCores(), "cores",
|
||||
Setting<unsigned int> buildCores{this, getDefaultCores(), "build-cores",
|
||||
"Number of CPU cores to utilize in parallel within a build, "
|
||||
"i.e. by passing this number to Make via '-j'. 0 means that the "
|
||||
"number of actual CPU cores on the local host ought to be "
|
||||
"auto-detected.", {"build-cores"}};
|
||||
"auto-detected."};
|
||||
|
||||
/* Read-only mode. Don't copy stuff to the store, don't change
|
||||
the database. */
|
||||
@@ -122,26 +115,27 @@ public:
|
||||
Setting<std::string> thisSystem{this, SYSTEM, "system",
|
||||
"The canonical Nix system name."};
|
||||
|
||||
Setting<time_t> maxSilentTime{this, 0, "max-silent-time",
|
||||
Setting<time_t> maxSilentTime{this, 0, "build-max-silent-time",
|
||||
"The maximum time in seconds that a builer can go without "
|
||||
"producing any output on stdout/stderr before it is killed. "
|
||||
"0 means infinity.",
|
||||
{"build-max-silent-time"}};
|
||||
"0 means infinity."};
|
||||
|
||||
Setting<time_t> buildTimeout{this, 0, "timeout",
|
||||
Setting<time_t> buildTimeout{this, 0, "build-timeout",
|
||||
"The maximum duration in seconds that a builder can run. "
|
||||
"0 means infinity.", {"build-timeout"}};
|
||||
"0 means infinity."};
|
||||
|
||||
Setting<bool> useBuildHook{this, true, "remote-builds",
|
||||
"Whether to use build hooks (for distributed builds)."};
|
||||
|
||||
PathSetting buildHook{this, true, nixLibexecDir + "/nix/build-remote", "build-hook",
|
||||
"The path of the helper program that executes builds to remote machines."};
|
||||
|
||||
Setting<std::string> builders{this, "@" + nixConfDir + "/machines", "builders",
|
||||
Setting<std::string> builders{this, "", "builders",
|
||||
"A semicolon-separated list of build machines, in the format of nix.machines."};
|
||||
|
||||
Setting<bool> buildersUseSubstitutes{this, false, "builders-use-substitutes",
|
||||
"Whether build machines should use their own substitutes for obtaining "
|
||||
"build dependencies if possible, rather than waiting for this host to "
|
||||
"upload them."};
|
||||
Setting<Strings> builderFiles{this,
|
||||
{nixConfDir + "/machines"}, "builder-files",
|
||||
"A list of files specifying build machines."};
|
||||
|
||||
Setting<off_t> reservedSize{this, 8 * 1024 * 1024, "gc-reserved-space",
|
||||
"Amount of reserved disk space for the garbage collector."};
|
||||
@@ -155,32 +149,27 @@ public:
|
||||
Setting<bool> syncBeforeRegistering{this, false, "sync-before-registering",
|
||||
"Whether to call sync() before registering a path as valid."};
|
||||
|
||||
Setting<bool> useSubstitutes{this, true, "substitute",
|
||||
"Whether to use substitutes.",
|
||||
{"build-use-substitutes"}};
|
||||
Setting<bool> useSubstitutes{this, true, "build-use-substitutes",
|
||||
"Whether to use substitutes."};
|
||||
|
||||
Setting<std::string> buildUsersGroup{this, "", "build-users-group",
|
||||
"The Unix group that contains the build users."};
|
||||
|
||||
Setting<bool> impersonateLinux26{this, false, "impersonate-linux-26",
|
||||
"Whether to impersonate a Linux 2.6 machine on newer kernels.",
|
||||
{"build-impersonate-linux-26"}};
|
||||
Setting<bool> impersonateLinux26{this, false, "build-impersonate-linux-26",
|
||||
"Whether to impersonate a Linux 2.6 machine on newer kernels."};
|
||||
|
||||
Setting<bool> keepLog{this, true, "keep-build-log",
|
||||
"Whether to store build logs.",
|
||||
{"build-keep-log"}};
|
||||
Setting<bool> keepLog{this, true, "build-keep-log",
|
||||
"Whether to store build logs."};
|
||||
|
||||
Setting<bool> compressLog{this, true, "compress-build-log",
|
||||
"Whether to compress logs.",
|
||||
{"build-compress-log"}};
|
||||
Setting<bool> compressLog{this, true, "build-compress-log",
|
||||
"Whether to compress logs."};
|
||||
|
||||
Setting<unsigned long> maxLogSize{this, 0, "max-build-log-size",
|
||||
Setting<unsigned long> maxLogSize{this, 0, "build-max-log-size",
|
||||
"Maximum number of bytes a builder can write to stdout/stderr "
|
||||
"before being killed (0 means no limit).",
|
||||
{"build-max-log-size"}};
|
||||
"before being killed (0 means no limit)."};
|
||||
|
||||
/* When buildRepeat > 0 and verboseBuild == true, whether to print
|
||||
repeated builds (i.e. builds other than the first one) to
|
||||
/* When build-repeat > 0 and verboseBuild == true, whether to
|
||||
print repeated builds (i.e. builds other than the first one) to
|
||||
stderr. Hack to prevent Hydra logs from being polluted. */
|
||||
bool printRepeatedBuilds = true;
|
||||
|
||||
@@ -191,21 +180,18 @@ public:
|
||||
"Whether to check if new GC roots can in fact be found by the "
|
||||
"garbage collector."};
|
||||
|
||||
Setting<bool> gcKeepOutputs{this, false, "keep-outputs",
|
||||
"Whether the garbage collector should keep outputs of live derivations.",
|
||||
{"gc-keep-outputs"}};
|
||||
Setting<bool> gcKeepOutputs{this, false, "gc-keep-outputs",
|
||||
"Whether the garbage collector should keep outputs of live derivations."};
|
||||
|
||||
Setting<bool> gcKeepDerivations{this, true, "keep-derivations",
|
||||
"Whether the garbage collector should keep derivers of live paths.",
|
||||
{"gc-keep-derivations"}};
|
||||
Setting<bool> gcKeepDerivations{this, true, "gc-keep-derivations",
|
||||
"Whether the garbage collector should keep derivers of live paths."};
|
||||
|
||||
Setting<bool> autoOptimiseStore{this, false, "auto-optimise-store",
|
||||
"Whether to automatically replace files with identical contents with hard links."};
|
||||
|
||||
Setting<bool> envKeepDerivations{this, false, "keep-env-derivations",
|
||||
Setting<bool> envKeepDerivations{this, false, "env-keep-derivations",
|
||||
"Whether to add derivations as a dependency of user environments "
|
||||
"(to prevent them from being GCed).",
|
||||
{"env-keep-derivations"}};
|
||||
"(to prevent them from being GCed)."};
|
||||
|
||||
/* Whether to lock the Nix client and worker to the same CPU. */
|
||||
bool lockCPU;
|
||||
@@ -216,25 +202,24 @@ public:
|
||||
Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation",
|
||||
"Whether builtin functions that allow executing native code should be enabled."};
|
||||
|
||||
Setting<SandboxMode> sandboxMode{this, smDisabled, "sandbox",
|
||||
Setting<SandboxMode> sandboxMode{this, smDisabled, "build-use-sandbox",
|
||||
"Whether to enable sandboxed builds. Can be \"true\", \"false\" or \"relaxed\".",
|
||||
{"build-use-chroot", "build-use-sandbox"}};
|
||||
{"build-use-chroot"}};
|
||||
|
||||
Setting<PathSet> sandboxPaths{this, {}, "sandbox-paths",
|
||||
Setting<PathSet> sandboxPaths{this, {}, "build-sandbox-paths",
|
||||
"The paths to make available inside the build sandbox.",
|
||||
{"build-chroot-dirs", "build-sandbox-paths"}};
|
||||
{"build-chroot-dirs"}};
|
||||
|
||||
Setting<PathSet> extraSandboxPaths{this, {}, "extra-sandbox-paths",
|
||||
Setting<PathSet> extraSandboxPaths{this, {}, "build-extra-sandbox-paths",
|
||||
"Additional paths to make available inside the build sandbox.",
|
||||
{"build-extra-chroot-dirs", "build-extra-sandbox-paths"}};
|
||||
{"build-extra-chroot-dirs"}};
|
||||
|
||||
Setting<bool> restrictEval{this, false, "restrict-eval",
|
||||
"Whether to restrict file system access to paths in $NIX_PATH, "
|
||||
"and network access to the URI prefixes listed in 'allowed-uris'."};
|
||||
"and to disallow fetching files from the network."};
|
||||
|
||||
Setting<size_t> buildRepeat{this, 0, "repeat",
|
||||
"The number of times to repeat a build in order to verify determinism.",
|
||||
{"build-repeat"}};
|
||||
Setting<size_t> buildRepeat{this, 0, "build-repeat",
|
||||
"The number of times to repeat a build in order to verify determinism."};
|
||||
|
||||
#if __linux__
|
||||
Setting<std::string> sandboxShmSize{this, "50%", "sandbox-dev-shm-size",
|
||||
@@ -264,11 +249,10 @@ public:
|
||||
Setting<bool> enforceDeterminism{this, true, "enforce-determinism",
|
||||
"Whether to fail if repeated builds produce different output."};
|
||||
|
||||
Setting<Strings> trustedPublicKeys{this,
|
||||
Setting<Strings> binaryCachePublicKeys{this,
|
||||
{"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="},
|
||||
"trusted-public-keys",
|
||||
"Trusted public keys for secure substitution.",
|
||||
{"binary-cache-public-keys"}};
|
||||
"binary-cache-public-keys",
|
||||
"Trusted public keys for secure substitution."};
|
||||
|
||||
Setting<Strings> secretKeyFiles{this, {}, "secret-key-files",
|
||||
"Secret keys with which to sign local builds."};
|
||||
@@ -277,7 +261,7 @@ public:
|
||||
"Number of parallel HTTP connections.",
|
||||
{"binary-caches-parallel-connections"}};
|
||||
|
||||
Setting<bool> enableHttp2{this, true, "http2",
|
||||
Setting<bool> enableHttp2{this, true, "enable-http2",
|
||||
"Whether to enable HTTP/2 support."};
|
||||
|
||||
Setting<unsigned int> tarballTtl{this, 60 * 60, "tarball-ttl",
|
||||
@@ -286,11 +270,6 @@ public:
|
||||
Setting<std::string> signedBinaryCaches{this, "*", "signed-binary-caches",
|
||||
"Obsolete."};
|
||||
|
||||
Setting<bool> requireSigs{this, signedBinaryCaches == "*", "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'."};
|
||||
|
||||
Setting<Strings> substituters{this,
|
||||
nixStore == "/nix/store" ? Strings{"https://cache.nixos.org/"} : Strings(),
|
||||
"substituters",
|
||||
@@ -342,30 +321,6 @@ public:
|
||||
|
||||
Setting<std::string> userAgentSuffix{this, "", "user-agent-suffix",
|
||||
"String appended to the user agent in HTTP requests."};
|
||||
|
||||
#if __linux__
|
||||
Setting<bool> filterSyscalls{this, true, "filter-syscalls",
|
||||
"Whether to prevent certain dangerous system calls, such as "
|
||||
"creation of setuid/setgid files or adding ACLs or extended "
|
||||
"attributes. Only disable this if you're aware of the "
|
||||
"security implications."};
|
||||
|
||||
Setting<bool> allowNewPrivileges{this, false, "allow-new-privileges",
|
||||
"Whether builders can acquire new privileges by calling programs with "
|
||||
"setuid/setgid bits or with file capabilities."};
|
||||
#endif
|
||||
|
||||
Setting<Strings> hashedMirrors{this, {"http://tarballs.nixos.org/"}, "hashed-mirrors",
|
||||
"A list of servers used by builtins.fetchurl to fetch files by hash."};
|
||||
|
||||
Setting<uint64_t> minFree{this, 0, "min-free",
|
||||
"Automatically run the garbage collector when free disk space drops below the specified amount."};
|
||||
|
||||
Setting<uint64_t> maxFree{this, std::numeric_limits<uint64_t>::max(), "max-free",
|
||||
"Stop deleting garbage when free disk space is above the specified amount."};
|
||||
|
||||
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
||||
"Prefixes of URIs that builtin functions such as fetchurl and fetchGit are allowed to fetch."};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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(format("‘%s’ does not appear to be a binary cache") % cacheUri);
|
||||
}
|
||||
diskCache->createCache(cacheUri, storeDir, wantMassQuery_, priority);
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ struct LegacySSHStore : public Store
|
||||
|
||||
unsigned int magic = readInt(conn->from);
|
||||
if (magic != SERVE_MAGIC_2)
|
||||
throw Error("protocol mismatch with 'nix-store --serve' on '%s'", host);
|
||||
throw Error("protocol mismatch with ‘nix-store --serve’ on ‘%s’", host);
|
||||
conn->remoteVersion = readInt(conn->from);
|
||||
if (GET_PROTOCOL_MAJOR(conn->remoteVersion) != 0x200)
|
||||
throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host);
|
||||
throw Error("unsupported ‘nix-store --serve’ protocol version on ‘%s’", host);
|
||||
|
||||
} catch (EndOfFile & e) {
|
||||
throw Error("cannot connect to '%1%'", host);
|
||||
throw Error("cannot connect to ‘%1%’", host);
|
||||
}
|
||||
|
||||
return conn;
|
||||
@@ -89,7 +89,7 @@ struct LegacySSHStore : public Store
|
||||
sync2async<std::shared_ptr<ValidPathInfo>>(success, failure, [&]() -> std::shared_ptr<ValidPathInfo> {
|
||||
auto conn(connections->get());
|
||||
|
||||
debug("querying remote host '%s' for info on '%s'", host, path);
|
||||
debug("querying remote host ‘%s’ for info on ‘%s’", host, path);
|
||||
|
||||
conn->to << cmdQueryPathInfos << PathSet{path};
|
||||
conn->to.flush();
|
||||
@@ -105,13 +105,6 @@ struct LegacySSHStore : public Store
|
||||
readLongLong(conn->from); // download size
|
||||
info->narSize = readLongLong(conn->from);
|
||||
|
||||
if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4) {
|
||||
auto s = readString(conn->from);
|
||||
info->narHash = s.empty() ? Hash() : Hash(s);
|
||||
conn->from >> info->ca;
|
||||
info->sigs = readStrings<StringSet>(conn->from);
|
||||
}
|
||||
|
||||
auto s = readString(conn->from);
|
||||
assert(s == "");
|
||||
|
||||
@@ -120,10 +113,10 @@ struct LegacySSHStore : public Store
|
||||
}
|
||||
|
||||
void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
bool repair, bool dontCheckSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override
|
||||
{
|
||||
debug("adding path '%s' to remote host '%s'", info.path, host);
|
||||
debug("adding path ‘%s’ to remote host ‘%s’", info.path, host);
|
||||
|
||||
auto conn(connections->get());
|
||||
|
||||
@@ -141,7 +134,8 @@ struct LegacySSHStore : public Store
|
||||
conn->to.flush();
|
||||
|
||||
if (readInt(conn->from) != 1)
|
||||
throw Error("failed to add path '%s' to remote host '%s', info.path, host");
|
||||
throw Error("failed to add path ‘%s’ to remote host ‘%s’, info.path, host");
|
||||
|
||||
}
|
||||
|
||||
void narFromPath(const Path & path, Sink & sink) override
|
||||
@@ -174,11 +168,11 @@ struct LegacySSHStore : public Store
|
||||
|
||||
Path addToStore(const string & name, const Path & srcPath,
|
||||
bool recursive, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override
|
||||
PathFilter & filter, bool repair) override
|
||||
{ unsupported(); }
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references, RepairFlag repair) override
|
||||
const PathSet & references, bool repair) override
|
||||
{ unsupported(); }
|
||||
|
||||
BuildResult buildDerivation(const Path & drvPath, const BasicDerivation & drv,
|
||||
@@ -255,8 +249,7 @@ struct LegacySSHStore : public Store
|
||||
out.insert(res.begin(), res.end());
|
||||
}
|
||||
|
||||
PathSet queryValidPaths(const PathSet & paths,
|
||||
SubstituteFlag maybeSubstitute = NoSubstitute) override
|
||||
PathSet queryValidPaths(const PathSet & paths, bool maybeSubstitute = false) override
|
||||
{
|
||||
auto conn(connections->get());
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ static void atomicWrite(const Path & path, const std::string & s)
|
||||
AutoDelete del(tmp, false);
|
||||
writeFile(tmp, s);
|
||||
if (rename(tmp.c_str(), path.c_str()))
|
||||
throw SysError(format("renaming '%1%' to '%2%'") % tmp % path);
|
||||
throw SysError(format("renaming ‘%1%’ to ‘%2%’") % tmp % path);
|
||||
del.cancel();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ struct LocalStoreAccessor : public FSAccessor
|
||||
{
|
||||
Path storePath = store->toStorePath(path);
|
||||
if (!store->isValidPath(storePath))
|
||||
throw InvalidPath(format("path '%1%' is not a valid store path") % storePath);
|
||||
throw InvalidPath(format("path ‘%1%’ is not a valid store path") % storePath);
|
||||
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
|
||||
}
|
||||
|
||||
@@ -33,11 +33,11 @@ struct LocalStoreAccessor : public FSAccessor
|
||||
struct stat st;
|
||||
if (lstat(realPath.c_str(), &st)) {
|
||||
if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false};
|
||||
throw SysError(format("getting status of '%1%'") % path);
|
||||
throw SysError(format("getting status of ‘%1%’") % path);
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode))
|
||||
throw Error(format("file '%1%' has unsupported type") % path);
|
||||
throw Error(format("file ‘%1%’ has unsupported type") % path);
|
||||
|
||||
return {
|
||||
S_ISREG(st.st_mode) ? Type::tRegular :
|
||||
@@ -80,7 +80,7 @@ ref<FSAccessor> LocalFSStore::getFSAccessor()
|
||||
void LocalFSStore::narFromPath(const Path & path, Sink & sink)
|
||||
{
|
||||
if (!isValidPath(path))
|
||||
throw Error(format("path '%s' is not valid") % path);
|
||||
throw Error(format("path ‘%s’ is not valid") % path);
|
||||
dumpPath(getRealStoreDir() + std::string(path, storeDir.size()), sink);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,6 @@
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
|
||||
@@ -51,8 +47,6 @@ LocalStore::LocalStore(const Params & params)
|
||||
, reservedPath(dbDir + "/reserved")
|
||||
, schemaPath(dbDir + "/schema")
|
||||
, trashDir(realStoreDir + "/trash")
|
||||
, tempRootsDir(stateDir + "/temproots")
|
||||
, fnTempRoots(fmt("%s/%d", tempRootsDir, getpid()))
|
||||
, publicKeys(getDefaultPublicKeys())
|
||||
{
|
||||
auto state(_state.lock());
|
||||
@@ -63,7 +57,7 @@ LocalStore::LocalStore(const Params & params)
|
||||
createDirs(linksDir);
|
||||
Path profilesDir = stateDir + "/profiles";
|
||||
createDirs(profilesDir);
|
||||
createDirs(tempRootsDir);
|
||||
createDirs(stateDir + "/temproots");
|
||||
createDirs(dbDir);
|
||||
Path gcRootsDir = stateDir + "/gcroots";
|
||||
if (!pathExists(gcRootsDir)) {
|
||||
@@ -78,24 +72,24 @@ LocalStore::LocalStore(const Params & params)
|
||||
Path perUserDir = profilesDir + "/per-user";
|
||||
createDirs(perUserDir);
|
||||
if (chmod(perUserDir.c_str(), 01777) == -1)
|
||||
throw SysError(format("could not set permissions on '%1%' to 1777") % perUserDir);
|
||||
throw SysError(format("could not set permissions on ‘%1%’ to 1777") % perUserDir);
|
||||
|
||||
mode_t perm = 01775;
|
||||
|
||||
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
||||
if (!gr)
|
||||
printError(format("warning: the group '%1%' specified in 'build-users-group' does not exist")
|
||||
printError(format("warning: the group ‘%1%’ specified in ‘build-users-group’ does not exist")
|
||||
% settings.buildUsersGroup);
|
||||
else {
|
||||
struct stat st;
|
||||
if (stat(realStoreDir.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path '%1%'") % realStoreDir);
|
||||
throw SysError(format("getting attributes of path ‘%1%’") % realStoreDir);
|
||||
|
||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
||||
if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
|
||||
throw SysError(format("changing ownership of path '%1%'") % realStoreDir);
|
||||
throw SysError(format("changing ownership of path ‘%1%’") % realStoreDir);
|
||||
if (chmod(realStoreDir.c_str(), perm) == -1)
|
||||
throw SysError(format("changing permissions on path '%1%'") % realStoreDir);
|
||||
throw SysError(format("changing permissions on path ‘%1%’") % realStoreDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,10 +100,10 @@ LocalStore::LocalStore(const Params & params)
|
||||
struct stat st;
|
||||
while (path != "/") {
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting status of '%1%'") % path);
|
||||
throw SysError(format("getting status of ‘%1%’") % path);
|
||||
if (S_ISLNK(st.st_mode))
|
||||
throw Error(format(
|
||||
"the path '%1%' is a symlink; "
|
||||
"the path ‘%1%’ is a symlink; "
|
||||
"this is not allowed for the Nix store and its parent directories")
|
||||
% path);
|
||||
path = dirOf(path);
|
||||
@@ -244,24 +238,12 @@ LocalStore::LocalStore(const Params & params)
|
||||
|
||||
LocalStore::~LocalStore()
|
||||
{
|
||||
std::shared_future<void> future;
|
||||
|
||||
{
|
||||
auto state(_state.lock());
|
||||
if (state->gcRunning)
|
||||
future = state->gcFuture;
|
||||
}
|
||||
|
||||
if (future.valid()) {
|
||||
printError("waiting for auto-GC to finish on exit...");
|
||||
future.get();
|
||||
}
|
||||
auto state(_state.lock());
|
||||
|
||||
try {
|
||||
auto state(_state.lock());
|
||||
if (state->fdTempRoots) {
|
||||
state->fdTempRoots = -1;
|
||||
unlink(fnTempRoots.c_str());
|
||||
unlink(state->fnTempRoots.c_str());
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
@@ -281,7 +263,7 @@ int LocalStore::getSchema()
|
||||
if (pathExists(schemaPath)) {
|
||||
string s = readFile(schemaPath);
|
||||
if (!string2Int(s, curSchema))
|
||||
throw Error(format("'%1%' is corrupt") % schemaPath);
|
||||
throw Error(format("‘%1%’ is corrupt") % schemaPath);
|
||||
}
|
||||
return curSchema;
|
||||
}
|
||||
@@ -290,24 +272,14 @@ int LocalStore::getSchema()
|
||||
void LocalStore::openDB(State & state, bool create)
|
||||
{
|
||||
if (access(dbDir.c_str(), R_OK | W_OK))
|
||||
throw SysError(format("Nix database directory '%1%' is not writable") % dbDir);
|
||||
throw SysError(format("Nix database directory ‘%1%’ is not writable") % dbDir);
|
||||
|
||||
/* Open the Nix database. */
|
||||
string dbPath = dbDir + "/db.sqlite";
|
||||
auto & db(state.db);
|
||||
if (sqlite3_open_v2(dbPath.c_str(), &db.db,
|
||||
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
|
||||
throw Error(format("cannot open Nix database '%1%'") % dbPath);
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
/* The cygwin version of sqlite3 has a patch which calls
|
||||
SetDllDirectory("/usr/bin") on init. It was intended to fix extension
|
||||
loading, which we don't use, and the effect of SetDllDirectory is
|
||||
inherited by child processes, and causes libraries to be loaded from
|
||||
/usr/bin instead of $PATH. This breaks quite a few things (e.g.
|
||||
checkPhase on openssh), so we set it back to default behaviour. */
|
||||
SetDllDirectoryW(L"");
|
||||
#endif
|
||||
throw Error(format("cannot open Nix database ‘%1%’") % dbPath);
|
||||
|
||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||
throwSQLiteError(db, "setting timeout");
|
||||
@@ -392,7 +364,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||
| 0444
|
||||
| (st.st_mode & S_IXUSR ? 0111 : 0);
|
||||
if (chmod(path.c_str(), mode) == -1)
|
||||
throw SysError(format("changing mode of '%1%' to %2$o") % path % mode);
|
||||
throw SysError(format("changing mode of ‘%1%’ to %2$o") % path % mode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -410,7 +382,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
|
||||
#endif
|
||||
throw SysError(format("changing modification time of '%1%'") % path);
|
||||
throw SysError(format("changing modification time of ‘%1%’") % path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +391,7 @@ void canonicaliseTimestampAndPermissions(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
throw SysError(format("getting attributes of path ‘%1%’") % path);
|
||||
canonicaliseTimestampAndPermissions(path, st);
|
||||
}
|
||||
|
||||
@@ -434,17 +406,17 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
setattrlist() to remove other attributes as well. */
|
||||
if (lchflags(path.c_str(), 0)) {
|
||||
if (errno != ENOTSUP)
|
||||
throw SysError(format("clearing flags of path '%1%'") % path);
|
||||
throw SysError(format("clearing flags of path ‘%1%’") % path);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
throw SysError(format("getting attributes of path ‘%1%’") % path);
|
||||
|
||||
/* Really make sure that the path is of a supported type. */
|
||||
if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)))
|
||||
throw Error(format("file '%1%' has an unsupported type") % path);
|
||||
throw Error(format("file ‘%1%’ has an unsupported type") % path);
|
||||
|
||||
#if __linux__
|
||||
/* Remove extended attributes / ACLs. */
|
||||
@@ -452,19 +424,19 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
|
||||
if (eaSize < 0) {
|
||||
if (errno != ENOTSUP)
|
||||
throw SysError("querying extended attributes of '%s'", path);
|
||||
throw SysError("querying extended attributes of ‘%s’", path);
|
||||
} else if (eaSize > 0) {
|
||||
std::vector<char> eaBuf(eaSize);
|
||||
|
||||
if ((eaSize = llistxattr(path.c_str(), eaBuf.data(), eaBuf.size())) < 0)
|
||||
throw SysError("querying extended attributes of '%s'", path);
|
||||
throw SysError("querying extended attributes of ‘%s’", path);
|
||||
|
||||
for (auto & eaName: tokenizeString<Strings>(std::string(eaBuf.data(), eaSize), std::string("\000", 1))) {
|
||||
/* Ignore SELinux security labels since these cannot be
|
||||
removed even by root. */
|
||||
if (eaName == "security.selinux") continue;
|
||||
if (lremovexattr(path.c_str(), eaName.c_str()) == -1)
|
||||
throw SysError("removing extended attribute '%s' from '%s'", eaName, path);
|
||||
throw SysError("removing extended attribute ‘%s’ from ‘%s’", eaName, path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -478,7 +450,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
|
||||
assert(!S_ISDIR(st.st_mode));
|
||||
if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
|
||||
throw BuildError(format("invalid ownership on file '%1%'") % path);
|
||||
throw BuildError(format("invalid ownership on file ‘%1%’") % path);
|
||||
mode_t mode = st.st_mode & ~S_IFMT;
|
||||
assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
|
||||
return;
|
||||
@@ -502,7 +474,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
|
||||
if (!S_ISLNK(st.st_mode) &&
|
||||
chown(path.c_str(), geteuid(), getegid()) == -1)
|
||||
#endif
|
||||
throw SysError(format("changing owner of '%1%' to %2%")
|
||||
throw SysError(format("changing owner of ‘%1%’ to %2%")
|
||||
% path % geteuid());
|
||||
}
|
||||
|
||||
@@ -522,11 +494,11 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & ino
|
||||
be a symlink, since we can't change its ownership. */
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path '%1%'") % path);
|
||||
throw SysError(format("getting attributes of path ‘%1%’") % path);
|
||||
|
||||
if (st.st_uid != geteuid()) {
|
||||
assert(S_ISLNK(st.st_mode));
|
||||
throw Error(format("wrong ownership of top-level store path '%1%'") % path);
|
||||
throw Error(format("wrong ownership of top-level store path ‘%1%’") % path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +519,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
||||
if (drv.isFixedOutput()) {
|
||||
DerivationOutputs::const_iterator out = drv.outputs.find("out");
|
||||
if (out == drv.outputs.end())
|
||||
throw Error(format("derivation '%1%' does not have an output named 'out'") % drvPath);
|
||||
throw Error(format("derivation ‘%1%’ does not have an output named ‘out’") % drvPath);
|
||||
|
||||
bool recursive; Hash h;
|
||||
out->second.parseHashInfo(recursive, h);
|
||||
@@ -555,7 +527,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
||||
|
||||
StringPairs::const_iterator j = drv.env.find("out");
|
||||
if (out->second.path != outPath || j == drv.env.end() || j->second != outPath)
|
||||
throw Error(format("derivation '%1%' has incorrect output '%2%', should be '%3%'")
|
||||
throw Error(format("derivation ‘%1%’ has incorrect output ‘%2%’, should be ‘%3%’")
|
||||
% drvPath % out->second.path % outPath);
|
||||
}
|
||||
|
||||
@@ -572,7 +544,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
||||
Path outPath = makeOutputPath(i.first, h, drvName);
|
||||
StringPairs::const_iterator j = drv.env.find(i.first);
|
||||
if (i.second.path != outPath || j == drv.env.end() || j->second != outPath)
|
||||
throw Error(format("derivation '%1%' has incorrect output '%2%', should be '%3%'")
|
||||
throw Error(format("derivation ‘%1%’ has incorrect output ‘%2%’, should be ‘%3%’")
|
||||
% drvPath % i.second.path % outPath);
|
||||
}
|
||||
}
|
||||
@@ -586,7 +558,7 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||
|
||||
state.stmtRegisterValidPath.use()
|
||||
(info.path)
|
||||
(info.narHash.to_string(Base16))
|
||||
("sha256:" + printHash(info.narHash))
|
||||
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
||||
(info.deriver, info.deriver != "")
|
||||
(info.narSize, info.narSize != 0)
|
||||
@@ -628,6 +600,20 @@ uint64_t LocalStore::addValidPath(State & state,
|
||||
}
|
||||
|
||||
|
||||
Hash parseHashField(const Path & path, const string & s)
|
||||
{
|
||||
string::size_type colon = s.find(':');
|
||||
if (colon == string::npos)
|
||||
throw Error(format("corrupt hash ‘%1%’ in valid-path entry for ‘%2%’")
|
||||
% s % path);
|
||||
HashType ht = parseHashType(string(s, 0, colon));
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type ‘%1%’ in valid-path entry for ‘%2%’")
|
||||
% string(s, 0, colon) % path);
|
||||
return parseHash(ht, string(s, colon + 1));
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::queryPathInfoUncached(const Path & path,
|
||||
std::function<void(std::shared_ptr<ValidPathInfo>)> success,
|
||||
std::function<void(std::exception_ptr exc)> failure)
|
||||
@@ -650,11 +636,7 @@ void LocalStore::queryPathInfoUncached(const Path & path,
|
||||
|
||||
info->id = useQueryPathInfo.getInt(0);
|
||||
|
||||
try {
|
||||
info->narHash = Hash(useQueryPathInfo.getStr(1));
|
||||
} catch (BadHash & e) {
|
||||
throw Error("in valid-path entry for '%s': %s", path, e.what());
|
||||
}
|
||||
info->narHash = parseHashField(path, useQueryPathInfo.getStr(1));
|
||||
|
||||
info->registrationTime = useQueryPathInfo.getInt(2);
|
||||
|
||||
@@ -689,7 +671,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info)
|
||||
{
|
||||
state.stmtUpdatePathInfo.use()
|
||||
(info.narSize, info.narSize != 0)
|
||||
(info.narHash.to_string(Base16))
|
||||
("sha256:" + printHash(info.narHash))
|
||||
(info.ultimate ? 1 : 0, info.ultimate)
|
||||
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
||||
(info.ca, !info.ca.empty())
|
||||
@@ -702,7 +684,7 @@ uint64_t LocalStore::queryValidPathId(State & state, const Path & path)
|
||||
{
|
||||
auto use(state.stmtQueryPathInfo.use()(path));
|
||||
if (!use.next())
|
||||
throw Error(format("path '%1%' is not valid") % path);
|
||||
throw Error(format("path ‘%1%’ is not valid") % path);
|
||||
return use.getInt(0);
|
||||
}
|
||||
|
||||
@@ -722,7 +704,7 @@ bool LocalStore::isValidPathUncached(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
PathSet LocalStore::queryValidPaths(const PathSet & paths, SubstituteFlag maybeSubstitute)
|
||||
PathSet LocalStore::queryValidPaths(const PathSet & paths, bool maybeSubstitute)
|
||||
{
|
||||
PathSet res;
|
||||
for (auto & i : paths)
|
||||
@@ -869,7 +851,7 @@ void LocalStore::querySubstitutablePathInfos(const PathSet & paths,
|
||||
if (sub->storeDir != storeDir) continue;
|
||||
for (auto & path : paths) {
|
||||
if (infos.count(path)) continue;
|
||||
debug(format("checking substituter '%s' for path '%s'")
|
||||
debug(format("checking substituter ‘%s’ for path ‘%s’")
|
||||
% sub->getUri() % path);
|
||||
try {
|
||||
auto info = sub->queryPathInfo(path);
|
||||
@@ -950,7 +932,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
there are no referrers. */
|
||||
void LocalStore::invalidatePath(State & state, const Path & path)
|
||||
{
|
||||
debug(format("invalidating path '%1%'") % path);
|
||||
debug(format("invalidating path ‘%1%’") % path);
|
||||
|
||||
state.stmtInvalidatePath.use()(path).exec();
|
||||
|
||||
@@ -965,21 +947,21 @@ void LocalStore::invalidatePath(State & state, const Path & path)
|
||||
|
||||
|
||||
void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
bool repair, bool dontCheckSigs, std::shared_ptr<FSAccessor> accessor)
|
||||
{
|
||||
assert(info.narHash);
|
||||
|
||||
Hash h = hashString(htSHA256, *nar);
|
||||
if (h != info.narHash)
|
||||
throw Error("hash mismatch importing path '%s'; expected hash '%s', got '%s'",
|
||||
throw Error("hash mismatch importing path ‘%s’; expected hash ‘%s’, got ‘%s’",
|
||||
info.path, info.narHash.to_string(), h.to_string());
|
||||
|
||||
if (nar->size() != info.narSize)
|
||||
throw Error("size mismatch importing path '%s'; expected %s, got %s",
|
||||
throw Error("size mismatch importing path ‘%s’; expected %s, got %s",
|
||||
info.path, info.narSize, nar->size());
|
||||
|
||||
if (requireSigs && checkSigs && !info.checkSignatures(*this, publicKeys))
|
||||
throw Error("cannot add path '%s' because it lacks a valid signature", info.path);
|
||||
if (requireSigs && !dontCheckSigs && !info.checkSignatures(*this, publicKeys))
|
||||
throw Error("cannot add path ‘%s’ because it lacks a valid signature", info.path);
|
||||
|
||||
addTempRoot(info.path);
|
||||
|
||||
@@ -1003,8 +985,6 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> &
|
||||
StringSource source(*nar);
|
||||
restorePath(realPath, source);
|
||||
|
||||
autoGC();
|
||||
|
||||
canonicalisePathMetaData(realPath, -1);
|
||||
|
||||
optimisePath(realPath); // FIXME: combine with hashPath()
|
||||
@@ -1018,7 +998,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, const ref<std::string> &
|
||||
|
||||
|
||||
Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||
bool recursive, HashType hashAlgo, RepairFlag repair)
|
||||
bool recursive, HashType hashAlgo, bool repair)
|
||||
{
|
||||
Hash h = hashString(hashAlgo, dump);
|
||||
|
||||
@@ -1039,8 +1019,6 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||
|
||||
deletePath(realPath);
|
||||
|
||||
autoGC();
|
||||
|
||||
if (recursive) {
|
||||
StringSource source(dump);
|
||||
restorePath(realPath, source);
|
||||
@@ -1078,7 +1056,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||
|
||||
|
||||
Path LocalStore::addToStore(const string & name, const Path & _srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
|
||||
{
|
||||
Path srcPath(absPath(_srcPath));
|
||||
|
||||
@@ -1096,7 +1074,7 @@ Path LocalStore::addToStore(const string & name, const Path & _srcPath,
|
||||
|
||||
|
||||
Path LocalStore::addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references, RepairFlag repair)
|
||||
const PathSet & references, bool repair)
|
||||
{
|
||||
auto hash = hashString(htSHA256, s);
|
||||
auto dstPath = makeTextPath(name, hash, references);
|
||||
@@ -1113,8 +1091,6 @@ Path LocalStore::addTextToStore(const string & name, const string & s,
|
||||
|
||||
deletePath(realPath);
|
||||
|
||||
autoGC();
|
||||
|
||||
writeFile(realPath, s);
|
||||
|
||||
canonicalisePathMetaData(realPath, -1);
|
||||
@@ -1170,7 +1146,7 @@ void LocalStore::invalidatePathChecked(const Path & path)
|
||||
PathSet referrers; queryReferrers(*state, path, referrers);
|
||||
referrers.erase(path); /* ignore self-references */
|
||||
if (!referrers.empty())
|
||||
throw PathInUse(format("cannot delete path '%1%' because it is in use by %2%")
|
||||
throw PathInUse(format("cannot delete path ‘%1%’ because it is in use by %2%")
|
||||
% path % showPaths(referrers));
|
||||
invalidatePath(*state, path);
|
||||
}
|
||||
@@ -1180,7 +1156,7 @@ void LocalStore::invalidatePathChecked(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
bool LocalStore::verifyStore(bool checkContents, bool repair)
|
||||
{
|
||||
printError(format("reading the Nix store..."));
|
||||
|
||||
@@ -1215,13 +1191,13 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(i)));
|
||||
|
||||
/* Check the content hash (optionally - slow). */
|
||||
printMsg(lvlTalkative, format("checking contents of '%1%'") % i);
|
||||
printMsg(lvlTalkative, format("checking contents of ‘%1%’") % i);
|
||||
HashResult current = hashPath(info->narHash.type, i);
|
||||
|
||||
if (info->narHash != nullHash && info->narHash != current.first) {
|
||||
printError(format("path '%1%' was modified! "
|
||||
"expected hash '%2%', got '%3%'")
|
||||
% i % info->narHash.to_string() % current.first.to_string());
|
||||
printError(format("path ‘%1%’ was modified! "
|
||||
"expected hash ‘%2%’, got ‘%3%’")
|
||||
% i % printHash(info->narHash) % printHash(current.first));
|
||||
if (repair) repairPath(i); else errors = true;
|
||||
} else {
|
||||
|
||||
@@ -1229,14 +1205,14 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
|
||||
/* Fill in missing hashes. */
|
||||
if (info->narHash == nullHash) {
|
||||
printError(format("fixing missing hash on '%1%'") % i);
|
||||
printError(format("fixing missing hash on ‘%1%’") % i);
|
||||
info->narHash = current.first;
|
||||
update = true;
|
||||
}
|
||||
|
||||
/* Fill in missing narSize fields (from old stores). */
|
||||
if (info->narSize == 0) {
|
||||
printError(format("updating size field on '%1%' to %2%") % i % current.second);
|
||||
printError(format("updating size field on ‘%1%’ to %2%") % i % current.second);
|
||||
info->narSize = current.second;
|
||||
update = true;
|
||||
}
|
||||
@@ -1265,7 +1241,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||
|
||||
|
||||
void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||
PathSet & done, PathSet & validPaths, RepairFlag repair, bool & errors)
|
||||
PathSet & done, PathSet & validPaths, bool repair, bool & errors)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
@@ -1273,7 +1249,7 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||
done.insert(path);
|
||||
|
||||
if (!isStorePath(path)) {
|
||||
printError(format("path '%1%' is not in the Nix store") % path);
|
||||
printError(format("path ‘%1%’ is not in the Nix store") % path);
|
||||
auto state(_state.lock());
|
||||
invalidatePath(*state, path);
|
||||
return;
|
||||
@@ -1292,11 +1268,11 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||
}
|
||||
|
||||
if (canInvalidate) {
|
||||
printError(format("path '%1%' disappeared, removing from database...") % path);
|
||||
printError(format("path ‘%1%’ disappeared, removing from database...") % path);
|
||||
auto state(_state.lock());
|
||||
invalidatePath(*state, path);
|
||||
} else {
|
||||
printError(format("path '%1%' disappeared, but it still has valid referrers!") % path);
|
||||
printError(format("path ‘%1%’ disappeared, but it still has valid referrers!") % path);
|
||||
if (repair)
|
||||
try {
|
||||
repairPath(path);
|
||||
@@ -1335,7 +1311,7 @@ static void makeMutable(const Path & path)
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
if (errno == ELOOP) return; // it's a symlink
|
||||
throw SysError(format("opening file '%1%'") % path);
|
||||
throw SysError(format("opening file ‘%1%’") % path);
|
||||
}
|
||||
|
||||
unsigned int flags = 0, old;
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "sync.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -61,22 +59,8 @@ private:
|
||||
SQLiteStmt stmtQueryValidPaths;
|
||||
|
||||
/* The file to which we write our temporary roots. */
|
||||
Path fnTempRoots;
|
||||
AutoCloseFD fdTempRoots;
|
||||
|
||||
/* The last time we checked whether to do an auto-GC, or an
|
||||
auto-GC finished. */
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastGCCheck;
|
||||
|
||||
/* Whether auto-GC is running. If so, get gcFuture to wait for
|
||||
the GC to finish. */
|
||||
bool gcRunning = false;
|
||||
std::shared_future<void> gcFuture;
|
||||
|
||||
/* How much disk space was available after the previous
|
||||
auto-GC. If the current available disk space is below
|
||||
minFree but not much below availAfterGC, then there is no
|
||||
point in starting a new GC. */
|
||||
uint64_t availAfterGC = std::numeric_limits<uint64_t>::max();
|
||||
};
|
||||
|
||||
Sync<State, std::recursive_mutex> _state;
|
||||
@@ -91,13 +75,11 @@ public:
|
||||
const Path reservedPath;
|
||||
const Path schemaPath;
|
||||
const Path trashDir;
|
||||
const Path tempRootsDir;
|
||||
const Path fnTempRoots;
|
||||
|
||||
private:
|
||||
|
||||
Setting<bool> requireSigs{(Store*) this,
|
||||
settings.requireSigs,
|
||||
settings.signedBinaryCaches != "", // FIXME
|
||||
"require-sigs", "whether store paths should have a trusted signature on import"};
|
||||
|
||||
PublicKeys publicKeys;
|
||||
@@ -116,8 +98,7 @@ public:
|
||||
|
||||
bool isValidPathUncached(const Path & path) override;
|
||||
|
||||
PathSet queryValidPaths(const PathSet & paths,
|
||||
SubstituteFlag maybeSubstitute = NoSubstitute) override;
|
||||
PathSet queryValidPaths(const PathSet & paths, bool maybeSubstitute = false) override;
|
||||
|
||||
PathSet queryAllValidPaths() override;
|
||||
|
||||
@@ -141,22 +122,22 @@ public:
|
||||
SubstitutablePathInfos & infos) override;
|
||||
|
||||
void addToStore(const ValidPathInfo & info, const ref<std::string> & nar,
|
||||
RepairFlag repair, CheckSigsFlag checkSigs,
|
||||
bool repair, bool dontCheckSigs,
|
||||
std::shared_ptr<FSAccessor> accessor) override;
|
||||
|
||||
Path addToStore(const string & name, const Path & srcPath,
|
||||
bool recursive, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override;
|
||||
PathFilter & filter, bool repair) override;
|
||||
|
||||
/* Like addToStore(), but the contents of the path are contained
|
||||
in `dump', which is either a NAR serialisation (if recursive ==
|
||||
true) or simply the contents of a regular file (if recursive ==
|
||||
false). */
|
||||
Path addToStoreFromDump(const string & dump, const string & name,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair);
|
||||
bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references, RepairFlag repair) override;
|
||||
const PathSet & references, bool repair) override;
|
||||
|
||||
void buildPaths(const PathSet & paths, BuildMode buildMode) override;
|
||||
|
||||
@@ -176,7 +157,7 @@ private:
|
||||
typedef std::shared_ptr<AutoCloseFD> FDPtr;
|
||||
typedef list<FDPtr> FDs;
|
||||
|
||||
std::set<std::pair<pid_t, Path>> readTempRoots(FDs & fds);
|
||||
void readTempRoots(PathSet & tempRoots, FDs & fds);
|
||||
|
||||
public:
|
||||
|
||||
@@ -193,7 +174,7 @@ public:
|
||||
/* Optimise a single store path. */
|
||||
void optimisePath(const Path & path);
|
||||
|
||||
bool verifyStore(bool checkContents, RepairFlag repair) override;
|
||||
bool verifyStore(bool checkContents, bool repair) override;
|
||||
|
||||
/* Register the validity of a path, i.e., that `path' exists, that
|
||||
the paths referenced by it exists, and in the case of an output
|
||||
@@ -213,10 +194,6 @@ public:
|
||||
|
||||
void addSignatures(const Path & storePath, const StringSet & sigs) override;
|
||||
|
||||
/* If free disk space in /nix/store if below minFree, delete
|
||||
garbage until it exceeds maxFree. */
|
||||
void autoGC(bool sync = true);
|
||||
|
||||
private:
|
||||
|
||||
int getSchema();
|
||||
@@ -235,7 +212,7 @@ private:
|
||||
void invalidatePathChecked(const Path & path);
|
||||
|
||||
void verifyPath(const Path & path, const PathSet & store,
|
||||
PathSet & done, PathSet & validPaths, RepairFlag repair, bool & errors);
|
||||
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
|
||||
|
||||
void updatePathInfo(State & state, const ValidPathInfo & info);
|
||||
|
||||
@@ -261,9 +238,7 @@ private:
|
||||
|
||||
void findRoots(const Path & path, unsigned char type, Roots & roots);
|
||||
|
||||
Roots findRootsNoTemp();
|
||||
|
||||
PathSet findRuntimeRoots();
|
||||
void findRuntimeRoots(PathSet & roots);
|
||||
|
||||
void removeUnusedLinks(const GCState & state);
|
||||
|
||||
@@ -275,7 +250,7 @@ private:
|
||||
|
||||
InodeHash loadInodeHash();
|
||||
Strings readDirectoryIgnoringInodes(const Path & path, const InodeHash & inodeHash);
|
||||
void optimisePath_(Activity * act, OptimiseStats & stats, const Path & path, InodeHash & inodeHash);
|
||||
void optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash);
|
||||
|
||||
// Internal versions that are not wrapped in retry_sqlite.
|
||||
bool isValidPath_(State & state, const Path & path);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user