aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéophane Hufschmitt <theophane.hufschmitt@tweag.io>2022-06-29 13:44:14 +0200
committerThéophane Hufschmitt <theophane.hufschmitt@tweag.io>2022-06-29 13:44:14 +0200
commitf8fea9075c3ae913d8370875f92feef12e5b0682 (patch)
treef6901627c33bbd0fd1046ef9e08ad651623ed6c1
parentae4c9ef8e284eabf3624d9e9ad0f0b432e06da41 (diff)
parent7633764342ce9ddadbf209c17faf10004b3372b6 (diff)
Merge remote-tracking branch 'origin/master' into better-flake-new-error-message
-rw-r--r--.github/workflows/backport.yml2
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.version2
-rw-r--r--Makefile3
-rw-r--r--Makefile.config.in6
-rw-r--r--configure.ac33
-rwxr-xr-xdoc/manual/anchors.jq31
-rw-r--r--doc/manual/book.toml5
-rw-r--r--doc/manual/local.mk2
-rw-r--r--doc/manual/redirects.js337
-rw-r--r--doc/manual/src/SUMMARY.md.in1
-rw-r--r--doc/manual/src/advanced-topics/diff-hook.md2
-rw-r--r--doc/manual/src/command-ref/env-common.md32
-rw-r--r--doc/manual/src/command-ref/nix-build.md12
-rw-r--r--doc/manual/src/command-ref/nix-shell.md6
-rw-r--r--doc/manual/src/command-ref/nix-store.md2
-rw-r--r--doc/manual/src/command-ref/opt-common.md42
-rw-r--r--doc/manual/src/expressions/advanced-attributes.md22
-rw-r--r--doc/manual/src/expressions/builtin-constants.md2
-rw-r--r--doc/manual/src/expressions/derivations.md2
-rw-r--r--doc/manual/src/glossary.md36
-rw-r--r--doc/manual/src/installation/installing-binary.md3
-rw-r--r--doc/manual/src/release-notes/rl-2.9.md47
-rw-r--r--doc/manual/src/release-notes/rl-next.md26
-rw-r--r--flake.lock18
-rw-r--r--flake.nix124
-rw-r--r--misc/systemd/nix-daemon.service.in1
-rw-r--r--mk/libraries.mk6
-rw-r--r--mk/programs.mk4
-rwxr-xr-xscripts/create-darwin-volume.sh19
-rw-r--r--scripts/install-multi-user.sh13
-rw-r--r--src/libcmd/command.cc12
-rw-r--r--src/libcmd/command.hh6
-rw-r--r--src/libcmd/installables.cc15
-rw-r--r--src/libcmd/local.mk4
-rw-r--r--src/libcmd/repl.cc (renamed from src/nix/repl.cc)207
-rw-r--r--src/libexpr/eval-cache.cc18
-rw-r--r--src/libexpr/eval-inline.hh1
-rw-r--r--src/libexpr/eval.cc368
-rw-r--r--src/libexpr/eval.hh157
-rw-r--r--src/libexpr/flake/flake.cc21
-rw-r--r--src/libexpr/get-drvs.cc37
-rw-r--r--src/libexpr/get-drvs.hh2
-rw-r--r--src/libexpr/lexer.l2
-rw-r--r--src/libexpr/nixexpr.cc132
-rw-r--r--src/libexpr/nixexpr.hh49
-rw-r--r--src/libexpr/parser.y32
-rw-r--r--src/libexpr/primops.cc224
-rw-r--r--src/libexpr/primops/fetchTree.cc40
-rw-r--r--src/libexpr/tests/primops.cc24
-rw-r--r--src/libexpr/value-to-json.cc3
-rw-r--r--src/libexpr/value.hh6
-rw-r--r--src/libfetchers/fetch-settings.hh2
-rw-r--r--src/libfetchers/git.cc28
-rw-r--r--src/libfetchers/github.cc9
-rw-r--r--src/libfetchers/tarball.cc90
-rw-r--r--src/libstore/build/hook-instance.cc25
-rw-r--r--src/libstore/build/local-derivation-goal.cc14
-rw-r--r--src/libstore/build/substitution-goal.cc2
-rw-r--r--src/libstore/gc.cc2
-rw-r--r--src/libstore/globals.cc6
-rw-r--r--src/libstore/globals.hh7
-rw-r--r--src/libstore/http-binary-cache-store.cc7
-rw-r--r--src/libstore/local-binary-cache-store.cc3
-rw-r--r--src/libstore/local.mk11
-rw-r--r--src/libstore/lock.cc23
-rw-r--r--src/libstore/nar-info-disk-cache.cc7
-rw-r--r--src/libstore/nar-info.cc5
-rw-r--r--src/libstore/nar-info.hh1
-rw-r--r--src/libstore/remote-store.cc50
-rw-r--r--src/libstore/schema.sql2
-rw-r--r--src/libstore/store-api.cc23
-rw-r--r--src/libutil/args.hh2
-rw-r--r--src/libutil/error.hh9
-rw-r--r--src/libutil/hilite.cc4
-rw-r--r--src/libutil/json.cc1
-rw-r--r--src/libutil/ref.hh2
-rw-r--r--src/libutil/url.cc18
-rw-r--r--src/libutil/url.hh15
-rw-r--r--src/libutil/util.cc38
-rw-r--r--src/libutil/util.hh21
-rw-r--r--src/nix-build/nix-build.cc4
-rw-r--r--src/nix-collect-garbage/nix-collect-garbage.cc1
-rw-r--r--src/nix-env/nix-env.cc2
-rw-r--r--src/nix-store/nix-store.cc2
-rw-r--r--src/nix/flake.cc12
-rw-r--r--src/nix/flake.md14
-rw-r--r--src/nix/key-generate-secret.md2
-rw-r--r--src/nix/main.cc8
-rw-r--r--src/nix/profile.cc3
-rw-r--r--src/nix/registry.md2
-rw-r--r--src/nix/run.cc2
-rw-r--r--src/nix/search.cc31
-rw-r--r--src/nix/search.md13
-rw-r--r--src/nix/upgrade-nix.cc2
-rw-r--r--src/nix/upgrade-nix.md9
-rw-r--r--tests/ca/content-addressed.nix2
-rw-r--r--tests/common.sh.in2
-rw-r--r--tests/fetchTree-file.sh105
-rw-r--r--tests/flakes.sh14
-rw-r--r--tests/fmt.sh7
-rw-r--r--tests/github-flakes.nix16
-rw-r--r--tests/lang/parse-fail-eof-in-string.nix3
-rw-r--r--tests/local.mk7
-rw-r--r--tests/nix-copy-closure.nix4
-rw-r--r--tests/nss-preload.nix70
-rw-r--r--tests/plugins.sh5
-rw-r--r--tests/pure-eval.sh2
-rw-r--r--tests/remote-builds.nix2
-rw-r--r--tests/repl.sh5
-rw-r--r--tests/search.sh14
-rw-r--r--tests/setuid.nix4
-rw-r--r--tests/sourcehut-flakes.nix15
113 files changed, 2268 insertions, 746 deletions
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index dd481160f..3a2d4de0e 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -15,7 +15,7 @@ jobs:
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
- uses: zeebe-io/backport-action@v0.0.7
+ uses: zeebe-io/backport-action@v0.0.8
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aae5b93e0..fc6531ea5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -88,7 +88,7 @@ jobs:
fetch-depth: 0
- uses: cachix/install-nix-action@v17
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- - run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
+ - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v10
if: needs.check_cachix.outputs.secret == 'true'
with:
diff --git a/.version b/.version
index f3ac133c5..f161b5d80 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.9.0 \ No newline at end of file
+2.10.0 \ No newline at end of file
diff --git a/Makefile b/Makefile
index 33f47ca85..c1a1ce2c7 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,8 @@ makefiles = \
OPTIMIZE = 1
ifeq ($(OPTIMIZE), 1)
- GLOBAL_CXXFLAGS += -O3
+ GLOBAL_CXXFLAGS += -O3 $(CXXLTO)
+ GLOBAL_LDFLAGS += $(CXXLTO)
else
GLOBAL_CXXFLAGS += -O0 -U_FORTIFY_SOURCE
endif
diff --git a/Makefile.config.in b/Makefile.config.in
index 3505f337e..1c5405c6d 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -1,4 +1,3 @@
-HOST_OS = @host_os@
AR = @AR@
BDW_GC_LIBS = @BDW_GC_LIBS@
BOOST_LDFLAGS = @BOOST_LDFLAGS@
@@ -7,18 +6,20 @@ CC = @CC@
CFLAGS = @CFLAGS@
CXX = @CXX@
CXXFLAGS = @CXXFLAGS@
+CXXLTO = @CXXLTO@
EDITLINE_LIBS = @EDITLINE_LIBS@
ENABLE_S3 = @ENABLE_S3@
GTEST_LIBS = @GTEST_LIBS@
HAVE_LIBCPUID = @HAVE_LIBCPUID@
HAVE_SECCOMP = @HAVE_SECCOMP@
+HOST_OS = @host_os@
LDFLAGS = @LDFLAGS@
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
LIBCURL_LIBS = @LIBCURL_LIBS@
+LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
LOWDOWN_LIBS = @LOWDOWN_LIBS@
OPENSSL_LIBS = @OPENSSL_LIBS@
-LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
SHELL = @bash@
@@ -30,6 +31,7 @@ datadir = @datadir@
datarootdir = @datarootdir@
doc_generate = @doc_generate@
docdir = @docdir@
+embedded_sandbox_shell = @embedded_sandbox_shell@
exec_prefix = @exec_prefix@
includedir = @includedir@
libdir = @libdir@
diff --git a/configure.ac b/configure.ac
index 8a01c33ec..f0210ab78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -147,6 +147,20 @@ if test "x$GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC" = xyes; then
LDFLAGS="-latomic $LDFLAGS"
fi
+# LTO is currently broken with clang for unknown reasons; ld segfaults in the llvm plugin
+AC_ARG_ENABLE(lto, AS_HELP_STRING([--enable-lto],[Enable LTO (only supported with GCC) [default=no]]),
+ lto=$enableval, lto=no)
+if test "$lto" = yes; then
+ if $CXX --version | grep -q GCC; then
+ AC_SUBST(CXXLTO, [-flto=jobserver])
+ else
+ echo "error: LTO is only supported with GCC at the moment" >&2
+ exit 1
+ fi
+else
+ AC_SUBST(CXXLTO, [""])
+fi
+
PKG_PROG_PKG_CONFIG
AC_ARG_ENABLE(shared, AS_HELP_STRING([--enable-shared],[Build shared libraries for Nix [default=yes]]),
@@ -294,6 +308,25 @@ esac
AC_ARG_WITH(sandbox-shell, AS_HELP_STRING([--with-sandbox-shell=PATH],[path of a statically-linked shell to use as /bin/sh in sandboxes]),
sandbox_shell=$withval)
AC_SUBST(sandbox_shell)
+if test ${cross_compiling:-no} = no && ! test -z ${sandbox_shell+x}; then
+ AC_MSG_CHECKING([whether sandbox-shell has the standalone feature])
+ # busybox shell sometimes allows executing other busybox applets,
+ # even if they are not in the path, breaking our sandbox
+ if PATH= $sandbox_shell -c "busybox" 2>&1 | grep -qv "not found"; then
+ AC_MSG_RESULT(enabled)
+ AC_MSG_ERROR([Please disable busybox FEATURE_SH_STANDALONE])
+ else
+ AC_MSG_RESULT(disabled)
+ fi
+fi
+
+AC_ARG_ENABLE(embedded-sandbox-shell, AS_HELP_STRING([--enable-embedded-sandbox-shell],[include the sandbox shell in the Nix binary [default=no]]),
+ embedded_sandbox_shell=$enableval, embedded_sandbox_shell=no)
+AC_SUBST(embedded_sandbox_shell)
+if test "$embedded_sandbox_shell" = yes; then
+ AC_DEFINE(HAVE_EMBEDDED_SANDBOX_SHELL, 1, [Include the sandbox shell in the Nix binary.])
+fi
+
# Expand all variables in config.status.
test "$prefix" = NONE && prefix=$ac_default_prefix
diff --git a/doc/manual/anchors.jq b/doc/manual/anchors.jq
new file mode 100755
index 000000000..72309779c
--- /dev/null
+++ b/doc/manual/anchors.jq
@@ -0,0 +1,31 @@
+"\\[\\]\\{#(?<anchor>[^\\}]+?)\\}" as $empty_anchor_regex |
+"\\[(?<text>[^\\]]+?)\\]\\{#(?<anchor>[^\\}]+?)\\}" as $anchor_regex |
+
+
+def transform_anchors_html:
+ . | gsub($empty_anchor_regex; "<a name=\"" + .anchor + "\"></a>")
+ | gsub($anchor_regex; "<a href=\"#" + .anchor + "\" id=\"" + .anchor + "\">" + .text + "</a>");
+
+
+def transform_anchors_strip:
+ . | gsub($empty_anchor_regex; "")
+ | gsub($anchor_regex; .text);
+
+
+def map_contents_recursively(transformer):
+ . + {
+ Chapter: (.Chapter + {
+ content: .Chapter.content | transformer,
+ sub_items: .Chapter.sub_items | map(map_contents_recursively(transformer)),
+ }),
+ };
+
+
+def process_command:
+ .[0] as $context |
+ .[1] as $body |
+ $body + {
+ sections: $body.sections | map(map_contents_recursively(if $context.renderer == "html" then transform_anchors_html else transform_anchors_strip end)),
+ };
+
+process_command
diff --git a/doc/manual/book.toml b/doc/manual/book.toml
index fee41dfb3..5f78a7614 100644
--- a/doc/manual/book.toml
+++ b/doc/manual/book.toml
@@ -1,2 +1,7 @@
[output.html]
additional-css = ["custom.css"]
+additional-js = ["redirects.js"]
+
+[preprocessor.anchors]
+renderers = ["html"]
+command = "jq --from-file doc/manual/anchors.jq"
diff --git a/doc/manual/local.mk b/doc/manual/local.mk
index c1ce8aaeb..371ed6f21 100644
--- a/doc/manual/local.mk
+++ b/doc/manual/local.mk
@@ -97,7 +97,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
done
@touch $@
-$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/expressions/builtins.md $(call rwildcard, $(d)/src, *.md)
+$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/expressions/builtins.md $(call rwildcard, $(d)/src, *.md)
$(trace-gen) RUST_LOG=warn mdbook build doc/manual -d $(DESTDIR)$(docdir)/manual
endif
diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js
new file mode 100644
index 000000000..19f928c7e
--- /dev/null
+++ b/doc/manual/redirects.js
@@ -0,0 +1,337 @@
+// Redirects from old DocBook manual.
+var redirects = {
+ "#part-advanced-topics": "advanced-topics/advanced-topics.html",
+ "#chap-tuning-cores-and-jobs": "advanced-topics/cores-vs-jobs.html",
+ "#chap-diff-hook": "advanced-topics/diff-hook.html",
+ "#check-dirs-are-unregistered": "advanced-topics/diff-hook.html#check-dirs-are-unregistered",
+ "#chap-distributed-builds": "advanced-topics/distributed-builds.html",
+ "#chap-post-build-hook": "advanced-topics/post-build-hook.html",
+ "#chap-post-build-hook-caveats": "advanced-topics/post-build-hook.html#implementation-caveats",
+ "#part-command-ref": "command-ref/command-ref.html",
+ "#conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation",
+ "#conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges",
+ "#conf-allowed-uris": "command-ref/conf-file.html#conf-allowed-uris",
+ "#conf-allowed-users": "command-ref/conf-file.html#conf-allowed-users",
+ "#conf-auto-optimise-store": "command-ref/conf-file.html#conf-auto-optimise-store",
+ "#conf-binary-cache-public-keys": "command-ref/conf-file.html#conf-binary-cache-public-keys",
+ "#conf-binary-caches": "command-ref/conf-file.html#conf-binary-caches",
+ "#conf-build-compress-log": "command-ref/conf-file.html#conf-build-compress-log",
+ "#conf-build-cores": "command-ref/conf-file.html#conf-build-cores",
+ "#conf-build-extra-chroot-dirs": "command-ref/conf-file.html#conf-build-extra-chroot-dirs",
+ "#conf-build-extra-sandbox-paths": "command-ref/conf-file.html#conf-build-extra-sandbox-paths",
+ "#conf-build-fallback": "command-ref/conf-file.html#conf-build-fallback",
+ "#conf-build-max-jobs": "command-ref/conf-file.html#conf-build-max-jobs",
+ "#conf-build-max-log-size": "command-ref/conf-file.html#conf-build-max-log-size",
+ "#conf-build-max-silent-time": "command-ref/conf-file.html#conf-build-max-silent-time",
+ "#conf-build-repeat": "command-ref/conf-file.html#conf-build-repeat",
+ "#conf-build-timeout": "command-ref/conf-file.html#conf-build-timeout",
+ "#conf-build-use-chroot": "command-ref/conf-file.html#conf-build-use-chroot",
+ "#conf-build-use-sandbox": "command-ref/conf-file.html#conf-build-use-sandbox",
+ "#conf-build-use-substitutes": "command-ref/conf-file.html#conf-build-use-substitutes",
+ "#conf-build-users-group": "command-ref/conf-file.html#conf-build-users-group",
+ "#conf-builders": "command-ref/conf-file.html#conf-builders",
+ "#conf-builders-use-substitutes": "command-ref/conf-file.html#conf-builders-use-substitutes",
+ "#conf-compress-build-log": "command-ref/conf-file.html#conf-compress-build-log",
+ "#conf-connect-timeout": "command-ref/conf-file.html#conf-connect-timeout",
+ "#conf-cores": "command-ref/conf-file.html#conf-cores",
+ "#conf-diff-hook": "command-ref/conf-file.html#conf-diff-hook",
+ "#conf-enforce-determinism": "command-ref/conf-file.html#conf-enforce-determinism",
+ "#conf-env-keep-derivations": "command-ref/conf-file.html#conf-env-keep-derivations",
+ "#conf-extra-binary-caches": "command-ref/conf-file.html#conf-extra-binary-caches",
+ "#conf-extra-platforms": "command-ref/conf-file.html#conf-extra-platforms",
+ "#conf-extra-sandbox-paths": "command-ref/conf-file.html#conf-extra-sandbox-paths",
+ "#conf-extra-substituters": "command-ref/conf-file.html#conf-extra-substituters",
+ "#conf-fallback": "command-ref/conf-file.html#conf-fallback",
+ "#conf-fsync-metadata": "command-ref/conf-file.html#conf-fsync-metadata",
+ "#conf-gc-keep-derivations": "command-ref/conf-file.html#conf-gc-keep-derivations",
+ "#conf-gc-keep-outputs": "command-ref/conf-file.html#conf-gc-keep-outputs",
+ "#conf-hashed-mirrors": "command-ref/conf-file.html#conf-hashed-mirrors",
+ "#conf-http-connections": "command-ref/conf-file.html#conf-http-connections",
+ "#conf-keep-build-log": "command-ref/conf-file.html#conf-keep-build-log",
+ "#conf-keep-derivations": "command-ref/conf-file.html#conf-keep-derivations",
+ "#conf-keep-env-derivations": "command-ref/conf-file.html#conf-keep-env-derivations",
+ "#conf-keep-outputs": "command-ref/conf-file.html#conf-keep-outputs",
+ "#conf-max-build-log-size": "command-ref/conf-file.html#conf-max-build-log-size",
+ "#conf-max-free": "command-ref/conf-file.html#conf-max-free",
+ "#conf-max-jobs": "command-ref/conf-file.html#conf-max-jobs",
+ "#conf-max-silent-time": "command-ref/conf-file.html#conf-max-silent-time",
+ "#conf-min-free": "command-ref/conf-file.html#conf-min-free",
+ "#conf-narinfo-cache-negative-ttl": "command-ref/conf-file.html#conf-narinfo-cache-negative-ttl",
+ "#conf-narinfo-cache-positive-ttl": "command-ref/conf-file.html#conf-narinfo-cache-positive-ttl",
+ "#conf-netrc-file": "command-ref/conf-file.html#conf-netrc-file",
+ "#conf-plugin-files": "command-ref/conf-file.html#conf-plugin-files",
+ "#conf-post-build-hook": "command-ref/conf-file.html#conf-post-build-hook",
+ "#conf-pre-build-hook": "command-ref/conf-file.html#conf-pre-build-hook",
+ "#conf-repeat": "command-ref/conf-file.html#conf-repeat",
+ "#conf-require-sigs": "command-ref/conf-file.html#conf-require-sigs",
+ "#conf-restrict-eval": "command-ref/conf-file.html#conf-restrict-eval",
+ "#conf-run-diff-hook": "command-ref/conf-file.html#conf-run-diff-hook",
+ "#conf-sandbox": "command-ref/conf-file.html#conf-sandbox",
+ "#conf-sandbox-dev-shm-size": "command-ref/conf-file.html#conf-sandbox-dev-shm-size",
+ "#conf-sandbox-paths": "command-ref/conf-file.html#conf-sandbox-paths",
+ "#conf-secret-key-files": "command-ref/conf-file.html#conf-secret-key-files",
+ "#conf-show-trace": "command-ref/conf-file.html#conf-show-trace",
+ "#conf-stalled-download-timeout": "command-ref/conf-file.html#conf-stalled-download-timeout",
+ "#conf-substitute": "command-ref/conf-file.html#conf-substitute",
+ "#conf-substituters": "command-ref/conf-file.html#conf-substituters",
+ "#conf-system": "command-ref/conf-file.html#conf-system",
+ "#conf-system-features": "command-ref/conf-file.html#conf-system-features",
+ "#conf-tarball-ttl": "command-ref/conf-file.html#conf-tarball-ttl",
+ "#conf-timeout": "command-ref/conf-file.html#conf-timeout",
+ "#conf-trace-function-calls": "command-ref/conf-file.html#conf-trace-function-calls",
+ "#conf-trusted-binary-caches": "command-ref/conf-file.html#conf-trusted-binary-caches",
+ "#conf-trusted-public-keys": "command-ref/conf-file.html#conf-trusted-public-keys",
+ "#conf-trusted-substituters": "command-ref/conf-file.html#conf-trusted-substituters",
+ "#conf-trusted-users": "command-ref/conf-file.html#conf-trusted-users",
+ "#extra-sandbox-paths": "command-ref/conf-file.html#extra-sandbox-paths",
+ "#sec-conf-file": "command-ref/conf-file.html",
+ "#env-NIX_PATH": "command-ref/env-common.html#env-NIX_PATH",
+ "#env-common": "command-ref/env-common.html",
+ "#envar-remote": "command-ref/env-common.html#env-NIX_REMOTE",
+ "#sec-common-env": "command-ref/env-common.html",
+ "#ch-files": "command-ref/files.html",
+ "#ch-main-commands": "command-ref/main-commands.html",
+ "#opt-out-link": "command-ref/nix-build.html#opt-out-link",
+ "#sec-nix-build": "command-ref/nix-build.html",
+ "#sec-nix-channel": "command-ref/nix-channel.html",
+ "#sec-nix-collect-garbage": "command-ref/nix-collect-garbage.html",
+ "#sec-nix-copy-closure": "command-ref/nix-copy-closure.html",
+ "#sec-nix-daemon": "command-ref/nix-daemon.html",
+ "#refsec-nix-env-install-examples": "command-ref/nix-env.html#examples",
+ "#rsec-nix-env-install": "command-ref/nix-env.html#operation---install",
+ "#rsec-nix-env-set": "command-ref/nix-env.html#operation---set",
+ "#rsec-nix-env-set-flag": "command-ref/nix-env.html#operation---set-flag",
+ "#rsec-nix-env-upgrade": "command-ref/nix-env.html#operation---upgrade",
+ "#sec-nix-env": "command-ref/nix-env.html",
+ "#ssec-version-comparisons": "command-ref/nix-env.html#versions",
+ "#sec-nix-hash": "command-ref/nix-hash.html",
+ "#sec-nix-instantiate": "command-ref/nix-instantiate.html",
+ "#sec-nix-prefetch-url": "command-ref/nix-prefetch-url.html",
+ "#sec-nix-shell": "command-ref/nix-shell.html",
+ "#ssec-nix-shell-shebang": "command-ref/nix-shell.html#use-as-a--interpreter",
+ "#nixref-queries": "command-ref/nix-store.html#queries",
+ "#opt-add-root": "command-ref/nix-store.html#opt-add-root",
+ "#refsec-nix-store-dump": "command-ref/nix-store.html#operation---dump",
+ "#refsec-nix-store-export": "command-ref/nix-store.html#operation---export",
+ "#refsec-nix-store-import": "command-ref/nix-store.html#operation---import",
+ "#refsec-nix-store-query": "command-ref/nix-store.html#operation---query",
+ "#refsec-nix-store-verify": "command-ref/nix-store.html#operation---verify",
+ "#rsec-nix-store-gc": "command-ref/nix-store.html#operation---gc",
+ "#rsec-nix-store-generate-binary-cache-key": "command-ref/nix-store.html#operation---generate-binary-cache-key",
+ "#rsec-nix-store-realise": "command-ref/nix-store.html#operation---realise",
+ "#rsec-nix-store-serve": "command-ref/nix-store.html#operation---serve",
+ "#sec-nix-store": "command-ref/nix-store.html",
+ "#opt-I": "command-ref/opt-common.html#opt-I",
+ "#opt-attr": "command-ref/opt-common.html#opt-attr",
+ "#opt-common": "command-ref/opt-common.html",
+ "#opt-cores": "command-ref/opt-common.html#opt-cores",
+ "#opt-log-format": "command-ref/opt-common.html#opt-log-format",
+ "#opt-max-jobs": "command-ref/opt-common.html#opt-max-jobs",
+ "#opt-max-silent-time": "command-ref/opt-common.html#opt-max-silent-time",
+ "#opt-timeout": "command-ref/opt-common.html#opt-timeout",
+ "#sec-common-options": "command-ref/opt-common.html",
+ "#ch-utilities": "command-ref/utilities.html",
+ "#chap-hacking": "contributing/hacking.html",
+ "#adv-attr-allowSubstitutes": "expressions/advanced-attributes.html#adv-attr-allowSubstitutes",
+ "#adv-attr-allowedReferences": "expressions/advanced-attributes.html#adv-attr-allowedReferences",
+ "#adv-attr-allowedRequisites": "expressions/advanced-attributes.html#adv-attr-allowedRequisites",
+ "#adv-attr-disallowedReferences": "expressions/advanced-attributes.html#adv-attr-disallowedReferences",
+ "#adv-attr-disallowedRequisites": "expressions/advanced-attributes.html#adv-attr-disallowedRequisites",
+ "#adv-attr-exportReferencesGraph": "expressions/advanced-attributes.html#adv-attr-exportReferencesGraph",
+ "#adv-attr-impureEnvVars": "expressions/advanced-attributes.html#adv-attr-impureEnvVars",
+ "#adv-attr-outputHash": "expressions/advanced-attributes.html#adv-attr-outputHash",
+ "#adv-attr-outputHashAlgo": "expressions/advanced-attributes.html#adv-attr-outputHashAlgo",
+ "#adv-attr-outputHashMode": "expressions/advanced-attributes.html#adv-attr-outputHashMode",
+ "#adv-attr-passAsFile": "expressions/advanced-attributes.html#adv-attr-passAsFile",
+ "#adv-attr-preferLocalBuild": "expressions/advanced-attributes.html#adv-attr-preferLocalBuild",
+ "#fixed-output-drvs": "expressions/advanced-attributes.html#adv-attr-outputHash",
+ "#sec-advanced-attributes": "expressions/advanced-attributes.html",
+ "#sec-arguments": "expressions/arguments-variables.html",
+ "#sec-build-script": "expressions/build-script.html",
+ "#builtin-abort": "expressions/builtins.html#builtins-abort",
+ "#builtin-add": "expressions/builtins.html#builtins-add",
+ "#builtin-all": "expressions/builtins.html#builtins-all",
+ "#builtin-any": "expressions/builtins.html#builtins-any",
+ "#builtin-attrNames": "expressions/builtins.html#builtins-attrNames",
+ "#builtin-attrValues": "expressions/builtins.html#builtins-attrValues",
+ "#builtin-baseNameOf": "expressions/builtins.html#builtins-baseNameOf",
+ "#builtin-bitAnd": "expressions/builtins.html#builtins-bitAnd",
+ "#builtin-bitOr": "expressions/builtins.html#builtins-bitOr",
+ "#builtin-bitXor": "expressions/builtins.html#builtins-bitXor",
+ "#builtin-builtins": "expressions/builtins.html#builtins-builtins",
+ "#builtin-compareVersions": "expressions/builtins.html#builtins-compareVersions",
+ "#builtin-concatLists": "expressions/builtins.html#builtins-concatLists",
+ "#builtin-concatStringsSep": "expressions/builtins.html#builtins-concatStringsSep",
+ "#builtin-currentSystem": "expressions/builtins.html#builtins-currentSystem",
+ "#builtin-deepSeq": "expressions/builtins.html#builtins-deepSeq",
+ "#builtin-derivation": "expressions/builtins.html#builtins-derivation",
+ "#builtin-dirOf": "expressions/builtins.html#builtins-dirOf",
+ "#builtin-div": "expressions/builtins.html#builtins-div",
+ "#builtin-elem": "expressions/builtins.html#builtins-elem",
+ "#builtin-elemAt": "expressions/builtins.html#builtins-elemAt",
+ "#builtin-fetchGit": "expressions/builtins.html#builtins-fetchGit",
+ "#builtin-fetchTarball": "expressions/builtins.html#builtins-fetchTarball",
+ "#builtin-fetchurl": "expressions/builtins.html#builtins-fetchurl",
+ "#builtin-filterSource": "expressions/builtins.html#builtins-filterSource",
+ "#builtin-foldl-prime": "expressions/builtins.html#builtins-foldl-prime",
+ "#builtin-fromJSON": "expressions/builtins.html#builtins-fromJSON",
+ "#builtin-functionArgs": "expressions/builtins.html#builtins-functionArgs",
+ "#builtin-genList": "expressions/builtins.html#builtins-genList",
+ "#builtin-getAttr": "expressions/builtins.html#builtins-getAttr",
+ "#builtin-getEnv": "expressions/builtins.html#builtins-getEnv",
+ "#builtin-hasAttr": "expressions/builtins.html#builtins-hasAttr",
+ "#builtin-hashFile": "expressions/builtins.html#builtins-hashFile",
+ "#builtin-hashString": "expressions/builtins.html#builtins-hashString",
+ "#builtin-head": "expressions/builtins.html#builtins-head",
+ "#builtin-import": "expressions/builtins.html#builtins-import",
+ "#builtin-intersectAttrs": "expressions/builtins.html#builtins-intersectAttrs",
+ "#builtin-isAttrs": "expressions/builtins.html#builtins-isAttrs",
+ "#builtin-isBool": "expressions/builtins.html#builtins-isBool",
+ "#builtin-isFloat": "expressions/builtins.html#builtins-isFloat",
+ "#builtin-isFunction": "expressions/builtins.html#builtins-isFunction",
+ "#builtin-isInt": "expressions/builtins.html#builtins-isInt",
+ "#builtin-isList": "expressions/builtins.html#builtins-isList",
+ "#builtin-isNull": "expressions/builtins.html#builtins-isNull",
+ "#builtin-isString": "expressions/builtins.html#builtins-isString",
+ "#builtin-length": "expressions/builtins.html#builtins-length",
+ "#builtin-lessThan": "expressions/builtins.html#builtins-lessThan",
+ "#builtin-listToAttrs": "expressions/builtins.html#builtins-listToAttrs",
+ "#builtin-map": "expressions/builtins.html#builtins-map",
+ "#builtin-match": "expressions/builtins.html#builtins-match",
+ "#builtin-mul": "expressions/builtins.html#builtins-mul",
+ "#builtin-parseDrvName": "expressions/builtins.html#builtins-parseDrvName",
+ "#builtin-path": "expressions/builtins.html#builtins-path",
+ "#builtin-pathExists": "expressions/builtins.html#builtins-pathExists",
+ "#builtin-placeholder": "expressions/builtins.html#builtins-placeholder",
+ "#builtin-readDir": "expressions/builtins.html#builtins-readDir",
+ "#builtin-readFile": "expressions/builtins.html#builtins-readFile",
+ "#builtin-removeAttrs": "expressions/builtins.html#builtins-removeAttrs",
+ "#builtin-replaceStrings": "expressions/builtins.html#builtins-replaceStrings",
+ "#builtin-seq": "expressions/builtins.html#builtins-seq",
+ "#builtin-sort": "expressions/builtins.html#builtins-sort",
+ "#builtin-split": "expressions/builtins.html#builtins-split",
+ "#builtin-splitVersion": "expressions/builtins.html#builtins-splitVersion",
+ "#builtin-stringLength": "expressions/builtins.html#builtins-stringLength",
+ "#builtin-sub": "expressions/builtins.html#builtins-sub",
+ "#builtin-substring": "expressions/builtins.html#builtins-substring",
+ "#builtin-tail": "expressions/builtins.html#builtins-tail",
+ "#builtin-throw": "expressions/builtins.html#builtins-throw",
+ "#builtin-toFile": "expressions/builtins.html#builtins-toFile",
+ "#builtin-toJSON": "expressions/builtins.html#builtins-toJSON",
+ "#builtin-toPath": "expressions/builtins.html#builtins-toPath",
+ "#builtin-toString": "expressions/builtins.html#builtins-toString",
+ "#builtin-toXML": "expressions/builtins.html#builtins-toXML",
+ "#builtin-trace": "expressions/builtins.html#builtins-trace",
+ "#builtin-tryEval": "expressions/builtins.html#builtins-tryEval",
+ "#builtin-typeOf": "expressions/builtins.html#builtins-typeOf",
+ "#ssec-builtins": "expressions/builtins.html",
+ "#attr-system": "expressions/derivations.html#attr-system",
+ "#ssec-derivation": "expressions/derivations.html",
+ "#ch-expression-language": "expressions/expression-language.html",
+ "#sec-expression-syntax": "expressions/expression-syntax.html",
+ "#sec-generic-builder": "expressions/generic-builder.html",
+ "#sec-constructs": "expressions/language-constructs.html",
+ "#sect-let-expressions": "expressions/language-constructs.html#let-expressions",
+ "#ss-functions": "expressions/language-constructs.html#functions",
+ "#sec-language-operators": "expressions/language-operators.html",
+ "#table-operators": "expressions/language-operators.html",
+ "#ssec-values": "expressions/language-values.html",
+ "#sec-building-simple": "expressions/simple-building-testing.html",
+ "#ch-simple-expression": "expressions/simple-expression.html",
+ "#chap-writing-nix-expressions": "expressions/writing-nix-expressions.html",
+ "#gloss-closure": "glossary.html#gloss-closure",
+ "#gloss-derivation": "glossary.html#gloss-derivation",
+ "#gloss-deriver": "glossary.html#gloss-deriver",
+ "#gloss-nar": "glossary.html#gloss-nar",
+ "#gloss-output-path": "glossary.html#gloss-output-path",
+ "#gloss-profile": "glossary.html#gloss-profile",
+ "#gloss-reachable": "glossary.html#gloss-reachable",
+ "#gloss-reference": "glossary.html#gloss-reference",
+ "#gloss-substitute": "glossary.html#gloss-substitute",
+ "#gloss-user-env": "glossary.html#gloss-user-env",
+ "#gloss-validity": "glossary.html#gloss-validity",
+ "#part-glossary": "glossary.html",
+ "#sec-building-source": "installation/building-source.html",
+ "#ch-env-variables": "installation/env-variables.html",
+ "#sec-installer-proxy-settings": "installation/env-variables.html#proxy-environment-variables",
+ "#sec-nix-ssl-cert-file": "installation/env-variables.html#nix_ssl_cert_file",
+ "#sec-nix-ssl-cert-file-with-nix-daemon-and-macos": "installation/env-variables.html#nix_ssl_cert_file-with-macos-and-the-nix-daemon",
+ "#chap-installation": "installation/installation.html",
+ "#ch-installing-binary": "installation/installing-binary.html",
+ "#sect-macos-installation": "installation/installing-binary.html#macos-installation",
+ "#sect-macos-installation-change-store-prefix": "installation/installing-binary.html#macos-installation",
+ "#sect-macos-installation-encrypted-volume": "installation/installing-binary.html#macos-installation",
+ "#sect-macos-installation-recommended-notes": "installation/installing-binary.html#macos-installation",
+ "#sect-macos-installation-symlink": "installation/installing-binary.html#macos-installation",
+ "#sect-multi-user-installation": "installation/installing-binary.html#multi-user-installation",
+ "#sect-nix-install-binary-tarball": "installation/installing-binary.html#installing-from-a-binary-tarball",
+ "#sect-nix-install-pinned-version-url": "installation/installing-binary.html#installing-a-pinned-nix-version-from-a-url",
+ "#sect-single-user-installation": "installation/installing-binary.html#single-user-installation",
+ "#ch-installing-source": "installation/installing-source.html",
+ "#ssec-multi-user": "installation/multi-user.html",
+ "#ch-nix-security": "installation/nix-security.html",
+ "#sec-obtaining-source": "installation/obtaining-source.html",
+ "#sec-prerequisites-source": "installation/prerequisites-source.html",
+ "#sec-single-user": "installation/single-user.html",
+ "#ch-supported-platforms": "installation/supported-platforms.html",
+ "#ch-upgrading-nix": "installation/upgrading.html",
+ "#ch-about-nix": "introduction.html",
+ "#chap-introduction": "introduction.html",
+ "#ch-basic-package-mgmt": "package-management/basic-package-mgmt.html",
+ "#ssec-binary-cache-substituter": "package-management/binary-cache-substituter.html",
+ "#sec-channels": "package-management/channels.html",
+ "#ssec-copy-closure": "package-management/copy-closure.html",
+ "#sec-garbage-collection": "package-management/garbage-collection.html",
+ "#ssec-gc-roots": "package-management/garbage-collector-roots.html",
+ "#chap-package-management": "package-management/package-management.html",
+ "#sec-profiles": "package-management/profiles.html",
+ "#ssec-s3-substituter": "package-management/s3-substituter.html",
+ "#ssec-s3-substituter-anonymous-reads": "package-management/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache",
+ "#ssec-s3-substituter-authenticated-reads": "package-management/s3-substituter.html#authenticated-reads-to-your-s3-binary-cache",
+ "#ssec-s3-substituter-authenticated-writes": "package-management/s3-substituter.html#authenticated-writes-to-your-s3-compatible-binary-cache",
+ "#sec-sharing-packages": "package-management/sharing-packages.html",
+ "#ssec-ssh-substituter": "package-management/ssh-substituter.html",
+ "#chap-quick-start": "quick-start.html",
+ "#sec-relnotes": "release-notes/release-notes.html",
+ "#ch-relnotes-0.10.1": "release-notes/rl-0.10.1.html",
+ "#ch-relnotes-0.10": "release-notes/rl-0.10.html",
+ "#ssec-relnotes-0.11": "release-notes/rl-0.11.html",
+ "#ssec-relnotes-0.12": "release-notes/rl-0.12.html",
+ "#ssec-relnotes-0.13": "release-notes/rl-0.13.html",
+ "#ssec-relnotes-0.14": "release-notes/rl-0.14.html",
+ "#ssec-relnotes-0.15": "release-notes/rl-0.15.html",
+ "#ssec-relnotes-0.16": "release-notes/rl-0.16.html",
+ "#ch-relnotes-0.5": "release-notes/rl-0.5.html",
+ "#ch-relnotes-0.6": "release-notes/rl-0.6.html",
+ "#ch-relnotes-0.7": "release-notes/rl-0.7.html",
+ "#ch-relnotes-0.8.1": "release-notes/rl-0.8.1.html",
+ "#ch-relnotes-0.8": "release-notes/rl-0.8.html",
+ "#ch-relnotes-0.9.1": "release-notes/rl-0.9.1.html",
+ "#ch-relnotes-0.9.2": "release-notes/rl-0.9.2.html",
+ "#ch-relnotes-0.9": "release-notes/rl-0.9.html",
+ "#ssec-relnotes-1.0": "release-notes/rl-1.0.html",
+ "#ssec-relnotes-1.1": "release-notes/rl-1.1.html",
+ "#ssec-relnotes-1.10": "release-notes/rl-1.10.html",
+ "#ssec-relnotes-1.11.10": "release-notes/rl-1.11.10.html",
+ "#ssec-relnotes-1.11": "release-notes/rl-1.11.html",
+ "#ssec-relnotes-1.2": "release-notes/rl-1.2.html",
+ "#ssec-relnotes-1.3": "release-notes/rl-1.3.html",
+ "#ssec-relnotes-1.4": "release-notes/rl-1.4.html",
+ "#ssec-relnotes-1.5.1": "release-notes/rl-1.5.1.html",
+ "#ssec-relnotes-1.5.2": "release-notes/rl-1.5.2.html",
+ "#ssec-relnotes-1.5": "release-notes/rl-1.5.html",
+ "#ssec-relnotes-1.6.1": "release-notes/rl-1.6.1.html",
+ "#ssec-relnotes-1.6.0": "release-notes/rl-1.6.html",
+ "#ssec-relnotes-1.7": "release-notes/rl-1.7.html",
+ "#ssec-relnotes-1.8": "release-notes/rl-1.8.html",
+ "#ssec-relnotes-1.9": "release-notes/rl-1.9.html",
+ "#ssec-relnotes-2.0": "release-notes/rl-2.0.html",
+ "#ssec-relnotes-2.1": "release-notes/rl-2.1.html",
+ "#ssec-relnotes-2.2": "release-notes/rl-2.2.html",
+ "#ssec-relnotes-2.3": "release-notes/rl-2.3.html"
+};
+
+var isRoot = (document.location.pathname.endsWith('/') || document.location.pathname.endsWith('/index.html')) && path_to_root === '';
+if (isRoot && redirects[document.location.hash]) {
+ document.location.href = path_to_root + redirects[document.location.hash];
+}
diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in
index 860222337..825a8b4c0 100644
--- a/doc/manual/src/SUMMARY.md.in
+++ b/doc/manual/src/SUMMARY.md.in
@@ -72,6 +72,7 @@
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
+ - [Release 2.9 (2022-05-30)](release-notes/rl-2.9.md)
- [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md)
- [Release 2.7 (2022-03-07)](release-notes/rl-2.7.md)
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
diff --git a/doc/manual/src/advanced-topics/diff-hook.md b/doc/manual/src/advanced-topics/diff-hook.md
index 7a2622b3d..161e64b2a 100644
--- a/doc/manual/src/advanced-topics/diff-hook.md
+++ b/doc/manual/src/advanced-topics/diff-hook.md
@@ -101,7 +101,7 @@ In particular, notice the
`/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable.check` output. Nix
has copied the build results to that directory where you can examine it.
-> **Note**
+> []{#check-dirs-are-unregistered} **Note**
>
> Check paths are not protected against garbage collection, and this
> path will be deleted on the next garbage collection.
diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md
index 6e2403461..3f3eb6915 100644
--- a/doc/manual/src/command-ref/env-common.md
+++ b/doc/manual/src/command-ref/env-common.md
@@ -2,11 +2,11 @@
Most Nix commands interpret the following environment variables:
- - `IN_NIX_SHELL`\
+ - [`IN_NIX_SHELL`]{#env-IN_NIX_SHELL}\
Indicator that tells if the current environment was set up by
- `nix-shell`. Since Nix 2.0 the values are `"pure"` and `"impure"`
+ `nix-shell`. It can have the values `pure` or `impure`.
- - `NIX_PATH`\
+ - [`NIX_PATH`]{#env-NIX_PATH}\
A colon-separated list of directories used to look up Nix
expressions enclosed in angle brackets (i.e., `<path>`). For
instance, the value
@@ -44,7 +44,7 @@ Most Nix commands interpret the following environment variables:
The Nix search path can also be extended using the `-I` option to
many Nix commands, which takes precedence over `NIX_PATH`.
- - `NIX_IGNORE_SYMLINK_STORE`\
+ - [`NIX_IGNORE_SYMLINK_STORE`]{#env-NIX_IGNORE_SYMLINK_STORE}\
Normally, the Nix store directory (typically `/nix/store`) is not
allowed to contain any symlink components. This is to prevent
“impure” builds. Builders sometimes “canonicalise” paths by
@@ -66,41 +66,41 @@ Most Nix commands interpret the following environment variables:
Consult the mount 8 manual page for details.
- - `NIX_STORE_DIR`\
+ - [`NIX_STORE_DIR`]{#env-NIX_STORE_DIR}\
Overrides the location of the Nix store (default `prefix/store`).
- - `NIX_DATA_DIR`\
+ - [`NIX_DATA_DIR`]{#env-NIX_DATA_DIR}\
Overrides the location of the Nix static data directory (default
`prefix/share`).
- - `NIX_LOG_DIR`\
+ - [`NIX_LOG_DIR`]{#env-NIX_LOG_DIR}\
Overrides the location of the Nix log directory (default
`prefix/var/log/nix`).
- - `NIX_STATE_DIR`\
+ - [`NIX_STATE_DIR`]{#env-NIX_STATE_DIR}\
Overrides the location of the Nix state directory (default
`prefix/var/nix`).
- - `NIX_CONF_DIR`\
+ - [`NIX_CONF_DIR`]{#env-NIX_CONF_DIR}\
Overrides the location of the system Nix configuration directory
(default `prefix/etc/nix`).
- - `NIX_CONFIG`\
+ - [`NIX_CONFIG`]{#env-NIX_CONFIG}\
Applies settings from Nix configuration from the environment.
The content is treated as if it was read from a Nix configuration file.
Settings are separated by the newline character.
- - `NIX_USER_CONF_FILES`\
+ - [`NIX_USER_CONF_FILES`]{#env-NIX_USER_CONF_FILES}\
Overrides the location of the user Nix configuration files to load
from (defaults to the XDG spec locations). The variable is treated
as a list separated by the `:` token.
- - `TMPDIR`\
+ - [`TMPDIR`]{#env-TMPDIR}\
Use the specified directory to store temporary files. In particular,
this includes temporary build directories; these can take up
substantial amounts of disk space. The default is `/tmp`.
- - `NIX_REMOTE`\
+ - [`NIX_REMOTE`]{#env-NIX_REMOTE}\
This variable should be set to `daemon` if you want to use the Nix
daemon to execute Nix operations. This is necessary in [multi-user
Nix installations](../installation/multi-user.md). If the Nix
@@ -108,16 +108,16 @@ Most Nix commands interpret the following environment variables:
should be set to `unix://path/to/socket`. Otherwise, it should be
left unset.
- - `NIX_SHOW_STATS`\
+ - [`NIX_SHOW_STATS`]{#env-NIX_SHOW_STATS}\
If set to `1`, Nix will print some evaluation statistics, such as
the number of values allocated.
- - `NIX_COUNT_CALLS`\
+ - [`NIX_COUNT_CALLS`]{#env-NIX_COUNT_CALLS}\
If set to `1`, Nix will print how often functions were called during
Nix expression evaluation. This is useful for profiling your Nix
expressions.
- - `GC_INITIAL_HEAP_SIZE`\
+ - [`GC_INITIAL_HEAP_SIZE`]{#env-GC_INITIAL_HEAP_SIZE}\
If Nix has been configured to use the Boehm garbage collector, this
variable sets the initial size of the heap in bytes. It defaults to
384 MiB. Setting it to a low value reduces memory consumption, but
diff --git a/doc/manual/src/command-ref/nix-build.md b/doc/manual/src/command-ref/nix-build.md
index 43de7a6e6..49c6f3f55 100644
--- a/doc/manual/src/command-ref/nix-build.md
+++ b/doc/manual/src/command-ref/nix-build.md
@@ -12,6 +12,12 @@
[`--dry-run`]
[{`--out-link` | `-o`} *outlink*]
+# Disambiguation
+
+This man page describes the command `nix-build`, which is distinct from `nix
+build`. For documentation on the latter, run `nix build --help` or see `man
+nix3-build`.
+
# Description
The `nix-build` command builds the derivations described by the Nix
@@ -47,16 +53,16 @@ All options not listed here are passed to `nix-store
--realise`, except for `--arg` and `--attr` / `-A` which are passed to
`nix-instantiate`.
- - `--no-out-link`\
+ - [`--no-out-link`]{#opt-no-out-link}\
Do not create a symlink to the output path. Note that as a result
the output does not become a root of the garbage collector, and so
might be deleted by `nix-store
--gc`.
- - `--dry-run`\
+ - [`--dry-run`]{#opt-dry-run}\
Show what store paths would be built or downloaded.
- - `--out-link` / `-o` *outlink*\
+ - [`--out-link`]{#opt-out-link} / `-o` *outlink*\
Change the name of the symlink to the output path created from
`result` to *outlink*.
diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md
index a2b6d8a8e..840bccd25 100644
--- a/doc/manual/src/command-ref/nix-shell.md
+++ b/doc/manual/src/command-ref/nix-shell.md
@@ -15,6 +15,12 @@
[`--keep` *name*]
{{`--packages` | `-p`} {*packages* | *expressions*} … | [*path*]}
+# Disambiguation
+
+This man page describes the command `nix-shell`, which is distinct from `nix
+shell`. For documentation on the latter, run `nix shell --help` or see `man
+nix3-shell`.
+
# Description
The command `nix-shell` will build the dependencies of the specified
diff --git a/doc/manual/src/command-ref/nix-store.md b/doc/manual/src/command-ref/nix-store.md
index 7db9f0c1c..dc8faba68 100644
--- a/doc/manual/src/command-ref/nix-store.md
+++ b/doc/manual/src/command-ref/nix-store.md
@@ -22,7 +22,7 @@ This section lists the options that are common to all operations. These
options are allowed for every subcommand, though they may not always
have an effect.
- - `--add-root` *path*\
+ - [`--add-root`]{#opt-add-root} *path*\
Causes the result of a realisation (`--realise` and
`--force-realise`) to be registered as a root of the garbage
collector. *path* will be created as a symlink to the resulting
diff --git a/doc/manual/src/command-ref/opt-common.md b/doc/manual/src/command-ref/opt-common.md
index 7ee1a26bc..51d7de18a 100644
--- a/doc/manual/src/command-ref/opt-common.md
+++ b/doc/manual/src/command-ref/opt-common.md
@@ -2,13 +2,13 @@
Most Nix commands accept the following command-line options:
- - `--help`\
+ - [`--help`]{#opt-help}\
Prints out a summary of the command syntax and exits.
- - `--version`\
+ - [`--version`]{#opt-version}\
Prints out the Nix version number on standard output and exits.
- - `--verbose` / `-v`\
+ - [`--verbose`]{#opt-verbose} / `-v`\
Increases the level of verbosity of diagnostic messages printed on
standard error. For each Nix operation, the information printed on
standard output is well-defined; any diagnostic information is
@@ -37,14 +37,14 @@ Most Nix commands accept the following command-line options:
- 5\
“Vomit”: print vast amounts of debug information.
- - `--quiet`\
+ - [`--quiet`]{#opt-quiet}\
Decreases the level of verbosity of diagnostic messages printed on
standard error. This is the inverse option to `-v` / `--verbose`.
This option may be specified repeatedly. See the previous verbosity
levels list.
- - `--log-format` *format*\
+ - [`--log-format`]{#opt-log-format} *format*\
This option can be used to change the output of the log format, with
*format* being one of:
@@ -66,14 +66,14 @@ Most Nix commands accept the following command-line options:
- bar-with-logs\
Display the raw logs, with the progress bar at the bottom.
- - `--no-build-output` / `-Q`\
+ - [`--no-build-output`]{#opt-no-build-output} / `-Q`\
By default, output written by builders to standard output and
standard error is echoed to the Nix command's standard error. This
option suppresses this behaviour. Note that the builder's standard
output and error are always written to a log file in
`prefix/nix/var/log/nix`.
- - `--max-jobs` / `-j` *number*\
+ - [`--max-jobs`]{#opt-max-jobs} / `-j` *number*\
Sets the maximum number of build jobs that Nix will perform in
parallel to the specified number. Specify `auto` to use the number
of CPUs in the system. The default is specified by the `max-jobs`
@@ -83,7 +83,7 @@ Most Nix commands accept the following command-line options:
Setting it to `0` disallows building on the local machine, which is
useful when you want builds to happen only on remote builders.
- - `--cores`\
+ - [`--cores`]{#opt-cores}\
Sets the value of the `NIX_BUILD_CORES` environment variable in
the invocation of builders. Builders can use this variable at
their discretion to control the maximum amount of parallelism. For
@@ -94,18 +94,18 @@ Most Nix commands accept the following command-line options:
means that the builder should use all available CPU cores in the
system.
- - `--max-silent-time`\
+ - [`--max-silent-time`]{#opt-max-silent-time}\
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 `max-silent-time` configuration
setting. `0` means no time-out.
- - `--timeout`\
+ - [`--timeout`]{#opt-timeout}\
Sets the maximum number of seconds that a builder can run. The
default is specified by the `timeout` configuration setting. `0`
means no timeout.
- - `--keep-going` / `-k`\
+ - [`--keep-going`]{#opt-keep-going} / `-k`\
Keep going in case of failed builds, to the greatest extent
possible. That is, if building an input of some derivation fails,
Nix will still build the other inputs, but not the derivation
@@ -113,13 +113,13 @@ Most Nix commands accept the following command-line options:
for builds of substitutes), possibly killing builds in progress (in
case of parallel or distributed builds).
- - `--keep-failed` / `-K`\
+ - [`--keep-failed`]{#opt-keep-failed} / `-K`\
Specifies that in case of a build failure, the temporary directory
(usually in `/tmp`) in which the build takes place should not be
deleted. The path of the build directory is printed as an
informational message.
- - `--fallback`\
+ - [`--fallback`]{#opt-fallback}\
Whenever Nix attempts to build a derivation for which substitutes
are known for each output path, but realising the output paths
through the substitutes fails, fall back on building the derivation.
@@ -134,12 +134,12 @@ Most Nix commands accept the following command-line options:
failure in obtaining the substitutes to lead to a full build from
source (with the related consumption of resources).
- - `--readonly-mode`\
+ - [`--readonly-mode`]{#opt-readonly-mode}\
When this option is used, no attempt is made to open the Nix
database. Most Nix operations do need database access, so those
operations will fail.
- - `--arg` *name* *value*\
+ - [`--arg`]{#opt-arg} *name* *value*\
This option is accepted by `nix-env`, `nix-instantiate`,
`nix-shell` and `nix-build`. When evaluating Nix expressions, the
expression evaluator will automatically try to call functions that
@@ -170,13 +170,13 @@ Most Nix commands accept the following command-line options:
since the argument is a Nix string literal, you have to escape the
quotes.)
- - `--argstr` *name* *value*\
+ - [`--argstr`]{#opt-argstr} *name* *value*\
This option is like `--arg`, only the value is not a Nix
expression but a string. So instead of `--arg system
\"i686-linux\"` (the outer quotes are to keep the shell happy) you
can say `--argstr system i686-linux`.
- - `--attr` / `-A` *attrPath*\
+ - [`--attr`]{#opt-attr} / `-A` *attrPath*\
Select an attribute from the top-level Nix expression being
evaluated. (`nix-env`, `nix-instantiate`, `nix-build` and
`nix-shell` only.) The *attribute path* *attrPath* is a sequence
@@ -191,7 +191,7 @@ Most Nix commands accept the following command-line options:
attribute of the fourth element of the array in the `foo` attribute
of the top-level expression.
- - `--expr` / `-E`\
+ - [`--expr`]{#opt-expr} / `-E`\
Interpret the command line arguments as a list of Nix expressions to
be parsed and evaluated, rather than as a list of file names of Nix
expressions. (`nix-instantiate`, `nix-build` and `nix-shell` only.)
@@ -202,17 +202,17 @@ Most Nix commands accept the following command-line options:
use, give your expression to the `nix-shell -p` convenience flag
instead.
- - `-I` *path*\
+ - [`-I`]{#opt-I} *path*\
Add a path to the Nix expression search path. This option may be
given multiple times. See the `NIX_PATH` environment variable for
information on the semantics of the Nix search path. Paths added
through `-I` take precedence over `NIX_PATH`.
- - `--option` *name* *value*\
+ - [`--option`]{#opt-option} *name* *value*\
Set the Nix configuration option *name* to *value*. This overrides
settings in the Nix configuration file (see nix.conf5).
- - `--repair`\
+ - [`--repair`]{#opt-repair}\
Fix corrupted or missing store paths by redownloading or rebuilding
them. Note that this is slow because it requires computing a
cryptographic hash of the contents of every path in the closure of
diff --git a/doc/manual/src/expressions/advanced-attributes.md b/doc/manual/src/expressions/advanced-attributes.md
index 000595815..2e7e80ed0 100644
--- a/doc/manual/src/expressions/advanced-attributes.md
+++ b/doc/manual/src/expressions/advanced-attributes.md
@@ -2,7 +2,7 @@
Derivations can declare some infrequently used optional attributes.
- - `allowedReferences`\
+ - [`allowedReferences`]{#adv-attr-allowedReferences}\
The optional attribute `allowedReferences` specifies a list of legal
references (dependencies) of the output of the builder. For example,
@@ -17,7 +17,7 @@ Derivations can declare some infrequently used optional attributes.
booting Linux don’t have accidental dependencies on other paths in
the Nix store.
- - `allowedRequisites`\
+ - [`allowedRequisites`]{#adv-attr-allowedRequisites}\
This attribute is similar to `allowedReferences`, but it specifies
the legal requisites of the whole closure, so all the dependencies
recursively. For example,
@@ -30,7 +30,7 @@ Derivations can declare some infrequently used optional attributes.
runtime dependency than `foobar`, and in addition it enforces that
`foobar` itself doesn't introduce any other dependency itself.
- - `disallowedReferences`\
+ - [`disallowedReferences`]{#adv-attr-disallowedReferences}\
The optional attribute `disallowedReferences` specifies a list of
illegal references (dependencies) of the output of the builder. For
example,
@@ -42,7 +42,7 @@ Derivations can declare some infrequently used optional attributes.
enforces that the output of a derivation cannot have a direct
runtime dependencies on the derivation `foo`.
- - `disallowedRequisites`\
+ - [`disallowedRequisites`]{#adv-attr-disallowedRequisites}\
This attribute is similar to `disallowedReferences`, but it
specifies illegal requisites for the whole closure, so all the
dependencies recursively. For example,
@@ -55,7 +55,7 @@ Derivations can declare some infrequently used optional attributes.
dependency on `foobar` or any other derivation depending recursively
on `foobar`.
- - `exportReferencesGraph`\
+ - [`exportReferencesGraph`]{#adv-attr-exportReferencesGraph}\
This attribute allows builders access to the references graph of
their inputs. The attribute is a list of inputs in the Nix store
whose references graph the builder needs to know. The value of
@@ -84,7 +84,7 @@ Derivations can declare some infrequently used optional attributes.
with a Nix store containing the closure of a bootable NixOS
configuration).
- - `impureEnvVars`\
+ - [`impureEnvVars`]{#adv-attr-impureEnvVars}\
This attribute allows you to specify a list of environment variables
that should be passed from the environment of the calling user to
the builder. Usually, the environment is cleared completely when the
@@ -112,7 +112,7 @@ Derivations can declare some infrequently used optional attributes.
> environmental variables come from the environment of the
> `nix-build`.
- - `outputHash`; `outputHashAlgo`; `outputHashMode`\
+ - [`outputHash`]{#adv-attr-outputHash}; [`outputHashAlgo`]{#adv-attr-outputHashAlgo}; [`outputHashMode`]{#adv-attr-outputHashMode}\
These attributes declare that the derivation is a so-called
*fixed-output derivation*, which means that a cryptographic hash of
the output is already known in advance. When the build of a
@@ -208,7 +208,7 @@ Derivations can declare some infrequently used optional attributes.
[`nix-hash` command](../command-ref/nix-hash.md) for information
about converting to and from base-32 notation.)
- - `__contentAddressed`
+ - [`__contentAddressed`]{#adv-attr-__contentAddressed}
If this **experimental** attribute is set to true, then the derivation
outputs will be stored in a content-addressed location rather than the
traditional input-addressed one.
@@ -216,7 +216,7 @@ Derivations can declare some infrequently used optional attributes.
Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
- - `passAsFile`\
+ - [`passAsFile`]{#adv-attr-passAsFile}\
A list of names of attributes that should be passed via files rather
than environment variables. For example, if you have
@@ -234,7 +234,7 @@ Derivations can declare some infrequently used optional attributes.
builder, since most operating systems impose a limit on the size
of the environment (typically, a few hundred kilobyte).
- - `preferLocalBuild`\
+ - [`preferLocalBuild`]{#adv-attr-preferLocalBuild}\
If this attribute is set to `true` and [distributed building is
enabled](../advanced-topics/distributed-builds.md), then, if
possible, the derivation will be built locally instead of forwarded
@@ -242,7 +242,7 @@ Derivations can declare some infrequently used optional attributes.
where the cost of doing a download or remote build would exceed
the cost of building locally.
- - `allowSubstitutes`\
+ - [`allowSubstitutes`]{#adv-attr-allowSubstitutes}\
If this attribute is set to `false`, then Nix will always build this
derivation; it will not try to substitute its outputs. This is
useful for very trivial derivations (such as `writeText` in Nixpkgs)
diff --git a/doc/manual/src/expressions/builtin-constants.md b/doc/manual/src/expressions/builtin-constants.md
index 1404289e5..78d066a82 100644
--- a/doc/manual/src/expressions/builtin-constants.md
+++ b/doc/manual/src/expressions/builtin-constants.md
@@ -14,7 +14,7 @@ Here are the constants built into the Nix expression evaluator:
This allows a Nix expression to fall back gracefully on older Nix
installations that don’t have the desired built-in function.
- - `builtins.currentSystem`\
+ - [`builtins.currentSystem`]{#builtins-currentSystem}\
The built-in value `currentSystem` evaluates to the Nix platform
identifier for the Nix installation on which the expression is being
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
diff --git a/doc/manual/src/expressions/derivations.md b/doc/manual/src/expressions/derivations.md
index d26a33b7f..3391ec0d8 100644
--- a/doc/manual/src/expressions/derivations.md
+++ b/doc/manual/src/expressions/derivations.md
@@ -4,7 +4,7 @@ The most important built-in function is `derivation`, which is used to
describe a single derivation (a build action). It takes as input a set,
the attributes of which specify the inputs of the build.
- - There must be an attribute named `system` whose value must be a
+ - There must be an attribute named [`system`]{#attr-system} whose value must be a
string specifying a Nix system type, such as `"i686-linux"` or
`"x86_64-darwin"`. (To figure out your system type, run `nix -vv
--version`.) The build can only be performed on a machine and
diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md
index 71ff13275..3448b971b 100644
--- a/doc/manual/src/glossary.md
+++ b/doc/manual/src/glossary.md
@@ -1,48 +1,48 @@
# Glossary
- - derivation\
+ - [derivation]{#gloss-derivation}\
A description of a build action. The result of a derivation is a
store object. Derivations are typically specified in Nix expressions
using the [`derivation` primitive](expressions/derivations.md). These are
translated into low-level *store derivations* (implicitly by
`nix-env` and `nix-build`, or explicitly by `nix-instantiate`).
- - store\
+ - [store]{#gloss-store}\
The location in the file system where store objects live. Typically
`/nix/store`.
- - store path\
+ - [store path]{#gloss-store-path}\
The location in the file system of a store object, i.e., an
immediate child of the Nix store directory.
- - store object\
+ - [store object]{#gloss-store-object}\
A file that is an immediate child of the Nix store directory. These
can be regular files, but also entire directory trees. Store objects
can be sources (objects copied from outside of the store),
derivation outputs (objects produced by running a build action), or
derivations (files describing a build action).
- - substitute\
+ - [substitute]{#gloss-substitute}\
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 substitute builds the
store object by downloading a pre-built version of the store object
from some server.
- - purity\
+ - [purity]{#gloss-purity}\
The assumption that equal Nix derivations when run always produce
the same output. This cannot be guaranteed in general (e.g., a
builder can rely on external inputs such as the network or the
system time) but the Nix model assumes it.
- - Nix expression\
+ - [Nix expression]{#gloss-nix-expression}\
A high-level description of software packages and compositions
thereof. Deploying software using Nix entails writing Nix
expressions for your packages. Nix expressions are translated to
derivations that are stored in the Nix store. These derivations can
then be built.
- - reference\
+ - [reference]{#gloss-reference}\
A store path `P` is said to have a reference to a store path `Q` if
the store object at `P` contains the path `Q` somewhere. The
*references* of a store path are the set of store paths to which it
@@ -52,11 +52,11 @@
output paths), whereas an output path only references other output
paths.
- - reachable\
+ - [reachable]{#gloss-reachable}\
A store path `Q` is reachable from another store path `P` if `Q`
is in the *closure* of the *references* relation.
- - closure\
+ - [closure]{#gloss-closure}\
The closure of a store path is the set of store paths that are
directly or indirectly “reachable” from that store path; that is,
it’s the closure of the path under the *references* relation. For
@@ -71,34 +71,34 @@
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
references `R` then `R` is also in the closure of `P`.
- - output path\
+ - [output path]{#gloss-output-path}\
A store path produced by a derivation.
- - deriver\
+ - [deriver]{#gloss-deriver}\
The deriver of an *output path* is the store
derivation that built it.
- - validity\
+ - [validity]{#gloss-validity}\
A store path is considered *valid* if it exists in the file system,
is listed in the Nix database as being valid, and if all paths in
its closure are also valid.
- - user environment\
+ - [user environment]{#gloss-user-env}\
An automatically generated store object that consists of a set of
symlinks to “active” applications, i.e., other store paths. These
are generated automatically by
[`nix-env`](command-ref/nix-env.md). See *profiles*.
- - profile\
+ - [profile]{#gloss-profile}\
A symlink to the current *user environment* of a user, e.g.,
`/nix/var/nix/profiles/default`.
- - NAR\
+ - [NAR]{#gloss-nar}\
A *N*ix *AR*chive. This is a serialisation of a path in the Nix
store. It can contain regular files, directories and symbolic
links. NARs are generated and unpacked using `nix-store --dump`
and `nix-store --restore`.
- - `∅` \
+ - [`∅`]{#gloss-emtpy-set}\
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
- - `ε` \
+ - [`ε`]{#gloss-epsilon}\
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.
diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md
index e5fb50088..9fb9c80c3 100644
--- a/doc/manual/src/installation/installing-binary.md
+++ b/doc/manual/src/installation/installing-binary.md
@@ -186,7 +186,8 @@ and `/etc/zshrc` which you may remove.
> read-only root will prevent you from manually deleting the empty `/nix`
> mountpoint.
-# macOS Installation <a name="sect-macos-installation-change-store-prefix"></a><a name="sect-macos-installation-encrypted-volume"></a><a name="sect-macos-installation-symlink"></a><a name="sect-macos-installation-recommended-notes"></a>
+# macOS Installation
+[]{#sect-macos-installation-change-store-prefix}[]{#sect-macos-installation-encrypted-volume}[]{#sect-macos-installation-symlink}[]{#sect-macos-installation-recommended-notes}
<!-- Note: anchors above to catch permalinks to old explanations -->
We believe we have ironed out how to cleanly support the read-only root
diff --git a/doc/manual/src/release-notes/rl-2.9.md b/doc/manual/src/release-notes/rl-2.9.md
new file mode 100644
index 000000000..98cc4235d
--- /dev/null
+++ b/doc/manual/src/release-notes/rl-2.9.md
@@ -0,0 +1,47 @@
+# Release 2.9 (2022-05-30)
+
+* Running Nix with the new `--debugger` flag will cause it to start a
+ repl session if an exception is thrown during evaluation, or if
+ `builtins.break` is called. From there you can inspect the values
+ of variables and evaluate Nix expressions. In debug mode, the
+ following new repl commands are available:
+
+ ```
+ :env Show env stack
+ :bt Show trace stack
+ :st Show current trace
+ :st <idx> Change to another trace in the stack
+ :c Go until end of program, exception, or builtins.break().
+ :s Go one step
+ ```
+
+ Read more about the debugger
+ [here](https://www.zknotes.com/note/5970).
+
+* Nix now provides better integration with zsh's `run-help`
+ feature. It is now included in the Nix installation in the form of
+ an autoloadable shell function, `run-help-nix`. It picks up Nix
+ subcommands from the currently typed in command and directs the user
+ to the associated man pages.
+
+* `nix repl` has a new build-and-link (`:bl`) command that builds a
+ derivation while creating GC root symlinks.
+
+* The path produced by `builtins.toFile` is now allowed to be imported
+ or read even with restricted evaluation. Note that this will not
+ work with a read-only store.
+
+* `nix build` has a new `--print-out-paths` flag to print the
+ resulting output paths. This matches the default behaviour of
+ `nix-build`.
+
+* You can now specify which outputs of a derivation `nix` should
+ operate on using the syntax `installable^outputs`,
+ e.g. `nixpkgs#glibc^dev,static` or `nixpkgs#glibc^*`. By default,
+ `nix` will use the outputs specified by the derivation's
+ `meta.outputsToInstall` attribute if it exists, or all outputs
+ otherwise.
+
+* `builtins.fetchTree` (and flake inputs) can now be used to fetch
+ plain files over the `http(s)` and `file` protocols in addition to
+ directory tarballs.
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index efd893662..52e3b6240 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,26 +1,4 @@
# Release X.Y (202?-??-??)
-* Nix now provides better integration with zsh's run-help feature. It is now
- included in the Nix installation in the form of an autoloadable shell
- function, run-help-nix. It picks up Nix subcommands from the currently typed
- in command and directs the user to the associated man pages.
-
-* `nix repl` has a new build-'n-link (`:bl`) command that builds a derivation
- while creating GC root symlinks.
-
-* The path produced by `builtins.toFile` is now allowed to be imported or read
- even with restricted evaluation. Note that this will not work with a
- read-only store.
-
-* `nix build` has a new `--print-out-paths` flag to print the resulting output paths.
- This matches the default behaviour of `nix-build`.
-
-* You can now specify which outputs of a derivation `nix` should
- operate on using the syntax `installable^outputs`,
- e.g. `nixpkgs#glibc^dev,static` or `nixpkgs#glibc^*`. By default,
- `nix` will use the outputs specified by the derivation's
- `meta.outputsToInstall` attribute if it exists, or all outputs
- otherwise.
-
- Selecting derivation outputs using the attribute selection syntax
- (e.g. `nixpkgs#glibc.dev`) no longer works.
+* Nix can now be built with LTO by passing `--enable-lto` to `configure`.
+ LTO is currently only supported when building with GCC.
diff --git a/flake.lock b/flake.lock
index cd79fa85e..01e4f506a 100644
--- a/flake.lock
+++ b/flake.lock
@@ -18,17 +18,18 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1645296114,
- "narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=",
+ "lastModified": 1653988320,
+ "narHash": "sha256-ZaqFFsSDipZ6KVqriwM34T739+KLYJvNmCWzErjAg7c=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1",
+ "rev": "2fa57ed190fd6c7c746319444f34b5917666e5c1",
"type": "github"
},
"original": {
- "id": "nixpkgs",
- "ref": "nixos-21.05-small",
- "type": "indirect"
+ "owner": "NixOS",
+ "ref": "nixos-22.05-small",
+ "repo": "nixpkgs",
+ "type": "github"
}
},
"nixpkgs-regression": {
@@ -41,9 +42,10 @@
"type": "github"
},
"original": {
- "id": "nixpkgs",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "indirect"
+ "type": "github"
}
},
"root": {
diff --git a/flake.nix b/flake.nix
index dd3a25e9e..22367df71 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,8 +1,8 @@
{
description = "The purely functional package manager";
- inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
- inputs.nixpkgs-regression.url = "nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05-small";
+ inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
@@ -36,7 +36,7 @@
)
);
- forAllStdenvs = stdenvs: f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
+ forAllStdenvs = f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor =
@@ -88,7 +88,6 @@
"LDFLAGS=-fuse-ld=gold"
];
-
nativeBuildDeps =
[
buildPackages.bison
@@ -102,7 +101,7 @@
# Tests
buildPackages.git
buildPackages.mercurial # FIXME: remove? only needed for tests
- buildPackages.jq
+ buildPackages.jq # Also for custom mdBook preprocessor.
]
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
@@ -135,11 +134,6 @@
}))
nlohmann_json
];
-
- perlDeps =
- [ perl
- perlPackages.DBDSQLite
- ];
};
installScriptFor = systems:
@@ -319,6 +313,7 @@
for LIB in $out/lib/*.dylib; do
chmod u+w $LIB
install_name_tool -id $LIB $LIB
+ install_name_tool -delete_rpath ${boost}/lib/ $LIB || true
done
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
''}
@@ -353,7 +348,7 @@
strictDeps = true;
- passthru.perl-bindings = with final; currentStdenv.mkDerivation {
+ passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation {
name = "nix-perl-${version}";
src = self;
@@ -375,16 +370,17 @@
++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
- configureFlags = ''
- --with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
- --with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}
- '';
+ configureFlags = [
+ "--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}"
+ "--with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}"
+ ];
enableParallelBuilding = true;
postUnpack = "sourceRoot=$sourceRoot/perl";
- };
+ });
+ meta.platforms = systems;
};
lowdown-nix = with final; currentStdenv.mkDerivation rec {
@@ -409,7 +405,7 @@
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
- overlay = overlayFor (p: p.stdenv);
+ overlays.default = overlayFor (p: p.stdenv);
hydraJobs = {
@@ -434,7 +430,7 @@
value = let
nixpkgsCross = import nixpkgs {
inherit system crossSystem;
- overlays = [ self.overlay ];
+ overlays = [ self.overlays.default ];
};
in binaryTarball nixpkgsFor.${system} self.packages.${system}."nix-${crossSystem}" nixpkgsCross;
}) crossSystems));
@@ -480,31 +476,31 @@
tests.remoteBuilds = import ./tests/remote-builds.nix {
system = "x86_64-linux";
inherit nixpkgs;
- inherit (self) overlay;
+ overlay = self.overlays.default;
};
tests.nix-copy-closure = import ./tests/nix-copy-closure.nix {
system = "x86_64-linux";
inherit nixpkgs;
- inherit (self) overlay;
+ overlay = self.overlays.default;
};
tests.nssPreload = (import ./tests/nss-preload.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
- inherit (self) overlay;
+ overlay = self.overlays.default;
});
tests.githubFlakes = (import ./tests/github-flakes.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
- inherit (self) overlay;
+ overlay = self.overlays.default;
});
tests.sourcehutFlakes = (import ./tests/sourcehut-flakes.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
- inherit (self) overlay;
+ overlay = self.overlays.default;
});
tests.setuid = nixpkgs.lib.genAttrs
@@ -512,7 +508,7 @@
(system:
import ./tests/setuid.nix rec {
inherit nixpkgs system;
- inherit (self) overlay;
+ overlay = self.overlays.default;
});
# Make sure that nix-env still produces the exact same result
@@ -557,8 +553,9 @@
dockerImage = self.hydraJobs.dockerImage.${system};
});
- packages = forAllSystems (system: {
+ packages = forAllSystems (system: rec {
inherit (nixpkgsFor.${system}) nix;
+ default = nix;
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
nix-static = let
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
@@ -574,14 +571,24 @@
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps;
- configureFlags = [ "--sysconfdir=/etc" ];
+ # Work around pkgsStatic disabling all tests.
+ # Remove in NixOS 22.11, see https://github.com/NixOS/nixpkgs/pull/140271.
+ preHook =
+ ''
+ doCheck=1
+ doInstallCheck=1
+ '';
+
+ configureFlags =
+ configureFlags ++
+ [ "--sysconfdir=/etc"
+ "--enable-embedded-sandbox-shell"
+ ];
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d";
- doCheck = true;
-
installFlags = "sysconfdir=$(out)/etc";
postInstall = ''
@@ -591,7 +598,6 @@
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
'';
- doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
stripAllList = ["bin"];
@@ -600,6 +606,7 @@
hardeningDisable = [ "pie" ];
};
+
dockerImage =
let
pkgs = nixpkgsFor.${system};
@@ -614,12 +621,14 @@
ln -s ${image} $image
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
'';
- } // builtins.listToAttrs (map (crossSystem: {
+ }
+
+ // builtins.listToAttrs (map (crossSystem: {
name = "nix-${crossSystem}";
value = let
nixpkgsCross = import nixpkgs {
inherit system crossSystem;
- overlays = [ self.overlay ];
+ overlays = [ self.overlays.default ];
};
in with commonDeps nixpkgsCross; nixpkgsCross.stdenv.mkDerivation {
name = "nix-${version}";
@@ -653,44 +662,45 @@
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
};
- }) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
+ }) (if system == "x86_64-linux" then crossSystems else [])))
+
+ // (builtins.listToAttrs (map (stdenvName:
nixpkgsFor.${system}.lib.nameValuePair
"nix-${stdenvName}"
nixpkgsFor.${system}."${stdenvName}Packages".nix
) stdenvs)));
- defaultPackage = forAllSystems (system: self.packages.${system}.nix);
-
- devShell = forAllSystems (system: self.devShells.${system}.stdenvPackages);
-
- devShells = forAllSystemsAndStdenvs (system: stdenv:
- with nixpkgsFor.${system};
- with commonDeps pkgs;
-
- nixpkgsFor.${system}.${stdenv}.mkDerivation {
- name = "nix";
+ devShells = forAllSystems (system:
+ forAllStdenvs (stdenv:
+ with nixpkgsFor.${system};
+ with commonDeps pkgs;
+ nixpkgsFor.${system}.${stdenv}.mkDerivation {
+ name = "nix";
- outputs = [ "out" "dev" "doc" ];
+ outputs = [ "out" "dev" "doc" ];
- nativeBuildInputs = nativeBuildDeps;
- buildInputs = buildDeps ++ propagatedDeps ++ awsDeps ++ perlDeps;
+ nativeBuildInputs = nativeBuildDeps;
+ buildInputs = buildDeps ++ propagatedDeps ++ awsDeps;
- inherit configureFlags;
+ inherit configureFlags;
- enableParallelBuilding = true;
+ enableParallelBuilding = true;
- installFlags = "sysconfdir=$(out)/etc";
+ installFlags = "sysconfdir=$(out)/etc";
- shellHook =
- ''
- PATH=$prefix/bin:$PATH
- unset PYTHONPATH
- export MANPATH=$out/share/man:$MANPATH
+ shellHook =
+ ''
+ PATH=$prefix/bin:$PATH
+ unset PYTHONPATH
+ export MANPATH=$out/share/man:$MANPATH
- # Make bash completion work.
- XDG_DATA_DIRS+=:$out/share
- '';
- });
+ # Make bash completion work.
+ XDG_DATA_DIRS+=:$out/share
+ '';
+ }
+ )
+ // { default = self.devShells.${system}.stdenv; }
+ );
};
}
diff --git a/misc/systemd/nix-daemon.service.in b/misc/systemd/nix-daemon.service.in
index 24d894898..e3ac42beb 100644
--- a/misc/systemd/nix-daemon.service.in
+++ b/misc/systemd/nix-daemon.service.in
@@ -9,6 +9,7 @@ ConditionPathIsReadWrite=@localstatedir@/nix/daemon-socket
[Service]
ExecStart=@@bindir@/nix-daemon nix-daemon --daemon
KillMode=process
+LimitNOFILE=4096
[Install]
WantedBy=multi-user.target
diff --git a/mk/libraries.mk b/mk/libraries.mk
index ffd7b5610..6541775f3 100644
--- a/mk/libraries.mk
+++ b/mk/libraries.mk
@@ -91,7 +91,7 @@ define build-library
$(1)_PATH := $$(_d)/$$($(1)_NAME).$(SO_EXT)
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
- $$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED)
+ +$$(trace-ld) $(CXX) -o $$(abspath $$@) -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE)) $$($(1)_LDFLAGS_UNINSTALLED)
ifndef HOST_DARWIN
$(1)_LDFLAGS_USE += -Wl,-rpath,$$(abspath $$(_d))
@@ -105,7 +105,7 @@ define build-library
$$(eval $$(call create-dir, $$($(1)_INSTALL_DIR)))
$$($(1)_INSTALL_PATH): $$($(1)_OBJS) $$(_libs_final) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
- $$(trace-ld) $(CXX) -o $$@ -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
+ +$$(trace-ld) $(CXX) -o $$@ -shared $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$($(1)_LDFLAGS_PROPAGATED) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
$(1)_LDFLAGS_USE_INSTALLED += -L$$(DESTDIR)$$($(1)_INSTALL_DIR) -l$$(patsubst lib%,%,$$(strip $$($(1)_NAME)))
ifndef HOST_DARWIN
@@ -125,7 +125,7 @@ define build-library
$(1)_PATH := $$(_d)/$$($(1)_NAME).a
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
- $$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$?
+ +$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$^
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
diff --git a/mk/programs.mk b/mk/programs.mk
index d0cf5baf0..0fc1990f7 100644
--- a/mk/programs.mk
+++ b/mk/programs.mk
@@ -32,7 +32,7 @@ define build-program
$$(eval $$(call create-dir, $$(_d)))
$$($(1)_PATH): $$($(1)_OBJS) $$(_libs) | $$(_d)/
- $$(trace-ld) $(CXX) -o $$@ $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE))
+ +$$(trace-ld) $(CXX) -o $$@ $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE))
$(1)_INSTALL_DIR ?= $$(bindir)
@@ -49,7 +49,7 @@ define build-program
_libs_final := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_INSTALL_PATH))
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_OBJS) $$(_libs_final) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
- $$(trace-ld) $(CXX) -o $$@ $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
+ +$$(trace-ld) $(CXX) -o $$@ $$(LDFLAGS) $$(GLOBAL_LDFLAGS) $$($(1)_OBJS) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE_INSTALLED))
else
diff --git a/scripts/create-darwin-volume.sh b/scripts/create-darwin-volume.sh
index 4bac4b7ba..103e1e391 100755
--- a/scripts/create-darwin-volume.sh
+++ b/scripts/create-darwin-volume.sh
@@ -442,9 +442,14 @@ add_nix_vol_fstab_line() {
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
shift
- # wrap `ex` to work around a problem with vim plugins breaking exit codes;
- # (see https://github.com/NixOS/nix/issues/5468)
- # we'd prefer EDITOR="/usr/bin/ex --noplugin" but vifs doesn't word-split
+ # wrap `ex` to work around problems w/ vim features breaking exit codes
+ # - plugins (see github.com/NixOS/nix/issues/5468): -u NONE
+ # - swap file: -n
+ #
+ # the first draft used `--noplugin`, but github.com/NixOS/nix/issues/6462
+ # suggests we need the less-semantic `-u NONE`
+ #
+ # we'd prefer EDITOR="/usr/bin/ex -u NONE" but vifs doesn't word-split
# the EDITOR env.
#
# TODO: at some point we should switch to `--clean`, but it wasn't added
@@ -452,7 +457,7 @@ add_nix_vol_fstab_line() {
# minver 10.12.6 seems to have released with vim 7.4
cat > "$SCRATCH/ex_cleanroom_wrapper" <<EOF
#!/bin/sh
-/usr/bin/ex --noplugin "\$@"
+/usr/bin/ex -u NONE -n "\$@"
EOF
chmod 755 "$SCRATCH/ex_cleanroom_wrapper"
@@ -646,8 +651,9 @@ EOF
task "Configuring /etc/synthetic.conf to make a mount-point at $NIX_ROOT" >&2
# technically /etc/synthetic.d/nix is supported in Big Sur+
# but handling both takes even more code...
+ # See earlier note; `-u NONE` disables vim plugins/rc, `-n` skips swapfile
_sudo "to add Nix to /etc/synthetic.conf" \
- /usr/bin/ex --noplugin /etc/synthetic.conf <<EOF
+ /usr/bin/ex -u NONE -n /etc/synthetic.conf <<EOF
:a
${NIX_ROOT:1}
.
@@ -815,7 +821,8 @@ setup_volume_daemon() {
local volume_uuid="$2"
if ! test_voldaemon; then
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
- _sudo "to install the Nix volume mounter" /usr/bin/ex --noplugin "$NIX_VOLUME_MOUNTD_DEST" <<EOF
+ # See earlier note; `-u NONE` disables vim plugins/rc, `-n` skips swapfile
+ _sudo "to install the Nix volume mounter" /usr/bin/ex -u NONE -n "$NIX_VOLUME_MOUNTD_DEST" <<EOF
:a
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
.
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index b79a9c23a..9a18280ef 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -638,6 +638,17 @@ place_channel_configuration() {
fi
}
+check_selinux() {
+ if command -v getenforce > /dev/null 2>&1; then
+ if ! [ "$(getenforce)" = "Disabled" ]; then
+ failure <<EOF
+Nix does not work with selinux enabled yet!
+see https://github.com/NixOS/nix/issues/2374
+EOF
+ fi
+ fi
+}
+
welcome_to_nix() {
ok "Welcome to the Multi-User Nix Installation"
@@ -866,6 +877,8 @@ when I need to.
EOF
fi
+ check_selinux
+
if [ "$(uname -s)" = "Darwin" ]; then
# shellcheck source=./install-darwin-multi-user.sh
. "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index f28cfe5de..7f8072d75 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -86,6 +86,11 @@ ref<Store> CopyCommand::getDstStore()
EvalCommand::EvalCommand()
{
+ addFlag({
+ .longName = "debugger",
+ .description = "start an interactive environment if evaluation fails",
+ .handler = {&startReplOnEvalErrors, true},
+ });
}
EvalCommand::~EvalCommand()
@@ -103,7 +108,7 @@ ref<Store> EvalCommand::getEvalStore()
ref<EvalState> EvalCommand::getEvalState()
{
- if (!evalState)
+ if (!evalState) {
evalState =
#if HAVE_BOEHMGC
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
@@ -113,6 +118,11 @@ ref<EvalState> EvalCommand::getEvalState()
searchPath, getEvalStore(), getStore())
#endif
;
+
+ if (startReplOnEvalErrors) {
+ evalState->debugRepl = &runRepl;
+ };
+ }
return ref<EvalState>(evalState);
}
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 078e2a2ce..8982f21d0 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -57,6 +57,8 @@ struct CopyCommand : virtual StoreCommand
struct EvalCommand : virtual StoreCommand, MixEvalArgs
{
+ bool startReplOnEvalErrors = false;
+
EvalCommand();
~EvalCommand();
@@ -270,4 +272,8 @@ void printClosureDiff(
const StorePath & afterPath,
std::string_view indent);
+
+void runRepl(
+ ref<EvalState> evalState,
+ const ValMap & extraEnv);
}
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 635ce19b6..ffc25135e 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -146,7 +146,8 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
.shortName = 'f',
.description =
"Interpret installables as attribute paths relative to the Nix expression stored in *file*. "
- "If *file* is the character -, then a Nix expression will be read from standard input.",
+ "If *file* is the character -, then a Nix expression will be read from standard input. "
+ "Implies `--impure`.",
.category = installablesCategory,
.labels = {"file"},
.handler = {&file},
@@ -623,7 +624,14 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
std::set<std::string> outputsToInstall;
std::optional<NixInt> priority;
- if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
+ if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
+ if (aOutputSpecified->getBool()) {
+ if (auto aOutputName = attr->maybeGetAttr("outputName"))
+ outputsToInstall = { aOutputName->getString() };
+ }
+ }
+
+ else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
@@ -912,6 +920,9 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
break;
case Realise::Outputs: {
+ if (settings.printMissing)
+ printMissing(store, pathsToBuild, lvlInfo);
+
for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) {
if (!buildResult.success())
buildResult.rethrow();
diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk
index 7a2f83cc7..3a4de6bcb 100644
--- a/src/libcmd/local.mk
+++ b/src/libcmd/local.mk
@@ -6,9 +6,9 @@ libcmd_DIR := $(d)
libcmd_SOURCES := $(wildcard $(d)/*.cc)
-libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
+libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers -I src/nix
-libcmd_LDFLAGS += $(LOWDOWN_LIBS) -pthread
+libcmd_LDFLAGS = $(EDITLINE_LIBS) -llowdown -pthread
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
diff --git a/src/nix/repl.cc b/src/libcmd/repl.cc
index 2967632ed..3c89a8ea3 100644
--- a/src/nix/repl.cc
+++ b/src/libcmd/repl.cc
@@ -48,38 +48,42 @@ struct NixRepl
#endif
{
std::string curDir;
- std::unique_ptr<EvalState> state;
+ ref<EvalState> state;
Bindings * autoArgs;
+ size_t debugTraceIndex;
+
Strings loadedFiles;
const static int envSize = 32768;
- StaticEnv staticEnv;
+ std::shared_ptr<StaticEnv> staticEnv;
Env * env;
int displ;
StringSet varNames;
const Path historyFile;
- NixRepl(const Strings & searchPath, nix::ref<Store> store);
+ NixRepl(ref<EvalState> state);
~NixRepl();
void mainLoop(const std::vector<std::string> & files);
StringSet completePrefix(const std::string & prefix);
- bool getLine(std::string & input, const std::string &prompt);
+ bool getLine(std::string & input, const std::string & prompt);
StorePath getDerivationPath(Value & v);
bool processLine(std::string line);
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
void initEnv();
+ void loadFiles();
void reloadFiles();
void addAttrsToScope(Value & attrs);
void addVarToScope(const Symbol name, Value & v);
Expr * parseString(std::string s);
void evalString(std::string s, Value & v);
+ void loadDebugTraceEnv(DebugTrace & dt);
typedef std::set<Value *> ValuesSeen;
- std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
- std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
+ std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
+ std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
};
@@ -92,9 +96,10 @@ std::string removeWhitespace(std::string s)
}
-NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store)
- : state(std::make_unique<EvalState>(searchPath, store))
- , staticEnv(false, &state->staticBaseEnv)
+NixRepl::NixRepl(ref<EvalState> state)
+ : state(state)
+ , debugTraceIndex(0)
+ , staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
, historyFile(getDataDir() + "/nix/repl-history")
{
curDir = absPath(".");
@@ -198,15 +203,42 @@ namespace {
}
}
+static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt)
+{
+ if (dt.isError)
+ out << ANSI_RED "error: " << ANSI_NORMAL;
+ out << dt.hint.str() << "\n";
+
+ // prefer direct pos, but if noPos then try the expr.
+ auto pos = *dt.pos
+ ? *dt.pos
+ : positions[dt.expr.getPos() ? dt.expr.getPos() : noPos];
+
+ if (pos) {
+ printAtPos(pos, out);
+
+ auto loc = getCodeLines(pos);
+ if (loc.has_value()) {
+ out << "\n";
+ printCodeLines(out, "", pos, *loc);
+ out << "\n";
+ }
+ }
+
+ return out;
+}
+
void NixRepl::mainLoop(const std::vector<std::string> & files)
{
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
notice("Welcome to Nix " + nixVersion + ". Type :? for help.\n");
- for (auto & i : files)
- loadedFiles.push_back(i);
+ if (!files.empty()) {
+ for (auto & i : files)
+ loadedFiles.push_back(i);
+ }
- reloadFiles();
+ loadFiles();
if (!loadedFiles.empty()) notice("");
// Allow nix-repl specific settings in .inputrc
@@ -227,9 +259,12 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
while (true) {
// When continuing input from previous lines, don't print a prompt, just align to the same
// number of chars as the prompt.
- if (!getLine(input, input.empty() ? "nix-repl> " : " "))
+ if (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
+ // ctrl-D should exit the debugger.
+ state->debugStop = false;
+ state->debugQuit = true;
break;
-
+ }
try {
if (!removeWhitespace(input).empty() && !processLine(input)) return;
} catch (ParseError & e) {
@@ -240,6 +275,14 @@ void NixRepl::mainLoop(const std::vector<std::string> & files)
} else {
printMsg(lvlError, e.msg());
}
+ } catch (EvalError & e) {
+ // in debugger mode, an EvalError should trigger another repl session.
+ // when that session returns the exception will land here. No need to show it again;
+ // show the error for this repl session instead.
+ if (state->debugRepl && !state->debugTraces.empty())
+ showDebugTrace(std::cout, state->positions, state->debugTraces.front());
+ else
+ printMsg(lvlError, e.msg());
} catch (Error & e) {
printMsg(lvlError, e.msg());
} catch (Interrupted & e) {
@@ -394,6 +437,19 @@ StorePath NixRepl::getDerivationPath(Value & v) {
return *drvPath;
}
+void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
+{
+ initEnv();
+
+ auto se = state->getStaticEnv(dt.expr);
+ if (se) {
+ auto vm = mapStaticEnvBindings(state->symbols, *se.get(), dt.env);
+
+ // add staticenv vars.
+ for (auto & [name, value] : *(vm.get()))
+ addVarToScope(state->symbols.create(name), *value);
+ }
+}
bool NixRepl::processLine(std::string line)
{
@@ -429,12 +485,72 @@ bool NixRepl::processLine(std::string line)
<< " :p <expr> Evaluate and print expression recursively\n"
<< " :q Exit nix-repl\n"
<< " :r Reload all files\n"
- << " :s <expr> Build dependencies of derivation, then start nix-shell\n"
+ << " :sh <expr> Build dependencies of derivation, then start nix-shell\n"
<< " :t <expr> Describe result of evaluation\n"
<< " :u <expr> Build derivation, then start nix-shell\n"
<< " :doc <expr> Show documentation of a builtin function\n"
<< " :log <expr> Show logs for a derivation\n"
- << " :st [bool] Enable, disable or toggle showing traces for errors\n";
+ << " :te [bool] Enable, disable or toggle showing traces for errors\n"
+ ;
+ if (state->debugRepl) {
+ std::cout
+ << "\n"
+ << " Debug mode commands\n"
+ << " :env Show env stack\n"
+ << " :bt Show trace stack\n"
+ << " :st Show current trace\n"
+ << " :st <idx> Change to another trace in the stack\n"
+ << " :c Go until end of program, exception, or builtins.break\n"
+ << " :s Go one step\n"
+ ;
+ }
+
+ }
+
+ else if (state->debugRepl && (command == ":bt" || command == ":backtrace")) {
+ for (const auto & [idx, i] : enumerate(state->debugTraces)) {
+ std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
+ showDebugTrace(std::cout, state->positions, i);
+ }
+ }
+
+ else if (state->debugRepl && (command == ":env")) {
+ for (const auto & [idx, i] : enumerate(state->debugTraces)) {
+ if (idx == debugTraceIndex) {
+ printEnvBindings(*state, i.expr, i.env);
+ break;
+ }
+ }
+ }
+
+ else if (state->debugRepl && (command == ":st")) {
+ try {
+ // change the DebugTrace index.
+ debugTraceIndex = stoi(arg);
+ } catch (...) { }
+
+ for (const auto & [idx, i] : enumerate(state->debugTraces)) {
+ if (idx == debugTraceIndex) {
+ std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
+ showDebugTrace(std::cout, state->positions, i);
+ std::cout << std::endl;
+ printEnvBindings(*state, i.expr, i.env);
+ loadDebugTraceEnv(i);
+ break;
+ }
+ }
+ }
+
+ else if (state->debugRepl && (command == ":s" || command == ":step")) {
+ // set flag to stop at next DebugTrace; exit repl.
+ state->debugStop = true;
+ return false;
+ }
+
+ else if (state->debugRepl && (command == ":c" || command == ":continue")) {
+ // set flag to run to next breakpoint or end of program; exit repl.
+ state->debugStop = false;
+ return false;
}
else if (command == ":a" || command == ":add") {
@@ -506,7 +622,7 @@ bool NixRepl::processLine(std::string line)
runNix("nix-shell", {state->store->printStorePath(drvPath)});
}
- else if (command == ":b" || command == ":bl" || command == ":i" || command == ":s" || command == ":log") {
+ else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") {
Value v;
evalString(arg, v);
StorePath drvPath = getDerivationPath(v);
@@ -567,8 +683,11 @@ bool NixRepl::processLine(std::string line)
printValue(std::cout, v, 1000000000) << std::endl;
}
- else if (command == ":q" || command == ":quit")
+ else if (command == ":q" || command == ":quit") {
+ state->debugStop = false;
+ state->debugQuit = true;
return false;
+ }
else if (command == ":doc") {
Value v;
@@ -593,7 +712,7 @@ bool NixRepl::processLine(std::string line)
throw Error("value does not have documentation");
}
- else if (command == ":st" || command == ":show-trace") {
+ else if (command == ":te" || command == ":trace-enable") {
if (arg == "false" || (arg == "" && loggerSettings.showTrace)) {
std::cout << "not showing error traces\n";
loggerSettings.showTrace = false;
@@ -669,10 +788,10 @@ void NixRepl::initEnv()
env = &state->allocEnv(envSize);
env->up = &state->baseEnv;
displ = 0;
- staticEnv.vars.clear();
+ staticEnv->vars.clear();
varNames.clear();
- for (auto & i : state->staticBaseEnv.vars)
+ for (auto & i : state->staticBaseEnv->vars)
varNames.emplace(state->symbols[i.first]);
}
@@ -681,6 +800,12 @@ void NixRepl::reloadFiles()
{
initEnv();
+ loadFiles();
+}
+
+
+void NixRepl::loadFiles()
+{
Strings old = loadedFiles;
loadedFiles.clear();
@@ -701,12 +826,12 @@ void NixRepl::addAttrsToScope(Value & attrs)
throw Error("environment full; cannot add more variables");
for (auto & i : *attrs.attrs) {
- staticEnv.vars.emplace_back(i.name, displ);
+ staticEnv->vars.emplace_back(i.name, displ);
env->values[displ++] = i.value;
varNames.emplace(state->symbols[i.name]);
}
- staticEnv.sort();
- staticEnv.deduplicate();
+ staticEnv->sort();
+ staticEnv->deduplicate();
notice("Added %1% variables.", attrs.attrs->size());
}
@@ -715,10 +840,10 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
{
if (displ >= envSize)
throw Error("environment full; cannot add more variables");
- if (auto oldVar = staticEnv.find(name); oldVar != staticEnv.vars.end())
- staticEnv.vars.erase(oldVar);
- staticEnv.vars.emplace_back(name, displ);
- staticEnv.sort();
+ if (auto oldVar = staticEnv->find(name); oldVar != staticEnv->vars.end())
+ staticEnv->vars.erase(oldVar);
+ staticEnv->vars.emplace_back(name, displ);
+ staticEnv->sort();
env->values[displ++] = &v;
varNames.emplace(state->symbols[name]);
}
@@ -886,6 +1011,21 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
return str;
}
+void runRepl(
+ ref<EvalState>evalState,
+ const ValMap & extraEnv)
+{
+ auto repl = std::make_unique<NixRepl>(evalState);
+
+ repl->initEnv();
+
+ // add 'extra' vars.
+ for (auto & [name, value] : extraEnv)
+ repl->addVarToScope(repl->state->symbols.create(name), *value);
+
+ repl->mainLoop({});
+}
+
struct CmdRepl : StoreCommand, MixEvalArgs
{
std::vector<std::string> files;
@@ -899,6 +1039,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs
});
}
+ bool forceImpureByDefault() override
+ {
+ return true;
+ }
+
std::string description() override
{
return "start an interactive environment for evaluating Nix expressions";
@@ -913,9 +1058,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs
void run(ref<Store> store) override
{
- evalSettings.pureEval = false;
- auto repl = std::make_unique<NixRepl>(searchPath, openStore());
+ auto evalState = make_ref<EvalState>(searchPath, store);
+
+ auto repl = std::make_unique<NixRepl>(evalState);
repl->autoArgs = getAutoArgs(*repl->state);
+ repl->initEnv();
repl->mainLoop(files);
}
};
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 6a2e775d0..dbfd8e70b 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -282,7 +282,7 @@ struct AttrDb
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
if (!queryAttribute.next()) return {};
- auto rowId = (AttrType) queryAttribute.getInt(0);
+ auto rowId = (AttrId) queryAttribute.getInt(0);
auto type = (AttrType) queryAttribute.getInt(1);
switch (type) {
@@ -576,14 +576,14 @@ std::string AttrCursor::getString()
debug("using cached string attribute '%s'", getAttrPathStr());
return s->first;
} else
- throw TypeError("'%s' is not a string", getAttrPathStr());
+ root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr()));
}
}
auto & v = forceValue();
if (v.type() != nString && v.type() != nPath)
- throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()));
+ root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type())));
return v.type() == nString ? v.string.s : v.path;
}
@@ -607,7 +607,7 @@ string_t AttrCursor::getStringWithContext()
return *s;
}
} else
- throw TypeError("'%s' is not a string", getAttrPathStr());
+ root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr()));
}
}
@@ -618,7 +618,7 @@ string_t AttrCursor::getStringWithContext()
else if (v.type() == nPath)
return {v.path, {}};
else
- throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()));
+ root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type())));
}
bool AttrCursor::getBool()
@@ -631,14 +631,14 @@ bool AttrCursor::getBool()
debug("using cached Boolean attribute '%s'", getAttrPathStr());
return *b;
} else
- throw TypeError("'%s' is not a Boolean", getAttrPathStr());
+ root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr()));
}
}
auto & v = forceValue();
if (v.type() != nBool)
- throw TypeError("'%s' is not a Boolean", getAttrPathStr());
+ root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr()));
return v.boolean;
}
@@ -708,14 +708,14 @@ std::vector<Symbol> AttrCursor::getAttrs()
debug("using cached attrset attribute '%s'", getAttrPathStr());
return *attrs;
} else
- throw TypeError("'%s' is not an attribute set", getAttrPathStr());
+ root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr()));
}
}
auto & v = forceValue();
if (v.type() != nAttrs)
- throw TypeError("'%s' is not an attribute set", getAttrPathStr());
+ root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr()));
std::vector<Symbol> attrs;
for (auto & attr : *getValue().attrs)
diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh
index 7f01d08e3..f2f4ba725 100644
--- a/src/libexpr/eval-inline.hh
+++ b/src/libexpr/eval-inline.hh
@@ -4,7 +4,6 @@
namespace nix {
-
/* Note: Various places expect the allocated memory to be zeroed. */
[[gnu::always_inline]]
inline void * allocBytes(size_t n)
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 8d67691f0..40462afdf 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -18,6 +18,7 @@
#include <sys/resource.h>
#include <iostream>
#include <fstream>
+#include <functional>
#include <sys/resource.h>
@@ -36,7 +37,6 @@
namespace nix {
-
static char * allocString(size_t size)
{
char * t;
@@ -459,10 +459,14 @@ EvalState::EvalState(
, sKey(symbols.create("key"))
, sPath(symbols.create("path"))
, sPrefix(symbols.create("prefix"))
+ , sOutputSpecified(symbols.create("outputSpecified"))
, repair(NoRepair)
, emptyBindings(0)
, store(store)
, buildStore(buildStore ? buildStore : store)
+ , debugRepl(0)
+ , debugStop(false)
+ , debugQuit(false)
, regexCache(makeRegexCache())
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
@@ -472,7 +476,7 @@ EvalState::EvalState(
, env1AllocCache(std::make_shared<void *>(nullptr))
#endif
, baseEnv(allocEnv(128))
- , staticBaseEnv(false, 0)
+ , staticBaseEnv{std::make_shared<StaticEnv>(false, nullptr)}
{
countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0";
@@ -530,7 +534,7 @@ void EvalState::allowPath(const StorePath & storePath)
allowedPaths->insert(store->toRealPath(storePath));
}
-void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v)
+void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
{
allowPath(storePath);
@@ -638,7 +642,7 @@ Value * EvalState::addConstant(const std::string & name, Value & v)
void EvalState::addConstant(const std::string & name, Value * v)
{
- staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
+ staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
@@ -663,7 +667,7 @@ Value * EvalState::addPrimOp(const std::string & name,
Value * v = allocValue();
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
- staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl);
+ staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
return v;
@@ -689,7 +693,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
Value * v = allocValue();
v->mkPrimOp(new PrimOp(primOp));
- staticBaseEnv.vars.emplace_back(envName, baseEnvDispl);
+ staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
return v;
@@ -719,128 +723,284 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
}
+// just for the current level of StaticEnv, not the whole chain.
+void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se)
+{
+ std::cout << ANSI_MAGENTA;
+ for (auto & i : se.vars)
+ std::cout << st[i.first] << " ";
+ std::cout << ANSI_NORMAL;
+ std::cout << std::endl;
+}
+
+// just for the current level of Env, not the whole chain.
+void printWithBindings(const SymbolTable & st, const Env & env)
+{
+ if (env.type == Env::HasWithAttrs) {
+ std::cout << "with: ";
+ std::cout << ANSI_MAGENTA;
+ Bindings::iterator j = env.values[0]->attrs->begin();
+ while (j != env.values[0]->attrs->end()) {
+ std::cout << st[j->name] << " ";
+ ++j;
+ }
+ std::cout << ANSI_NORMAL;
+ std::cout << std::endl;
+ }
+}
+
+void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl)
+{
+ std::cout << "Env level " << lvl << std::endl;
+
+ if (se.up && env.up) {
+ std::cout << "static: ";
+ printStaticEnvBindings(st, se);
+ printWithBindings(st, env);
+ std::cout << std::endl;
+ printEnvBindings(st, *se.up, *env.up, ++lvl);
+ } else {
+ std::cout << ANSI_MAGENTA;
+ // for the top level, don't print the double underscore ones;
+ // they are in builtins.
+ for (auto & i : se.vars)
+ if (!hasPrefix(st[i.first], "__"))
+ std::cout << st[i.first] << " ";
+ std::cout << ANSI_NORMAL;
+ std::cout << std::endl;
+ printWithBindings(st, env); // probably nothing there for the top level.
+ std::cout << std::endl;
+
+ }
+}
+
+void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env)
+{
+ // just print the names for now
+ auto se = es.getStaticEnv(expr);
+ if (se)
+ printEnvBindings(es.symbols, *se, env, 0);
+}
+
+void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, ValMap & vm)
+{
+ // add bindings for the next level up first, so that the bindings for this level
+ // override the higher levels.
+ // The top level bindings (builtins) are skipped since they are added for us by initEnv()
+ if (env.up && se.up) {
+ mapStaticEnvBindings(st, *se.up, *env.up, vm);
+
+ if (env.type == Env::HasWithAttrs) {
+ // add 'with' bindings.
+ Bindings::iterator j = env.values[0]->attrs->begin();
+ while (j != env.values[0]->attrs->end()) {
+ vm[st[j->name]] = j->value;
+ ++j;
+ }
+ } else {
+ // iterate through staticenv bindings and add them.
+ for (auto & i : se.vars)
+ vm[st[i.first]] = env.values[i.second];
+ }
+ }
+}
+
+std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env)
+{
+ auto vm = std::make_unique<ValMap>();
+ mapStaticEnvBindings(st, se, env, *vm);
+ return vm;
+}
+
+void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr)
+{
+ // double check we've got the debugRepl function pointer.
+ if (!debugRepl)
+ return;
+
+ auto dts =
+ error && expr.getPos()
+ ? std::make_unique<DebugTraceStacker>(
+ *this,
+ DebugTrace {
+ .pos = error->info().errPos ? *error->info().errPos : positions[expr.getPos()],
+ .expr = expr,
+ .env = env,
+ .hint = error->info().msg,
+ .isError = true
+ })
+ : nullptr;
+
+ if (error)
+ printError("%s\n\n" ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL, error->what());
+
+ auto se = getStaticEnv(expr);
+ if (se) {
+ auto vm = mapStaticEnvBindings(symbols, *se.get(), env);
+ (debugRepl)(ref<EvalState>(shared_from_this()), *vm);
+ }
+}
+
/* Every "format" object (even temporary) takes up a few hundred bytes
of stack space, which is a real killer in the recursive
evaluator. So here are some helper functions for throwing
exceptions. */
-
-void EvalState::throwEvalError(const PosIdx pos, const char * s) const
+void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr)
{
- throw EvalError({
+ debugThrow(EvalError({
.msg = hintfmt(s),
.errPos = positions[pos]
- });
+ }), env, expr);
}
-void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
+void EvalState::throwEvalError(const PosIdx pos, const char * s)
{
- throw TypeError({
- .msg = hintfmt(s, showType(v)),
+ debugThrowLastTrace(EvalError({
+ .msg = hintfmt(s),
.errPos = positions[pos]
- });
+ }));
}
-void EvalState::throwEvalError(const char * s, const std::string & s2) const
+void EvalState::throwEvalError(const char * s, const std::string & s2)
{
- throw EvalError(s, s2);
+ debugThrowLastTrace(EvalError(s, s2));
}
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
- const std::string & s2) const
+ const std::string & s2, Env & env, Expr & expr)
{
- throw EvalError(ErrorInfo {
+ debugThrow(EvalError(ErrorInfo{
.msg = hintfmt(s, s2),
.errPos = positions[pos],
.suggestions = suggestions,
- });
+ }), env, expr);
}
-void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2)
{
- throw EvalError(ErrorInfo {
+ debugThrowLastTrace(EvalError({
.msg = hintfmt(s, s2),
.errPos = positions[pos]
- });
+ }));
}
-void EvalState::throwEvalError(const char * s, const std::string & s2, const std::string & s3) const
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, Env & env, Expr & expr)
{
- throw EvalError(s, s2, s3);
+ debugThrow(EvalError({
+ .msg = hintfmt(s, s2),
+ .errPos = positions[pos]
+ }), env, expr);
+}
+
+void EvalState::throwEvalError(const char * s, const std::string & s2,
+ const std::string & s3)
+{
+ debugThrowLastTrace(EvalError({
+ .msg = hintfmt(s, s2),
+ .errPos = positions[noPos]
+ }));
}
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
- const std::string & s3) const
+ const std::string & s3)
{
- throw EvalError({
- .msg = hintfmt(s, s2, s3),
+ debugThrowLastTrace(EvalError({
+ .msg = hintfmt(s, s2),
.errPos = positions[pos]
- });
+ }));
}
-void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const
+void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
+ const std::string & s3, Env & env, Expr & expr)
+{
+ debugThrow(EvalError({
+ .msg = hintfmt(s, s2),
+ .errPos = positions[pos]
+ }), env, expr);
+}
+
+void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr & expr)
{
// p1 is where the error occurred; p2 is a position mentioned in the message.
- throw EvalError({
+ debugThrow(EvalError({
.msg = hintfmt(s, symbols[sym], positions[p2]),
.errPos = positions[p1]
- });
+ }), env, expr);
}
-void EvalState::throwTypeError(const PosIdx pos, const char * s) const
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v)
{
- throw TypeError({
+ debugThrowLastTrace(TypeError({
+ .msg = hintfmt(s, showType(v)),
+ .errPos = positions[pos]
+ }));
+}
+
+void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, Expr & expr)
+{
+ debugThrow(TypeError({
+ .msg = hintfmt(s, showType(v)),
+ .errPos = positions[pos]
+ }), env, expr);
+}
+
+void EvalState::throwTypeError(const PosIdx pos, const char * s)
+{
+ debugThrowLastTrace(TypeError({
.msg = hintfmt(s),
.errPos = positions[pos]
- });
+ }));
}
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
- const Symbol s2) const
+ const Symbol s2, Env & env, Expr &expr)
{
- throw TypeError({
+ debugThrow(TypeError({
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
.errPos = positions[pos]
- });
+ }), env, expr);
}
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
- const ExprLambda & fun, const Symbol s2) const
+ const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr)
{
- throw TypeError(ErrorInfo {
+ debugThrow(TypeError(ErrorInfo {
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
.errPos = positions[pos],
.suggestions = suggestions,
- });
+ }), env, expr);
}
-
-void EvalState::throwTypeError(const char * s, const Value & v) const
+void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr)
{
- throw TypeError(s, showType(v));
+ debugThrow(TypeError({
+ .msg = hintfmt(s, showType(v)),
+ .errPos = positions[expr.getPos()],
+ }), env, expr);
}
-void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const
+void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
{
- throw AssertionError({
+ debugThrow(AssertionError({
.msg = hintfmt(s, s1),
.errPos = positions[pos]
- });
+ }), env, expr);
}
-void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const
+void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
{
- throw UndefinedVarError({
+ debugThrow(UndefinedVarError({
.msg = hintfmt(s, s1),
.errPos = positions[pos]
- });
+ }), env, expr);
}
-void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const
+void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
{
- throw MissingArgumentError({
+ debugThrow(MissingArgumentError({
.msg = hintfmt(s, s1),
.errPos = positions[pos]
- });
+ }), env, expr);
}
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
@@ -853,6 +1013,32 @@ void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const
e.addTrace(positions[pos], s, s2);
}
+static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
+ EvalState & state,
+ Expr & expr,
+ Env & env,
+ std::optional<ErrPos> pos,
+ const char * s,
+ const std::string & s2)
+{
+ return std::make_unique<DebugTraceStacker>(state,
+ DebugTrace {
+ .pos = pos,
+ .expr = expr,
+ .env = env,
+ .hint = hintfmt(s, s2),
+ .isError = false
+ });
+}
+
+DebugTraceStacker::DebugTraceStacker(EvalState & evalState, DebugTrace t)
+ : evalState(evalState)
+ , trace(std::move(t))
+{
+ evalState.debugTraces.push_front(trace);
+ if (evalState.debugStop && evalState.debugRepl)
+ evalState.runDebugRepl(nullptr, trace.env, trace.expr);
+}
void Value::mkString(std::string_view s)
{
@@ -911,12 +1097,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
return j->value;
}
if (!env->prevWith)
- throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name]);
+ throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, const_cast<ExprVar&>(var));
for (size_t l = env->prevWith; l; --l, env = env->up) ;
}
}
-
void EvalState::mkList(Value & v, size_t size)
{
v.mkList(size);
@@ -1049,6 +1234,15 @@ void EvalState::cacheFile(
fileParseCache[resolvedPath] = e;
try {
+ auto dts = debugRepl
+ ? makeDebugTraceStacker(
+ *this,
+ *e,
+ this->baseEnv,
+ e->getPos() ? std::optional(ErrPos(positions[e->getPos()])) : std::nullopt,
+ "while evaluating the file '%1%':", resolvedPath)
+ : nullptr;
+
// Enforce that 'flake.nix' is a direct attrset, not a
// computation.
if (mustBeTrivial &&
@@ -1076,7 +1270,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
Value v;
e->eval(*this, env, v);
if (v.type() != nBool)
- throwTypeError("value is %1% while a Boolean was expected", v);
+ throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e);
return v.boolean;
}
@@ -1086,7 +1280,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
Value v;
e->eval(*this, env, v);
if (v.type() != nBool)
- throwTypeError(pos, "value is %1% while a Boolean was expected", v);
+ throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e);
return v.boolean;
}
@@ -1095,7 +1289,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
{
e->eval(*this, env, v);
if (v.type() != nAttrs)
- throwTypeError("value is %1% while a set was expected", v);
+ throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e);
}
@@ -1200,7 +1394,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
auto nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
- state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos);
+ state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
@@ -1274,6 +1468,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
e->eval(state, env, vTmp);
try {
+ auto dts = state.debugRepl
+ ? makeDebugTraceStacker(
+ state,
+ *this,
+ env,
+ state.positions[pos2],
+ "while evaluating the attribute '%1%'",
+ showAttrPath(state, env, attrPath))
+ : nullptr;
for (auto & i : attrPath) {
state.nrLookups++;
@@ -1296,7 +1499,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
state.throwEvalError(
pos,
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
- "attribute '%1%' missing", state.symbols[name]);
+ "attribute '%1%' missing", state.symbols[name], env, *this);
}
}
vAttrs = j->value;
@@ -1387,7 +1590,6 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (!lambda.hasFormals())
env2.values[displ++] = args[0];
-
else {
forceAttrs(*args[0], pos);
@@ -1402,7 +1604,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
auto j = args[0]->attrs->get(i.name);
if (!j) {
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
- lambda, i.name);
+ lambda, i.name, *fun.lambda.env, lambda);
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
attrsUsed++;
@@ -1424,8 +1626,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
pos,
Suggestions::bestMatches(formalNames, symbols[i.name]),
"%1% called with unexpected argument '%2%'",
- lambda,
- i.name);
+ lambda, i.name, *fun.lambda.env, lambda);
}
abort(); // can't happen
}
@@ -1436,6 +1637,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* Evaluate the body. */
try {
+ auto dts = debugRepl
+ ? makeDebugTraceStacker(
+ *this, *lambda.body, env2, positions[lambda.pos],
+ "while evaluating %s",
+ lambda.name
+ ? concatStrings("'", symbols[lambda.name], "'")
+ : "anonymous lambda")
+ : nullptr;
+
lambda.body->eval(*this, env2, vCur);
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
@@ -1590,8 +1800,8 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
-https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name]);
-
+https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name],
+ *fun.lambda.env, *fun.lambda.fun);
}
}
}
@@ -1623,7 +1833,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
if (!state.evalBool(env, cond, pos)) {
std::ostringstream out;
cond->show(state.symbols, out);
- state.throwAssertionError(pos, "assertion '%1%' failed", out.str());
+ state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
}
body->eval(state, env, v);
}
@@ -1800,14 +2010,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf = n;
nf += vTmp.fpoint;
} else
- state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
+ state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
} else if (firstType == nFloat) {
if (vTmp.type() == nInt) {
nf += vTmp.integer;
} else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint;
} else
- state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
+ state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
} else {
if (s.empty()) s.reserve(es->size());
/* skip canonization of first path, which would only be not
@@ -1827,7 +2037,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
v.mkFloat(nf);
else if (firstType == nPath) {
if (!context.empty())
- state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
+ state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this);
v.mkPath(canonPath(str()));
} else
v.mkStringMove(c_str(), context);
@@ -1854,6 +2064,12 @@ void EvalState::forceValueDeep(Value & v)
if (v.type() == nAttrs) {
for (auto & i : *v.attrs)
try {
+ // If the value is a thunk, we're evaling. Otherwise no trace necessary.
+ auto dts = debugRepl && i.value->isThunk()
+ ? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos],
+ "while evaluating the attribute '%1%'", symbols[i.name])
+ : nullptr;
+
recurse(*i.value);
} catch (Error & e) {
addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]);
@@ -1876,6 +2092,7 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos)
forceValue(v, pos);
if (v.type() != nInt)
throwTypeError(pos, "value is %1% while an integer was expected", v);
+
return v.integer;
}
@@ -1918,10 +2135,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() != nString) {
- if (pos)
- throwTypeError(pos, "value is %1% while a string was expected", v);
- else
- throwTypeError("value is %1% while a string was expected", v);
+ throwTypeError(pos, "value is %1% while a string was expected", v);
}
return v.string.s;
}
@@ -2038,7 +2252,8 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
if (maybeString)
return std::move(*maybeString);
auto i = v.attrs->find(sOutPath);
- if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
+ if (i == v.attrs->end())
+ throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
}
@@ -2046,7 +2261,6 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
if (coerceMore) {
-
/* Note that `false' is represented as an empty string for
shell scripting convenience, just like `null'. */
if (v.type() == nBool && v.boolean) return "1";
@@ -2191,7 +2405,9 @@ bool EvalState::eqValues(Value & v1, Value & v2)
return v1.fpoint == v2.fpoint;
default:
- throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
+ throwEvalError("cannot compare %1% with %2%",
+ showType(v1),
+ showType(v2));
}
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 774bc17bb..4eaa3c9b0 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -13,7 +13,6 @@
#include <unordered_map>
#include <mutex>
-
namespace nix {
@@ -25,7 +24,6 @@ enum RepairFlag : bool;
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
-
struct PrimOp
{
PrimOpFun fun;
@@ -35,6 +33,11 @@ struct PrimOp
const char * doc = nullptr;
};
+#if HAVE_BOEHMGC
+ typedef std::map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *> > > ValMap;
+#else
+ typedef std::map<std::string, Value *> ValMap;
+#endif
struct Env
{
@@ -44,6 +47,10 @@ struct Env
Value * values[0];
};
+void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env);
+void printEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env, int lvl = 0);
+
+std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const Env & env);
void copyContext(const Value & v, PathSet & context);
@@ -70,8 +77,17 @@ struct RegexCache;
std::shared_ptr<RegexCache> makeRegexCache();
+struct DebugTrace {
+ std::optional<ErrPos> pos;
+ const Expr & expr;
+ const Env & env;
+ hintformat hint;
+ bool isError;
+};
+
+void debugError(Error * e, Env & env, Expr & expr);
-class EvalState
+class EvalState : public std::enable_shared_from_this<EvalState>
{
public:
SymbolTable symbols;
@@ -87,7 +103,8 @@ public:
sOutputHash, sOutputHashAlgo, sOutputHashMode,
sRecurseForDerivations,
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
- sPrefix;
+ sPrefix,
+ sOutputSpecified;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
@@ -109,12 +126,55 @@ public:
RootValue vCallFlake = nullptr;
RootValue vImportedDrvToDerivation = nullptr;
+ /* Debugger */
+ void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
+ bool debugStop;
+ bool debugQuit;
+ std::list<DebugTrace> debugTraces;
+ std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
+ const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
+ {
+ auto i = exprEnvs.find(&expr);
+ if (i != exprEnvs.end())
+ return i->second;
+ else
+ return std::shared_ptr<const StaticEnv>();;
+ }
+
+ void runDebugRepl(const Error * error, const Env & env, const Expr & expr);
+
+ template<class E>
+ [[gnu::noinline, gnu::noreturn]]
+ void debugThrow(E && error, const Env & env, const Expr & expr)
+ {
+ if (debugRepl)
+ runDebugRepl(&error, env, expr);
+
+ throw std::move(error);
+ }
+
+ template<class E>
+ [[gnu::noinline, gnu::noreturn]]
+ void debugThrowLastTrace(E && e)
+ {
+ // Call this in the situation where Expr and Env are inaccessible.
+ // The debugger will start in the last context that's in the
+ // DebugTrace stack.
+ if (debugRepl && !debugTraces.empty()) {
+ const DebugTrace & last = debugTraces.front();
+ runDebugRepl(&e, last.env, last.expr);
+ }
+
+ throw std::move(e);
+ }
+
+
private:
SrcToStore srcToStore;
/* A cache from path names to parse trees. */
#if HAVE_BOEHMGC
- typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *> > > FileParseCache;
+ typedef std::map<Path, Expr *, std::less<Path>, traceable_allocator<std::pair<const Path, Expr *>>> FileParseCache;
#else
typedef std::map<Path, Expr *> FileParseCache;
#endif
@@ -122,7 +182,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
@@ -185,10 +245,10 @@ public:
/* Parse a Nix expression from the specified file. */
Expr * parseExprFromFile(const Path & path);
- Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
+ Expr * parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv> & staticEnv);
/* Parse a Nix expression from the specified string. */
- Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
+ Expr * parseExprFromString(std::string s, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromString(std::string s, const Path & basePath);
Expr * parseStdin();
@@ -198,7 +258,7 @@ public:
trivial (i.e. doesn't require arbitrary computation). */
void evalFile(const Path & path, Value & v, bool mustBeTrivial = false);
- /* Like `cacheFile`, but with an already parsed expression. */
+ /* Like `evalFile`, but with an already parsed expression. */
void cacheFile(
const Path & path,
const Path & resolvedPath,
@@ -255,37 +315,68 @@ public:
std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s) const;
+ void throwEvalError(const PosIdx pos, const char * s);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const char * s, const std::string & s2,
+ Env & env, Expr & expr);
+ [[gnu::noinline, gnu::noreturn]]
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s, const Value & v) const;
+ void throwEvalError(const char * s, const std::string & s2, const std::string & s3,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const char * s, const std::string & s2) const;
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
- const std::string & s2) const;
+ void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3);
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
+ void throwEvalError(const char * s, const std::string & s2, const std::string & s3);
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
+ void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
+ void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2,
+ Env & env, Expr & expr);
+
[[gnu::noinline, gnu::noreturn]]
- void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const;
+ void throwTypeError(const PosIdx pos, const char * s, const Value & v);
[[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s) const;
+ void throwTypeError(const PosIdx pos, const char * s, const Value & v,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2) const;
+ void throwTypeError(const PosIdx pos, const char * s);
[[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
- const ExprLambda & fun, const Symbol s2) const;
+ void throwTypeError(const PosIdx pos, const char * s,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwTypeError(const char * s, const Value & v) const;
+ void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const;
+ void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2,
+ Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
- void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const;
+ void throwTypeError(const char * s, const Value & v,
+ Env & env, Expr & expr);
+
[[gnu::noinline, gnu::noreturn]]
- void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const;
+ void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1,
+ Env & env, Expr & expr);
+
+ [[gnu::noinline, gnu::noreturn]]
+ void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1,
+ Env & env, Expr & expr);
[[gnu::noinline]]
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
@@ -325,7 +416,7 @@ public:
Env & baseEnv;
/* The same, but used during parsing to resolve variables. */
- StaticEnv staticBaseEnv; // !!! should be private
+ std::shared_ptr<StaticEnv> staticBaseEnv; // !!! should be private
private:
@@ -366,7 +457,7 @@ private:
friend struct ExprLet;
Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
- const PathView basePath, StaticEnv & staticEnv);
+ const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv);
public:
@@ -461,6 +552,16 @@ private:
friend struct Value;
};
+struct DebugTraceStacker {
+ DebugTraceStacker(EvalState & evalState, DebugTrace t);
+ ~DebugTraceStacker()
+ {
+ // assert(evalState.debugTraces.front() == trace);
+ evalState.debugTraces.pop_front();
+ }
+ EvalState & evalState;
+ DebugTrace trace;
+};
/* Return a string representing the type of the value `v'. */
std::string_view showType(ValueType type);
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 35c841897..920726b73 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -513,6 +513,15 @@ LockedFlake lockFlake(
if (!lockFlags.allowMutable && !input.ref->input.isLocked())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
+ /* Note: in case of an --override-input, we use
+ the *original* ref (input2.ref) for the
+ "original" field, rather than the
+ override. This ensures that the override isn't
+ nuked the next time we update the lock
+ file. That is, overrides are sticky unless you
+ use --no-write-lock-file. */
+ auto ref = input2.ref ? *input2.ref : *input.ref;
+
if (input.isFlake) {
Path localPath = parentPath;
FlakeRef localRef = *input.ref;
@@ -524,15 +533,7 @@ LockedFlake lockFlake(
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
- /* Note: in case of an --override-input, we use
- the *original* ref (input2.ref) for the
- "original" field, rather than the
- override. This ensures that the override isn't
- nuked the next time we update the lock
- file. That is, overrides are sticky unless you
- use --no-write-lock-file. */
- auto childNode = std::make_shared<LockedNode>(
- inputFlake.lockedRef, input2.ref ? *input2.ref : *input.ref);
+ auto childNode = std::make_shared<LockedNode>(inputFlake.lockedRef, ref);
node->inputs.insert_or_assign(id, childNode);
@@ -560,7 +561,7 @@ LockedFlake lockFlake(
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, *input.ref, useRegistries, flakeCache);
node->inputs.insert_or_assign(id,
- std::make_shared<LockedNode>(lockedRef, *input.ref, false));
+ std::make_shared<LockedNode>(lockedRef, ref, false));
}
}
diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc
index d616b3921..346741dd5 100644
--- a/src/libexpr/get-drvs.cc
+++ b/src/libexpr/get-drvs.cc
@@ -132,23 +132,36 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
} else
outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt);
}
+
if (!onlyOutputsToInstall || !attrs)
return outputs;
- /* 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'");
- /* ^ this shows during `nix-env -i` right under the bad derivation */
- if (!outTI->isList()) throw errMsg;
- Outputs result;
- for (auto elem : outTI->listItems()) {
- if (elem->type() != nString) throw errMsg;
- auto out = outputs.find(elem->string.s);
- if (out == outputs.end()) throw errMsg;
+ Bindings::iterator i;
+ if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos)) {
+ Outputs result;
+ auto out = outputs.find(queryOutputName());
+ if (out == outputs.end())
+ throw Error("derivation does not have output '%s'", queryOutputName());
result.insert(*out);
+ return result;
+ }
+
+ else {
+ /* 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'");
+ /* ^ this shows during `nix-env -i` right under the bad derivation */
+ if (!outTI->isList()) throw errMsg;
+ Outputs result;
+ for (auto elem : outTI->listItems()) {
+ if (elem->type() != nString) throw errMsg;
+ auto out = outputs.find(elem->string.s);
+ if (out == outputs.end()) throw errMsg;
+ result.insert(*out);
+ }
+ return result;
}
- return result;
}
diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh
index 7cc1abef2..bbd2d3c47 100644
--- a/src/libexpr/get-drvs.hh
+++ b/src/libexpr/get-drvs.hh
@@ -73,7 +73,7 @@ public:
#if HAVE_BOEHMGC
-typedef std::list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
+typedef std::list<DrvInfo, traceable_allocator<DrvInfo>> DrvInfos;
#else
typedef std::list<DrvInfo> DrvInfos;
#endif
diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l
index 4c28b976e..462b3b602 100644
--- a/src/libexpr/lexer.l
+++ b/src/libexpr/lexer.l
@@ -198,7 +198,7 @@ or { return OR_KW; }
(...|\$[^\{\"\\]|\\.|\$\\.)+ would have triggered.
This is technically invalid, but we leave the problem to the
parser who fails with exact location. */
- return STR;
+ return EOF;
}
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index c529fdc89..7c623a07d 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -6,10 +6,8 @@
#include <cstdlib>
-
namespace nix {
-
/* Displaying abstract syntax trees. */
static void showString(std::ostream & str, std::string_view s)
@@ -294,35 +292,46 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
/* Computing levels/displacements for variables. */
-void Expr::bindVars(const EvalState & es, const StaticEnv & env)
+void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
abort();
}
-void ExprInt::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprInt::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
}
-void ExprFloat::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprFloat::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
}
-void ExprString::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprString::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
}
-void ExprPath::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprPath::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
}
-void ExprVar::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
/* Check whether the variable appears in the environment. If so,
set its level and displacement. */
const StaticEnv * curEnv;
Level level;
int withLevel = -1;
- for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) {
+ for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up, level++) {
if (curEnv->isWith) {
if (withLevel == -1) withLevel = level;
} else {
@@ -348,8 +357,11 @@ void ExprVar::bindVars(const EvalState & es, const StaticEnv & env)
this->level = withLevel;
}
-void ExprSelect::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
e->bindVars(es, env);
if (def) def->bindVars(es, env);
for (auto & i : attrPath)
@@ -357,64 +369,78 @@ void ExprSelect::bindVars(const EvalState & es, const StaticEnv & env)
i.expr->bindVars(es, env);
}
-void ExprOpHasAttr::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
e->bindVars(es, env);
for (auto & i : attrPath)
if (!i.symbol)
i.expr->bindVars(es, env);
}
-void ExprAttrs::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
- const StaticEnv * dynamicEnv = &env;
- StaticEnv newEnv(false, &env, recursive ? attrs.size() : 0);
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
if (recursive) {
- dynamicEnv = &newEnv;
+ auto newEnv = std::make_shared<StaticEnv>(false, env.get(), recursive ? attrs.size() : 0);
Displacement displ = 0;
for (auto & i : attrs)
- newEnv.vars.emplace_back(i.first, i.second.displ = displ++);
+ newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
// No need to sort newEnv since attrs is in sorted order.
for (auto & i : attrs)
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
- }
- else
+ for (auto & i : dynamicAttrs) {
+ i.nameExpr->bindVars(es, newEnv);
+ i.valueExpr->bindVars(es, newEnv);
+ }
+ }
+ else {
for (auto & i : attrs)
i.second.e->bindVars(es, env);
- for (auto & i : dynamicAttrs) {
- i.nameExpr->bindVars(es, *dynamicEnv);
- i.valueExpr->bindVars(es, *dynamicEnv);
+ for (auto & i : dynamicAttrs) {
+ i.nameExpr->bindVars(es, env);
+ i.valueExpr->bindVars(es, env);
+ }
}
}
-void ExprList::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprList::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
for (auto & i : elems)
i->bindVars(es, env);
}
-void ExprLambda::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
- StaticEnv newEnv(
- false, &env,
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
+ auto newEnv = std::make_shared<StaticEnv>(
+ false, env.get(),
(hasFormals() ? formals->formals.size() : 0) +
(!arg ? 0 : 1));
Displacement displ = 0;
- if (arg) newEnv.vars.emplace_back(arg, displ++);
+ if (arg) newEnv->vars.emplace_back(arg, displ++);
if (hasFormals()) {
for (auto & i : formals->formals)
- newEnv.vars.emplace_back(i.name, displ++);
+ newEnv->vars.emplace_back(i.name, displ++);
- newEnv.sort();
+ newEnv->sort();
for (auto & i : formals->formals)
if (i.def) i.def->bindVars(es, newEnv);
@@ -423,20 +449,26 @@ void ExprLambda::bindVars(const EvalState & es, const StaticEnv & env)
body->bindVars(es, newEnv);
}
-void ExprCall::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
fun->bindVars(es, env);
for (auto e : args)
e->bindVars(es, env);
}
-void ExprLet::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
- StaticEnv newEnv(false, &env, attrs->attrs.size());
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
+ auto newEnv = std::make_shared<StaticEnv>(false, env.get(), attrs->attrs.size());
Displacement displ = 0;
for (auto & i : attrs->attrs)
- newEnv.vars.emplace_back(i.first, i.second.displ = displ++);
+ newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
// No need to sort newEnv since attrs->attrs is in sorted order.
@@ -446,51 +478,71 @@ void ExprLet::bindVars(const EvalState & es, const StaticEnv & env)
body->bindVars(es, newEnv);
}
-void ExprWith::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
/* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous
`with' if this one doesn't contain the desired attribute. */
const StaticEnv * curEnv;
Level level;
prevWith = 0;
- for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++)
+ for (curEnv = env.get(), level = 1; curEnv; curEnv = curEnv->up, level++)
if (curEnv->isWith) {
prevWith = level;
break;
}
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
attrs->bindVars(es, env);
- StaticEnv newEnv(true, &env);
+ auto newEnv = std::make_shared<StaticEnv>(true, env.get());
body->bindVars(es, newEnv);
}
-void ExprIf::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprIf::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
cond->bindVars(es, env);
then->bindVars(es, env);
else_->bindVars(es, env);
}
-void ExprAssert::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprAssert::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
cond->bindVars(es, env);
body->bindVars(es, env);
}
-void ExprOpNot::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
e->bindVars(es, env);
}
-void ExprConcatStrings::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
+
for (auto & i : *this->es)
i.second->bindVars(es, env);
}
-void ExprPos::bindVars(const EvalState & es, const StaticEnv & env)
+void ExprPos::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
+ if (es.debugRepl)
+ es.exprEnvs.insert(std::make_pair(this, env));
}
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 5df69e000..5eb022770 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -22,7 +22,6 @@ MakeError(UndefinedVarError, Error);
MakeError(MissingArgumentError, EvalError);
MakeError(RestrictedPathError, Error);
-
/* Position objects. */
struct Pos
@@ -143,24 +142,25 @@ struct Expr
{
virtual ~Expr() { };
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
- virtual void bindVars(const EvalState & es, const StaticEnv & env);
+ virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol name);
+ virtual PosIdx getPos() const { return noPos; }
};
#define COMMON_METHODS \
- void show(const SymbolTable & symbols, std::ostream & str) const; \
- void eval(EvalState & state, Env & env, Value & v); \
- void bindVars(const EvalState & es, const StaticEnv & env);
+ void show(const SymbolTable & symbols, std::ostream & str) const override; \
+ void eval(EvalState & state, Env & env, Value & v) override; \
+ void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
struct ExprInt : Expr
{
NixInt n;
Value v;
ExprInt(NixInt n) : n(n) { v.mkInt(n); };
+ Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS
- Value * maybeThunk(EvalState & state, Env & env);
};
struct ExprFloat : Expr
@@ -168,8 +168,8 @@ struct ExprFloat : Expr
NixFloat nf;
Value v;
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
+ Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS
- Value * maybeThunk(EvalState & state, Env & env);
};
struct ExprString : Expr
@@ -177,8 +177,8 @@ struct ExprString : Expr
std::string s;
Value v;
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
+ Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS
- Value * maybeThunk(EvalState & state, Env & env);
};
struct ExprPath : Expr
@@ -186,8 +186,8 @@ struct ExprPath : Expr
std::string s;
Value v;
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
+ Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS
- Value * maybeThunk(EvalState & state, Env & env);
};
typedef uint32_t Level;
@@ -213,8 +213,9 @@ struct ExprVar : Expr
ExprVar(Symbol name) : name(name) { };
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
+ Value * maybeThunk(EvalState & state, Env & env) override;
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
- Value * maybeThunk(EvalState & state, Env & env);
};
struct ExprSelect : Expr
@@ -224,6 +225,7 @@ struct ExprSelect : Expr
AttrPath attrPath;
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -232,6 +234,7 @@ struct ExprOpHasAttr : Expr
Expr * e;
AttrPath attrPath;
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
+ PosIdx getPos() const override { return e->getPos(); }
COMMON_METHODS
};
@@ -260,6 +263,7 @@ struct ExprAttrs : Expr
DynamicAttrDefs dynamicAttrs;
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
ExprAttrs() : recursive(false) { };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -268,6 +272,11 @@ struct ExprList : Expr
std::vector<Expr *> elems;
ExprList() { };
COMMON_METHODS
+
+ PosIdx getPos() const override
+ {
+ return elems.empty() ? noPos : elems.front()->getPos();
+ }
};
struct Formal
@@ -317,9 +326,10 @@ struct ExprLambda : Expr
: pos(pos), formals(formals), body(body)
{
}
- void setName(Symbol name);
+ void setName(Symbol name) override;
std::string showNamePos(const EvalState & state) const;
inline bool hasFormals() const { return formals != nullptr; }
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -331,6 +341,7 @@ struct ExprCall : Expr
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
: fun(fun), args(args), pos(pos)
{ }
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -348,6 +359,7 @@ struct ExprWith : Expr
Expr * attrs, * body;
size_t prevWith;
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -356,6 +368,7 @@ struct ExprIf : Expr
PosIdx pos;
Expr * cond, * then, * else_;
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -364,6 +377,7 @@ struct ExprAssert : Expr
PosIdx pos;
Expr * cond, * body;
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -381,15 +395,16 @@ struct ExprOpNot : Expr
Expr * e1, * e2; \
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
- void show(const SymbolTable & symbols, std::ostream & str) const \
+ void show(const SymbolTable & symbols, std::ostream & str) const override \
{ \
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
} \
- void bindVars(const EvalState & es, const StaticEnv & env) \
+ void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override \
{ \
e1->bindVars(es, env); e2->bindVars(es, env); \
} \
- void eval(EvalState & state, Env & env, Value & v); \
+ void eval(EvalState & state, Env & env, Value & v) override; \
+ PosIdx getPos() const override { return pos; } \
};
MakeBinOp(ExprOpEq, "==")
@@ -404,9 +419,10 @@ struct ExprConcatStrings : Expr
{
PosIdx pos;
bool forceString;
- std::vector<std::pair<PosIdx, Expr *> > * es;
- ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *> > * es)
+ std::vector<std::pair<PosIdx, Expr *>> * es;
+ ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
: pos(pos), forceString(forceString), es(es) { };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -414,6 +430,7 @@ struct ExprPos : Expr
{
PosIdx pos;
ExprPos(const PosIdx & pos) : pos(pos) { };
+ PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index be0598b75..7c9b5a2db 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -193,7 +193,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
- std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > > & es)
+ std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> & es)
{
if (es.empty()) return new ExprString("");
@@ -233,7 +233,7 @@ static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
}
/* Strip spaces from each line. */
- auto * es2 = new std::vector<std::pair<PosIdx, Expr *> >;
+ auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
@@ -320,8 +320,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
StringToken uri;
StringToken str;
std::vector<nix::AttrName> * attrNames;
- std::vector<std::pair<nix::PosIdx, nix::Expr *> > * string_parts;
- std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
+ std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
+ std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, StringToken>>> * ind_string_parts;
}
%type <e> start expr expr_function expr_if expr_op
@@ -415,7 +415,7 @@ expr_op
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
- { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
+ { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *>>({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
@@ -503,9 +503,9 @@ string_parts_interpolated
: string_parts_interpolated STR
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
+ | DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *>>; $$->emplace_back(makeCurPos(@1, data), $2); }
| STR DOLLAR_CURLY expr '}' {
- $$ = new std::vector<std::pair<PosIdx, Expr *> >;
+ $$ = new std::vector<std::pair<PosIdx, Expr *>>;
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
$$->emplace_back(makeCurPos(@2, data), $3);
}
@@ -520,6 +520,12 @@ path_start
$$ = new ExprPath(path);
}
| HPATH {
+ if (evalSettings.pureEval) {
+ throw Error(
+ "the path '%s' can not be resolved in pure mode",
+ std::string_view($1.p, $1.l)
+ );
+ }
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
$$ = new ExprPath(path);
}
@@ -528,7 +534,7 @@ path_start
ind_string_parts
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
- | { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > >; }
+ | { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>; }
;
binds
@@ -643,7 +649,7 @@ namespace nix {
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
- const PathView path, const PathView basePath, StaticEnv & staticEnv)
+ const PathView path, const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv)
{
yyscan_t scanner;
std::string file;
@@ -706,7 +712,7 @@ Expr * EvalState::parseExprFromFile(const Path & path)
}
-Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
+Expr * EvalState::parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv> & staticEnv)
{
auto buffer = readFile(path);
// readFile should have left some extra space for terminators
@@ -715,7 +721,7 @@ Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
}
-Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv)
+Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv)
{
s.append("\0\0", 2);
return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
@@ -782,13 +788,13 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
if (hasPrefix(path, "nix/"))
return concatStrings(corepkgsPrefix, path.substr(4));
- throw ThrownError({
+ debugThrowLastTrace(ThrownError({
.msg = hintfmt(evalSettings.pureEval
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
path),
.errPos = positions[pos]
- });
+ }));
}
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index fe4d0fc5f..eea274301 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -46,7 +46,7 @@ StringMap EvalState::realiseContext(const PathSet & context)
auto [ctx, outputName] = decodeContext(*store, i);
auto ctxS = store->printStorePath(ctx);
if (!store->isValidPath(ctx))
- throw InvalidPathError(store->printStorePath(ctx));
+ debugThrowLastTrace(InvalidPathError(store->printStorePath(ctx)));
if (!outputName.empty() && ctx.isDerivation()) {
drvs.push_back({ctx, {outputName}});
} else {
@@ -57,9 +57,9 @@ StringMap EvalState::realiseContext(const PathSet & context)
if (drvs.empty()) return {};
if (!evalSettings.enableImportFromDerivation)
- throw Error(
+ debugThrowLastTrace(Error(
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
- store->printStorePath(drvs.begin()->drvPath));
+ store->printStorePath(drvs.begin()->drvPath)));
/* Build/substitute the context. */
std::vector<DerivedPath> buildReqs;
@@ -72,8 +72,8 @@ StringMap EvalState::realiseContext(const PathSet & context)
for (auto & outputName : outputs) {
auto outputPath = get(outputPaths, outputName);
if (!outputPath)
- throw Error("derivation '%s' does not have an output named '%s'",
- store->printStorePath(drvPath), outputName);
+ debugThrowLastTrace(Error("derivation '%s' does not have an output named '%s'",
+ store->printStorePath(drvPath), outputName));
res.insert_or_assign(
downstreamPlaceholder(*store, drvPath, outputName),
store->printStorePath(*outputPath)
@@ -216,11 +216,11 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
Env * env = &state.allocEnv(vScope->attrs->size());
env->up = &state.baseEnv;
- StaticEnv staticEnv(false, &state.staticBaseEnv, vScope->attrs->size());
+ auto staticEnv = std::make_shared<StaticEnv>(false, state.staticBaseEnv.get(), vScope->attrs->size());
unsigned int displ = 0;
for (auto & attr : *vScope->attrs) {
- staticEnv.vars.emplace_back(attr.name, displ);
+ staticEnv->vars.emplace_back(attr.name, displ);
env->values[displ++] = attr.value;
}
@@ -319,17 +319,16 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle)
- throw EvalError("could not open '%1%': %2%", path, dlerror());
+ state.debugThrowLastTrace(EvalError("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("could not load symbol '%1%' from '%2%': %3%", sym, path, message);
+ state.debugThrowLastTrace(EvalError("could not load symbol '%1%' from '%2%': %3%", sym, path, message));
else
- throw EvalError("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected",
- sym, path);
+ state.debugThrowLastTrace(EvalError("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected", sym, path));
}
(func)(state, v);
@@ -344,12 +343,11 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
state.forceList(*args[0], pos);
auto elems = args[0]->listElems();
auto count = args[0]->listSize();
- if (count == 0) {
- throw EvalError({
+ if (count == 0)
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("at least one argument to 'exec' required"),
.errPos = state.positions[pos]
- });
- }
+ }));
PathSet context;
auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
Strings commandArgs;
@@ -359,11 +357,11 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("cannot execute '%1%', since path '%2%' is not valid",
program, e.path),
.errPos = state.positions[pos]
- });
+ }));
}
auto output = runProgram(program, true, commandArgs);
@@ -547,7 +545,7 @@ struct CompareValues
if (v1->type() == nInt && v2->type() == nFloat)
return v1->integer < v2->fpoint;
if (v1->type() != v2->type())
- throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
+ state.debugThrowLastTrace(EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)));
switch (v1->type()) {
case nInt:
return v1->integer < v2->integer;
@@ -569,14 +567,14 @@ struct CompareValues
}
}
default:
- throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
+ state.debugThrowLastTrace(EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)));
}
}
};
#if HAVE_BOEHMGC
-typedef std::list<Value *, gc_allocator<Value *> > ValueList;
+typedef std::list<Value *, gc_allocator<Value *>> ValueList;
#else
typedef std::list<Value *> ValueList;
#endif
@@ -599,10 +597,10 @@ static Bindings::iterator getAttr(
auto aPos = attrSet->pos;
if (!aPos) {
- throw TypeError({
+ state.debugThrowLastTrace(TypeError({
.msg = errorMsg,
.errPos = state.positions[pos],
- });
+ }));
} else {
auto e = TypeError({
.msg = errorMsg,
@@ -612,7 +610,7 @@ static Bindings::iterator getAttr(
// Adding another trace for the function name to make it clear
// which call received wrong arguments.
e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", funcName));
- throw e;
+ state.debugThrowLastTrace(e);
}
}
@@ -666,10 +664,10 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a
Bindings::iterator key =
e->attrs->find(state.sKey);
if (key == e->attrs->end())
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("attribute 'key' required"),
.errPos = state.positions[pos]
- });
+ }));
state.forceValue(*key->value, pos);
if (!doneKeys.insert(key->value).second) continue;
@@ -725,6 +723,41 @@ static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info {
.fun = prim_genericClosure,
});
+
+static RegisterPrimOp primop_break({
+ .name = "break",
+ .args = {"v"},
+ .doc = R"(
+ In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL.
+ Otherwise, return the argument `v`.
+ )",
+ .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
+ {
+ if (state.debugRepl && !state.debugTraces.empty()) {
+ auto error = Error(ErrorInfo {
+ .level = lvlInfo,
+ .msg = hintfmt("breakpoint reached"),
+ .errPos = state.positions[pos],
+ });
+
+ auto & dt = state.debugTraces.front();
+ state.runDebugRepl(&error, dt.env, dt.expr);
+
+ if (state.debugQuit) {
+ // If the user elects to quit the repl, throw an exception.
+ throw Error(ErrorInfo{
+ .level = lvlInfo,
+ .msg = hintfmt("quit the debugger"),
+ .errPos = state.positions[noPos],
+ });
+ }
+ }
+
+ // Return the value we were passed.
+ v = *args[0];
+ }
+});
+
static RegisterPrimOp primop_abort({
.name = "abort",
.args = {"s"},
@@ -735,7 +768,7 @@ static RegisterPrimOp primop_abort({
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned();
- throw Abort("evaluation aborted with the following error message: '%1%'", s);
+ state.debugThrowLastTrace(Abort("evaluation aborted with the following error message: '%1%'", s));
}
});
@@ -753,7 +786,7 @@ static RegisterPrimOp primop_throw({
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context).toOwned();
- throw ThrownError(s);
+ state.debugThrowLastTrace(ThrownError(s));
}
});
@@ -1008,37 +1041,37 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
else
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("invalid value '%s' for 'outputHashMode' attribute", s),
.errPos = state.positions[posDrvName]
- });
+ }));
};
auto handleOutputs = [&](const Strings & ss) {
outputs.clear();
for (auto & j : ss) {
if (outputs.find(j) != outputs.end())
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("duplicate derivation output '%1%'", j),
.errPos = state.positions[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({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("invalid derivation output name 'drv'" ),
.errPos = state.positions[posDrvName]
- });
+ }));
outputs.insert(j);
}
if (outputs.empty())
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("derivation cannot have an empty set of outputs"),
.errPos = state.positions[posDrvName]
- });
+ }));
};
try {
@@ -1163,23 +1196,23 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
/* Do we have all required attributes? */
if (drv.builder == "")
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("required attribute 'builder' missing"),
.errPos = state.positions[posDrvName]
- });
+ }));
if (drv.platform == "")
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("required attribute 'system' missing"),
.errPos = state.positions[posDrvName]
- });
+ }));
/* Check whether the derivation name is valid. */
if (isDerivation(drvName))
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("derivation names are not allowed to end in '%s'", drvExtension),
.errPos = state.positions[posDrvName]
- });
+ }));
if (outputHash) {
/* Handle fixed-output derivations.
@@ -1187,10 +1220,10 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * *
Ignore `__contentAddressed` because fixed output derivations are
already content addressed. */
if (outputs.size() != 1 || *(outputs.begin()) != "out")
- throw Error({
+ state.debugThrowLastTrace(Error({
.msg = hintfmt("multiple outputs are not supported in fixed-output derivations"),
.errPos = state.positions[posDrvName]
- });
+ }));
auto h = newHashAllowEmpty(*outputHash, parseHashTypeOpt(outputHashAlgo));
@@ -1358,10 +1391,10 @@ static RegisterPrimOp primop_toPath({
static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
if (evalSettings.pureEval)
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
.errPos = state.positions[pos]
- });
+ }));
PathSet context;
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context));
@@ -1370,10 +1403,10 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args,
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path)) path = canonPath(path, true);
if (!state.store->isInStore(path))
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = state.positions[pos]
- });
+ }));
auto path2 = state.store->toStorePath(path).first;
if (!settings.readOnlyMode)
state.store->ensurePath(path2);
@@ -1476,7 +1509,7 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V
auto path = realisePath(state, pos, *args[0]);
auto s = readFile(path);
if (s.find((char) 0) != std::string::npos)
- throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
+ state.debugThrowLastTrace(Error("the contents of the file '%1%' cannot be represented as a Nix string", path));
StorePathSet refs;
if (state.store->isInStore(path)) {
try {
@@ -1528,13 +1561,12 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V
auto rewrites = state.realiseContext(context);
path = rewriteStrings(path, rewrites);
} catch (InvalidPathError & e) {
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
.errPos = state.positions[pos]
- });
+ }));
}
-
searchPath.emplace_back(prefix, path);
}
@@ -1555,10 +1587,10 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, V
auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
if (!ht)
- throw Error({
+ state.debugThrowLastTrace(Error({
.msg = hintfmt("unknown hash type '%1%'", type),
.errPos = state.positions[pos]
- });
+ }));
auto path = realisePath(state, pos, *args[1]);
@@ -1795,13 +1827,13 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val
for (auto path : context) {
if (path.at(0) != '/')
- throw EvalError( {
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt(
"in 'toFile': the file named '%1%' must not contain a reference "
"to a derivation but contains (%2%)",
name, path),
.errPos = state.positions[pos]
- });
+ }));
refs.insert(state.store->parseStorePath(path));
}
@@ -1959,7 +1991,7 @@ static void addPath(
? state.store->computeStorePathForPath(name, path, method, htSHA256, filter).first
: state.store->addToStore(name, path, method, htSHA256, filter, state.repair, refs);
if (expectedHash && expectedStorePath != dstPath)
- throw Error("store path mismatch in (possibly filtered) path added from '%s'", path);
+ state.debugThrowLastTrace(Error("store path mismatch in (possibly filtered) path added from '%s'", path));
state.allowAndSetStorePathString(dstPath, v);
} else
state.allowAndSetStorePathString(*expectedStorePath, v);
@@ -1977,12 +2009,12 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg
state.forceValue(*args[0], pos);
if (args[0]->type() != nFunction)
- throw TypeError({
+ state.debugThrowLastTrace(TypeError({
.msg = hintfmt(
"first argument in call to 'filterSource' is not a function but %1%",
showType(*args[0])),
.errPos = state.positions[pos]
- });
+ }));
addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context);
}
@@ -2066,16 +2098,16 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value
else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
else
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("unsupported argument '%1%' to 'addPath'", state.symbols[attr.name]),
.errPos = state.positions[attr.pos]
- });
+ }));
}
if (path.empty())
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'path' required"),
.errPos = state.positions[pos]
- });
+ }));
if (name.empty())
name = baseNameOf(path);
@@ -2447,10 +2479,10 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg
return;
}
if (!args[0]->isLambda())
- throw TypeError({
+ state.debugThrowLastTrace(TypeError({
.msg = hintfmt("'functionArgs' requires a function"),
.errPos = state.positions[pos]
- });
+ }));
if (!args[0]->lambda.fun->hasFormals()) {
v.mkAttrs(&state.emptyBindings);
@@ -2538,7 +2570,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg
attrsSeen[attr.name].first++;
} catch (TypeError & e) {
e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", "zipAttrsWith"));
- throw;
+ state.debugThrowLastTrace(e);
}
}
@@ -2625,10 +2657,10 @@ static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Val
{
state.forceList(list, pos);
if (n < 0 || (unsigned int) n >= list.listSize())
- throw Error({
+ state.debugThrowLastTrace(Error({
.msg = hintfmt("list index %1% is out of bounds", n),
.errPos = state.positions[pos]
- });
+ }));
state.forceValue(*list.listElems()[n], pos);
v = *list.listElems()[n];
}
@@ -2673,10 +2705,10 @@ static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value
{
state.forceList(*args[0], pos);
if (args[0]->listSize() == 0)
- throw Error({
+ state.debugThrowLastTrace(Error({
.msg = hintfmt("'tail' called on an empty list"),
.errPos = state.positions[pos]
- });
+ }));
state.mkList(v, args[0]->listSize() - 1);
for (unsigned int n = 0; n < v.listSize(); ++n)
@@ -2911,10 +2943,10 @@ static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Va
auto len = state.forceInt(*args[1], pos);
if (len < 0)
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("cannot create list of size %1%", len),
.errPos = state.positions[pos]
- });
+ }));
state.mkList(v, len);
@@ -3123,7 +3155,7 @@ static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args,
state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)));
} catch (TypeError &e) {
e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", "concatMap"));
- throw;
+ state.debugThrowLastTrace(e);
}
len += lists[n].listSize();
}
@@ -3218,10 +3250,10 @@ static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value
NixFloat f2 = state.forceFloat(*args[1], pos);
if (f2 == 0)
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("division by zero"),
.errPos = state.positions[pos]
- });
+ }));
if (args[0]->type() == nFloat || args[1]->type() == nFloat) {
v.mkFloat(state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
@@ -3230,10 +3262,10 @@ static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value
NixInt i2 = state.forceInt(*args[1], pos);
/* Avoid division overflow as it might raise SIGFPE. */
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("overflow in integer division"),
.errPos = state.positions[pos]
- });
+ }));
v.mkInt(i1 / i2);
}
@@ -3361,10 +3393,10 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args,
auto s = state.coerceToString(pos, *args[2], context);
if (start < 0)
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("negative start position in 'substring'"),
.errPos = state.positions[pos]
- });
+ }));
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
}
@@ -3412,10 +3444,10 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args,
auto type = state.forceStringNoCtx(*args[0], pos);
std::optional<HashType> ht = parseHashType(type);
if (!ht)
- throw Error({
+ state.debugThrowLastTrace(Error({
.msg = hintfmt("unknown hash type '%1%'", type),
.errPos = state.positions[pos]
- });
+ }));
PathSet context; // discarded
auto s = state.forceString(*args[1], context, pos);
@@ -3482,19 +3514,18 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v)
(v.listElems()[i] = state.allocValue())->mkString(match[i + 1].str());
}
- } catch (std::regex_error &e) {
+ } catch (std::regex_error & e) {
if (e.code() == std::regex_constants::error_space) {
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("memory limit exceeded by regular expression '%s'", re),
.errPos = state.positions[pos]
- });
- } else {
- throw EvalError({
+ }));
+ } else
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("invalid regular expression '%s'", re),
.errPos = state.positions[pos]
- });
- }
+ }));
}
}
@@ -3587,19 +3618,18 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v)
assert(idx == 2 * len + 1);
- } catch (std::regex_error &e) {
+ } catch (std::regex_error & e) {
if (e.code() == std::regex_constants::error_space) {
// limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("memory limit exceeded by regular expression '%s'", re),
.errPos = state.positions[pos]
- });
- } else {
- throw EvalError({
+ }));
+ } else
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("invalid regular expression '%s'", re),
.errPos = state.positions[pos]
- });
- }
+ }));
}
}
@@ -3675,10 +3705,10 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
state.forceList(*args[0], pos);
state.forceList(*args[1], pos);
if (args[0]->listSize() != args[1]->listSize())
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
.errPos = state.positions[pos]
- });
+ }));
std::vector<std::string> from;
from.reserve(args[0]->listSize());
@@ -3931,7 +3961,7 @@ void EvalState::createBaseEnv()
because attribute lookups expect it to be sorted. */
baseEnv.values[0]->attrs->sort();
- staticBaseEnv.sort();
+ staticBaseEnv->sort();
/* Note: we have to initialize the 'derivation' constant *after*
building baseEnv/staticBaseEnv because it uses 'builtins'. */
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index d7c3c9918..84e7f5c02 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -108,16 +108,16 @@ static void fetchTree(
if (auto aType = args[0]->attrs->get(state.sType)) {
if (type)
- throw Error({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("unexpected attribute 'type'"),
.errPos = state.positions[pos]
- });
+ }));
type = state.forceStringNoCtx(*aType->value, aType->pos);
} else if (!type)
- throw Error({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
.errPos = state.positions[pos]
- });
+ }));
attrs.emplace("type", type.value());
@@ -138,16 +138,16 @@ static void fetchTree(
else if (attr.value->type() == nInt)
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
else
- throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
- state.symbols[attr.name], showType(*attr.value));
+ state.debugThrowLastTrace(TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
+ state.symbols[attr.name], showType(*attr.value)));
}
if (!params.allowNameArgument)
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
- throw Error({
- .msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
+ state.debugThrowLastTrace(EvalError({
+ .msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
.errPos = state.positions[pos]
- });
+ }));
input = fetchers::Input::fromAttrs(std::move(attrs));
} else {
@@ -167,7 +167,7 @@ static void fetchTree(
input = lookupInRegistries(state.store, input).first;
if (evalSettings.pureEval && !input.isLocked())
- throw Error("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]);
+ state.debugThrowLastTrace(EvalError("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]));
auto [tree, input2] = input.fetch(state.store);
@@ -206,17 +206,17 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, attr.pos);
else
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
.errPos = state.positions[attr.pos]
- });
- }
+ }));
+ }
if (!url)
- throw EvalError({
+ state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'url' argument required"),
.errPos = state.positions[pos]
- });
+ }));
} else
url = state.forceStringNoCtx(*args[0], pos);
@@ -228,7 +228,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
name = baseNameOf(*url);
if (evalSettings.pureEval && !expectedHash)
- throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
+ state.debugThrowLastTrace(EvalError("in pure evaluation mode, '%s' requires a 'sha256' argument", who));
// early exit if pinned and already in the store
if (expectedHash && expectedHash->type == htSHA256) {
@@ -255,8 +255,8 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
? state.store->queryPathInfo(storePath)->narHash
: hashFile(htSHA256, state.store->toRealPath(storePath));
if (hash != *expectedHash)
- throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
- *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true));
+ state.debugThrowLastTrace(EvalError((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
+ *url, expectedHash->to_string(Base32, true), hash.to_string(Base32, true)));
}
state.allowAndSetStorePathString(storePath, v);
@@ -364,6 +364,10 @@ static RegisterPrimOp primop_fetchGit({
A Boolean parameter that specifies whether submodules should be
checked out. Defaults to `false`.
+ - shallow\
+ A Boolean parameter that specifies whether fetching a shallow clone
+ is allowed. Defaults to `false`.
+
- allRefs\
Whether to fetch all refs of the repository. With this argument being
true, it's possible to load a `rev` from *any* `ref` (by default only
diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc
index f65b6593d..16cf66d2c 100644
--- a/src/libexpr/tests/primops.cc
+++ b/src/libexpr/tests/primops.cc
@@ -540,22 +540,22 @@ namespace nix {
ASSERT_THAT(v, IsStringEq(output));
}
-#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " #input), std::string_view(output)))
+#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " input), std::string_view(output)))
INSTANTIATE_TEST_SUITE_P(
toString,
ToStringPrimOpTest,
testing::Values(
- CASE("foo", "foo"),
- CASE(1, "1"),
- CASE([1 2 3], "1 2 3"),
- CASE(.123, "0.123000"),
- CASE(true, "1"),
- CASE(false, ""),
- CASE(null, ""),
- CASE({ v = "bar"; __toString = self: self.v; }, "bar"),
- CASE({ v = "bar"; __toString = self: self.v; outPath = "foo"; }, "bar"),
- CASE({ outPath = "foo"; }, "foo"),
- CASE(./test, "/test")
+ CASE(R"("foo")", "foo"),
+ CASE(R"(1)", "1"),
+ CASE(R"([1 2 3])", "1 2 3"),
+ CASE(R"(.123)", "0.123000"),
+ CASE(R"(true)", "1"),
+ CASE(R"(false)", ""),
+ CASE(R"(null)", ""),
+ CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
+ CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
+ CASE(R"({ outPath = "foo"; })", "foo"),
+ CASE(R"(./test)", "/test")
)
);
#undef CASE
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 68235ad11..03504db61 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -85,6 +85,7 @@ void printValueAsJSON(EvalState & state, bool strict,
.errPos = state.positions[v.determinePos(pos)]
});
e.addTrace(state.positions[pos], hintfmt("message for the trace"));
+ state.debugThrowLastTrace(e);
throw e;
}
}
@@ -99,7 +100,7 @@ void printValueAsJSON(EvalState & state, bool strict,
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
JSONPlaceholder & out, PathSet & context) const
{
- throw TypeError("cannot convert %1% to JSON", showType());
+ state.debugThrowLastTrace(TypeError("cannot convert %1% to JSON", showType()));
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 58a8a56a0..2008df74d 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -404,9 +404,9 @@ public:
#if HAVE_BOEHMGC
-typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
-typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
-typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
+typedef std::vector<Value *, traceable_allocator<Value *>> ValueVector;
+typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *>>> ValueMap;
+typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector>>> ValueVectorMap;
#else
typedef std::vector<Value *> ValueVector;
typedef std::map<Symbol, Value *> ValueMap;
diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh
index 04c9feda0..6452143a1 100644
--- a/src/libfetchers/fetch-settings.hh
+++ b/src/libfetchers/fetch-settings.hh
@@ -70,7 +70,7 @@ struct FetchSettings : public Config
Setting<bool> warnDirty{this, true, "warn-dirty",
"Whether to warn about dirty Git/Mercurial trees."};
- Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
+ Setting<std::string> flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry",
"Path or URI of the global flake registry."};
Setting<bool> useRegistries{this, true, "use-registries",
diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc
index d23a820a4..7d01aaa7a 100644
--- a/src/libfetchers/git.cc
+++ b/src/libfetchers/git.cc
@@ -26,11 +26,6 @@ namespace {
// old version of git, which will ignore unrecognized `-c` options.
const std::string gitInitialBranch = "__nix_dummy_branch";
-std::string getGitDir()
-{
- return getEnv("GIT_DIR").value_or(".git");
-}
-
bool isCacheFileWithinTtl(const time_t now, const struct stat & st)
{
return st.st_mtime + settings.tarballTtl > now;
@@ -90,8 +85,9 @@ std::optional<std::string> readHead(const Path & path)
bool storeCachedHead(const std::string& actualUrl, const std::string& headRef)
{
Path cacheDir = getCachePath(actualUrl);
+ auto gitDir = ".";
try {
- runProgram("git", true, { "-C", cacheDir, "symbolic-ref", "--", "HEAD", headRef });
+ runProgram("git", true, { "-C", cacheDir, "--git-dir", gitDir, "symbolic-ref", "--", "HEAD", headRef });
} catch (ExecError &e) {
if (!WIFEXITED(e.status)) throw;
return false;
@@ -152,7 +148,7 @@ struct WorkdirInfo
WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
{
const bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
- auto gitDir = getGitDir();
+ std::string gitDir(".git");
auto env = getEnv();
// Set LC_ALL to C: because we rely on the error messages from git rev-parse to determine what went wrong
@@ -187,7 +183,7 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
if (hasHead) {
// Using git diff is preferrable over lower-level operations here,
// because its conceptually simpler and we only need the exit code anyways.
- auto gitDiffOpts = Strings({ "-C", workdir, "diff", "HEAD", "--quiet"});
+ auto gitDiffOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "diff", "HEAD", "--quiet"});
if (!submodules) {
// Changes in submodules should only make the tree dirty
// when those submodules will be copied as well.
@@ -208,6 +204,7 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, const Path & workdir, const WorkdirInfo & workdirInfo)
{
const bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
+ auto gitDir = ".git";
if (!fetchSettings.allowDirty)
throw Error("Git tree '%s' is dirty", workdir);
@@ -215,7 +212,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
if (fetchSettings.warnDirty)
warn("Git tree '%s' is dirty", workdir);
- auto gitOpts = Strings({ "-C", workdir, "ls-files", "-z" });
+ auto gitOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "ls-files", "-z" });
if (submodules)
gitOpts.emplace_back("--recurse-submodules");
@@ -245,7 +242,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
// modified dirty file?
input.attrs.insert_or_assign(
"lastModified",
- workdirInfo.hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
+ workdirInfo.hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "--git-dir", gitDir, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
return {std::move(storePath), input};
}
@@ -370,7 +367,7 @@ struct GitInputScheme : InputScheme
{
auto sourcePath = getSourcePath(input);
assert(sourcePath);
- auto gitDir = getGitDir();
+ auto gitDir = ".git";
runProgram("git", true,
{ "-C", *sourcePath, "--git-dir", gitDir, "add", "--force", "--intent-to-add", "--", std::string(file) });
@@ -396,7 +393,7 @@ struct GitInputScheme : InputScheme
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
{
Input input(_input);
- auto gitDir = getGitDir();
+ auto gitDir = ".git";
std::string name = input.getName();
@@ -454,11 +451,10 @@ struct GitInputScheme : InputScheme
}
}
- const Attrs unlockedAttrs({
+ Attrs unlockedAttrs({
{"type", cacheType},
{"name", name},
{"url", actualUrl},
- {"ref", *input.getRef()},
});
Path repoDir;
@@ -471,6 +467,7 @@ struct GitInputScheme : InputScheme
head = "master";
}
input.attrs.insert_or_assign("ref", *head);
+ unlockedAttrs.insert_or_assign("ref", *head);
}
if (!input.getRev())
@@ -487,6 +484,7 @@ struct GitInputScheme : InputScheme
head = "master";
}
input.attrs.insert_or_assign("ref", *head);
+ unlockedAttrs.insert_or_assign("ref", *head);
}
if (auto res = getCache()->lookup(store, unlockedAttrs)) {
@@ -576,7 +574,7 @@ struct GitInputScheme : InputScheme
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "rev-parse", "--is-shallow-repository" })) == "true";
if (isShallow && !shallow)
- throw Error("'%s' is a shallow Git repository, but a non-shallow repository is needed", actualUrl);
+ throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified.", actualUrl);
// FIXME: check whether rev is an ancestor of ref.
diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc
index 0631fb6e8..a491d82a6 100644
--- a/src/libfetchers/github.cc
+++ b/src/libfetchers/github.cc
@@ -381,7 +381,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
Headers headers = makeHeadersWithAuthTokens(host);
- std::string ref_uri;
+ std::string refUri;
if (ref == "HEAD") {
auto file = store->toRealPath(
downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath);
@@ -393,10 +393,11 @@ struct SourceHutInputScheme : GitArchiveInputScheme
if (!remoteLine) {
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
}
- ref_uri = remoteLine->target;
+ refUri = remoteLine->target;
} else {
- ref_uri = fmt("refs/(heads|tags)/%s", ref);
+ refUri = fmt("refs/(heads|tags)/%s", ref);
}
+ std::regex refRegex(refUri);
auto file = store->toRealPath(
downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
@@ -406,7 +407,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
std::optional<std::string> id;
while(!id && getline(is, line)) {
auto parsedLine = git::parseLsRemoteLine(line);
- if (parsedLine && parsedLine->reference == ref_uri)
+ if (parsedLine && parsedLine->reference && std::regex_match(*parsedLine->reference, refRegex))
id = parsedLine->target;
}
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index dde0ad761..6c551bd93 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -6,6 +6,7 @@
#include "archive.hh"
#include "tarfile.hh"
#include "types.hh"
+#include "split.hh"
namespace nix::fetchers {
@@ -168,24 +169,34 @@ std::pair<Tree, time_t> downloadTarball(
};
}
-struct TarballInputScheme : InputScheme
+// An input scheme corresponding to a curl-downloadable resource.
+struct CurlInputScheme : InputScheme
{
- std::optional<Input> inputFromURL(const ParsedURL & url) override
+ virtual const std::string inputType() const = 0;
+ const std::set<std::string> transportUrlSchemes = {"file", "http", "https"};
+
+ const bool hasTarballExtension(std::string_view path) const
{
- if (url.scheme != "file" && url.scheme != "http" && url.scheme != "https") return {};
+ return hasSuffix(path, ".zip") || hasSuffix(path, ".tar")
+ || hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz")
+ || hasSuffix(path, ".tar.xz") || hasSuffix(path, ".tar.bz2")
+ || hasSuffix(path, ".tar.zst");
+ }
- if (!hasSuffix(url.path, ".zip")
- && !hasSuffix(url.path, ".tar")
- && !hasSuffix(url.path, ".tgz")
- && !hasSuffix(url.path, ".tar.gz")
- && !hasSuffix(url.path, ".tar.xz")
- && !hasSuffix(url.path, ".tar.bz2")
- && !hasSuffix(url.path, ".tar.zst"))
- return {};
+ virtual bool isValidURL(const ParsedURL & url) const = 0;
+
+ std::optional<Input> inputFromURL(const ParsedURL & url) override
+ {
+ if (!isValidURL(url))
+ return std::nullopt;
Input input;
- input.attrs.insert_or_assign("type", "tarball");
- input.attrs.insert_or_assign("url", url.to_string());
+
+ auto urlWithoutApplicationScheme = url;
+ urlWithoutApplicationScheme.scheme = parseUrlScheme(url.scheme).transport;
+
+ input.attrs.insert_or_assign("type", inputType());
+ input.attrs.insert_or_assign("url", urlWithoutApplicationScheme.to_string());
auto narHash = url.query.find("narHash");
if (narHash != url.query.end())
input.attrs.insert_or_assign("narHash", narHash->second);
@@ -194,14 +205,17 @@ struct TarballInputScheme : InputScheme
std::optional<Input> inputFromAttrs(const Attrs & attrs) override
{
- if (maybeGetStrAttr(attrs, "type") != "tarball") return {};
+ auto type = maybeGetStrAttr(attrs, "type");
+ if (type != inputType()) return {};
+ std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack"};
for (auto & [name, value] : attrs)
- if (name != "type" && name != "url" && /* name != "hash" && */ name != "narHash" && name != "name")
- throw Error("unsupported tarball input attribute '%s'", name);
+ if (!allowedNames.count(name))
+ throw Error("unsupported %s input attribute '%s'", *type, name);
Input input;
input.attrs = attrs;
+
//input.locked = (bool) maybeGetStrAttr(input.attrs, "hash");
return input;
}
@@ -209,14 +223,9 @@ struct TarballInputScheme : InputScheme
ParsedURL toURL(const Input & input) override
{
auto url = parseURL(getStrAttr(input.attrs, "url"));
- // NAR hashes are preferred over file hashes since tar/zip files
- // don't have a canonical representation.
+ // NAR hashes are preferred over file hashes since tar/zip files // don't have a canonical representation.
if (auto narHash = input.getNarHash())
url.query.insert_or_assign("narHash", narHash->to_string(SRI, true));
- /*
- else if (auto hash = maybeGetStrAttr(input.attrs, "hash"))
- url.query.insert_or_assign("hash", Hash(*hash).to_string(SRI, true));
- */
return url;
}
@@ -225,6 +234,42 @@ struct TarballInputScheme : InputScheme
return true;
}
+};
+
+struct FileInputScheme : CurlInputScheme
+{
+ const std::string inputType() const override { return "file"; }
+
+ bool isValidURL(const ParsedURL & url) const override
+ {
+ auto parsedUrlScheme = parseUrlScheme(url.scheme);
+ return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
+ && (parsedUrlScheme.application
+ ? parsedUrlScheme.application.value() == inputType()
+ : !hasTarballExtension(url.path));
+ }
+
+ std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
+ {
+ auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName(), false);
+ return {std::move(file.storePath), input};
+ }
+};
+
+struct TarballInputScheme : CurlInputScheme
+{
+ const std::string inputType() const override { return "tarball"; }
+
+ bool isValidURL(const ParsedURL & url) const override
+ {
+ auto parsedUrlScheme = parseUrlScheme(url.scheme);
+
+ return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
+ && (parsedUrlScheme.application
+ ? parsedUrlScheme.application.value() == inputType()
+ : hasTarballExtension(url.path));
+ }
+
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
{
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
@@ -233,5 +278,6 @@ struct TarballInputScheme : InputScheme
};
static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique<TarballInputScheme>()); });
+static auto rFileInputScheme = OnStartup([] { registerInputScheme(std::make_unique<FileInputScheme>()); });
}
diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/build/hook-instance.cc
index 0f6f580be..1f19ddccc 100644
--- a/src/libstore/build/hook-instance.cc
+++ b/src/libstore/build/hook-instance.cc
@@ -7,6 +7,22 @@ HookInstance::HookInstance()
{
debug("starting build hook '%s'", settings.buildHook);
+ auto buildHookArgs = tokenizeString<std::list<std::string>>(settings.buildHook.get());
+
+ if (buildHookArgs.empty())
+ throw Error("'build-hook' setting is empty");
+
+ auto buildHook = buildHookArgs.front();
+ buildHookArgs.pop_front();
+
+ Strings args;
+
+ for (auto & arg : buildHookArgs)
+ args.push_back(arg);
+
+ args.push_back(std::string(baseNameOf(settings.buildHook.get())));
+ args.push_back(std::to_string(verbosity));
+
/* Create a pipe to get the output of the child. */
fromHook.create();
@@ -36,14 +52,9 @@ HookInstance::HookInstance()
if (dup2(builderOut.readSide.get(), 5) == -1)
throw SysError("dupping builder's stdout/stderr");
- Strings args = {
- std::string(baseNameOf(settings.buildHook.get())),
- std::to_string(verbosity),
- };
-
- execv(settings.buildHook.get().c_str(), stringsToCharPtrs(args).data());
+ execv(buildHook.c_str(), stringsToCharPtrs(args).data());
- throw SysError("executing '%s'", settings.buildHook);
+ throw SysError("executing '%s'", buildHook);
});
pid.setSeparatePG(true);
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 3ac9c20f9..d1ec91ed5 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -1717,7 +1717,19 @@ void LocalDerivationGoal::runChild()
for (auto & i : dirsInChroot) {
if (i.second.source == "/proc") continue; // backwards compatibility
- doBind(i.second.source, chrootRootDir + i.first, i.second.optional);
+
+ #if HAVE_EMBEDDED_SANDBOX_SHELL
+ if (i.second.source == "__embedded_sandbox_shell__") {
+ static unsigned char sh[] = {
+ #include "embedded-sandbox-shell.gen.hh"
+ };
+ auto dst = chrootRootDir + i.first;
+ createDirs(dirOf(dst));
+ writeFile(dst, std::string_view((const char *) sh, sizeof(sh)));
+ chmod_(dst, 0555);
+ } else
+ #endif
+ doBind(i.second.source, chrootRootDir + i.first, i.second.optional);
}
/* Bind a new instance of procfs on /proc. */
diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc
index ca5218627..2af105b4d 100644
--- a/src/libstore/build/substitution-goal.cc
+++ b/src/libstore/build/substitution-goal.cc
@@ -154,7 +154,7 @@ void PathSubstitutionGoal::tryNext()
only after we've downloaded the path. */
if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info))
{
- warn("the substitute for '%s' from '%s' is not signed by any of the keys in 'trusted-public-keys'",
+ warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'",
worker.store.printStorePath(storePath), sub->getUri());
tryNext();
return;
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index f65fb1b2e..d58ed78b1 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -135,6 +135,7 @@ void LocalStore::addTempRoot(const StorePath & path)
state->fdRootsSocket.close();
goto restart;
}
+ throw;
}
}
@@ -153,6 +154,7 @@ void LocalStore::addTempRoot(const StorePath & path)
state->fdRootsSocket.close();
goto restart;
}
+ throw;
} catch (EndOfFile & e) {
debug("GC socket disconnected");
state->fdRootsSocket.close();
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index cc009a026..0f2ca4b15 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -36,7 +36,6 @@ Settings::Settings()
, nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
, nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR)))
, nixUserConfFiles(getUserConfigFiles())
- , nixLibexecDir(canonPath(getEnv("NIX_LIBEXEC_DIR").value_or(NIX_LIBEXEC_DIR)))
, nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR)))
, nixManDir(canonPath(NIX_MAN_DIR))
, nixDaemonSocketFile(canonPath(getEnv("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
@@ -67,12 +66,13 @@ Settings::Settings()
sandboxPaths = tokenizeString<StringSet>("/bin/sh=" SANDBOX_SHELL);
#endif
-
-/* chroot-like behavior from Apple's sandbox */
+ /* chroot-like behavior from Apple's sandbox */
#if __APPLE__
sandboxPaths = tokenizeString<StringSet>("/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh /bin/bash /private/tmp /private/var/tmp /usr/lib");
allowedImpureHostPrefixes = tokenizeString<StringSet>("/System/Library /usr/lib /dev /bin/sh");
#endif
+
+ buildHook = getSelfExe().value_or("nix") + " __build-remote";
}
void loadConfFile()
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index feb6899cd..d7f351166 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -79,9 +79,6 @@ public:
/* A list of user configuration files to load. */
std::vector<Path> nixUserConfFiles;
- /* The directory where internal helper programs are stored. */
- Path nixLibexecDir;
-
/* The directory where the main programs are stored. */
Path nixBinDir;
@@ -195,7 +192,7 @@ public:
)",
{"build-timeout"}};
- PathSetting buildHook{this, true, nixLibexecDir + "/nix/build-remote", "build-hook",
+ PathSetting buildHook{this, true, "", "build-hook",
"The path of the helper program that executes builds to remote machines."};
Setting<std::string> builders{
@@ -802,7 +799,7 @@ public:
)"};
Setting<StringSet> ignoredAcls{
- this, {"security.selinux", "system.nfs4_acl"}, "ignored-acls",
+ this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls",
R"(
A list of ACLs that should be ignored, normally Nix attempts to
remove all ACLs from files and directories in the Nix store, but
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
index 3cb5efdbf..73bcd6e81 100644
--- a/src/libstore/http-binary-cache-store.cc
+++ b/src/libstore/http-binary-cache-store.cc
@@ -161,7 +161,12 @@ protected:
void getFile(const std::string & path,
Callback<std::optional<std::string>> callback) noexcept override
{
- checkEnabled();
+ try {
+ checkEnabled();
+ } catch (...) {
+ callback.rethrow();
+ return;
+ }
auto request(makeRequest(path));
diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc
index f754770f9..ba4416f6d 100644
--- a/src/libstore/local-binary-cache-store.cc
+++ b/src/libstore/local-binary-cache-store.cc
@@ -69,6 +69,7 @@ protected:
} catch (SysError & e) {
if (e.errNo == ENOENT)
throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache", path);
+ throw;
}
}
@@ -107,7 +108,7 @@ bool LocalBinaryCacheStore::fileExists(const std::string & path)
std::set<std::string> LocalBinaryCacheStore::uriSchemes()
{
- if (getEnv("_NIX_FORCE_HTTP_BINARY_CACHE_STORE") == "1")
+ if (getEnv("_NIX_FORCE_HTTP") == "1")
return {};
else
return {"file"};
diff --git a/src/libstore/local.mk b/src/libstore/local.mk
index b992bcbc0..1d26ac918 100644
--- a/src/libstore/local.mk
+++ b/src/libstore/local.mk
@@ -39,14 +39,23 @@ libstore_CXXFLAGS += \
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
- -DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
-DNIX_BIN_DIR=\"$(bindir)\" \
-DNIX_MAN_DIR=\"$(mandir)\" \
-DLSOF=\"$(lsof)\"
+ifeq ($(embedded_sandbox_shell),yes)
+libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\"
+
+$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh
+
+$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
+ $(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp
+ @mv $@.tmp $@
+else
ifneq ($(sandbox_shell),)
libstore_CXXFLAGS += -DSANDBOX_SHELL="\"$(sandbox_shell)\""
endif
+endif
$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc
index f1356fdca..fa718f55d 100644
--- a/src/libstore/lock.cc
+++ b/src/libstore/lock.cc
@@ -67,13 +67,26 @@ bool UserLock::findFreeUser() {
#if __linux__
/* Get the list of supplementary groups of this build user. This
is usually either empty or contains a group such as "kvm". */
- supplementaryGIDs.resize(10);
- int ngroups = supplementaryGIDs.size();
- int err = getgrouplist(pw->pw_name, pw->pw_gid,
- supplementaryGIDs.data(), &ngroups);
+ int ngroups = 32; // arbitrary initial guess
+ supplementaryGIDs.resize(ngroups);
+
+ int err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(),
+ &ngroups);
+
+ // Our initial size of 32 wasn't sufficient, the correct size has
+ // been stored in ngroups, so we try again.
+ if (err == -1) {
+ supplementaryGIDs.resize(ngroups);
+ err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(),
+ &ngroups);
+ }
+
+ // If it failed once more, then something must be broken.
if (err == -1)
- throw Error("failed to get list of supplementary groups for '%1%'", pw->pw_name);
+ throw Error("failed to get list of supplementary groups for '%1%'",
+ pw->pw_name);
+ // Finally, trim back the GID list to its real size
supplementaryGIDs.resize(ngroups);
#endif
diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc
index 9dd81ddfb..f4ea739b0 100644
--- a/src/libstore/nar-info-disk-cache.cc
+++ b/src/libstore/nar-info-disk-cache.cc
@@ -62,6 +62,9 @@ public:
/* How often to purge expired entries from the cache. */
const int purgeInterval = 24 * 3600;
+ /* How long to cache binary cache info (i.e. /nix-cache-info) */
+ const int cacheInfoTtl = 7 * 24 * 3600;
+
struct Cache
{
int id;
@@ -98,7 +101,7 @@ public:
"insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)");
state->queryCache.create(state->db,
- "select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?");
+ "select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
state->insertNAR.create(state->db,
"insert or replace into NARs(cache, hashPart, namePart, url, compression, fileHash, fileSize, narHash, "
@@ -183,7 +186,7 @@ public:
auto i = state->caches.find(uri);
if (i == state->caches.end()) {
- auto queryCache(state->queryCache.use()(uri));
+ auto queryCache(state->queryCache.use()(uri)(time(0) - cacheInfoTtl));
if (!queryCache.next())
return std::nullopt;
state->caches.emplace(uri,
diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc
index 2d75e7a82..071d8355e 100644
--- a/src/libstore/nar-info.cc
+++ b/src/libstore/nar-info.cc
@@ -69,8 +69,6 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
if (value != "unknown-deriver")
deriver = StorePath(value);
}
- else if (name == "System")
- system = value;
else if (name == "Sig")
sigs.insert(value);
else if (name == "CA") {
@@ -106,9 +104,6 @@ std::string NarInfo::to_string(const Store & store) const
if (deriver)
res += "Deriver: " + std::string(deriver->to_string()) + "\n";
- if (!system.empty())
- res += "System: " + system + "\n";
-
for (auto sig : sigs)
res += "Sig: " + sig + "\n";
diff --git a/src/libstore/nar-info.hh b/src/libstore/nar-info.hh
index 39ced76e5..01683ec73 100644
--- a/src/libstore/nar-info.hh
+++ b/src/libstore/nar-info.hh
@@ -14,7 +14,6 @@ struct NarInfo : ValidPathInfo
std::string compression;
std::optional<Hash> fileHash;
uint64_t fileSize = 0;
- std::string system;
NarInfo() = delete;
NarInfo(StorePath && path, Hash narHash) : ValidPathInfo(std::move(path), narHash) { }
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 14aeba75c..bc36aef5d 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -718,36 +718,34 @@ void RemoteStore::registerDrvOutput(const Realisation & info)
void RemoteStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
{
- auto conn(getConnection());
+ try {
+ auto conn(getConnection());
- if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 27) {
- warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4");
- try {
- callback(nullptr);
- } catch (...) { return callback.rethrow(); }
- }
+ if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 27) {
+ warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4");
+ return callback(nullptr);
+ }
- conn->to << wopQueryRealisation;
- conn->to << id.to_string();
- conn.processStderr();
+ conn->to << wopQueryRealisation;
+ conn->to << id.to_string();
+ conn.processStderr();
- auto real = [&]() -> std::shared_ptr<const Realisation> {
- if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) {
- auto outPaths = worker_proto::read(
- *this, conn->from, Phantom<std::set<StorePath>> {});
- if (outPaths.empty())
- return nullptr;
- return std::make_shared<const Realisation>(Realisation { .id = id, .outPath = *outPaths.begin() });
- } else {
- auto realisations = worker_proto::read(
- *this, conn->from, Phantom<std::set<Realisation>> {});
- if (realisations.empty())
- return nullptr;
- return std::make_shared<const Realisation>(*realisations.begin());
- }
- }();
+ auto real = [&]() -> std::shared_ptr<const Realisation> {
+ if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) {
+ auto outPaths = worker_proto::read(
+ *this, conn->from, Phantom<std::set<StorePath>> {});
+ if (outPaths.empty())
+ return nullptr;
+ return std::make_shared<const Realisation>(Realisation { .id = id, .outPath = *outPaths.begin() });
+ } else {
+ auto realisations = worker_proto::read(
+ *this, conn->from, Phantom<std::set<Realisation>> {});
+ if (realisations.empty())
+ return nullptr;
+ return std::make_shared<const Realisation>(*realisations.begin());
+ }
+ }();
- try {
callback(std::shared_ptr<const Realisation>(real));
} catch (...) { return callback.rethrow(); }
}
diff --git a/src/libstore/schema.sql b/src/libstore/schema.sql
index 09c71a2b8..d65e5335e 100644
--- a/src/libstore/schema.sql
+++ b/src/libstore/schema.sql
@@ -1,7 +1,7 @@
create table if not exists ValidPaths (
id integer primary key autoincrement not null,
path text unique not null,
- hash text not null,
+ hash text not null, -- base16 representation
registrationTime integer not null,
deriver text,
narSize integer,
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 8861274a2..05353bce2 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -1302,7 +1302,8 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_
return {uri, params};
}
-static bool isNonUriPath(const std::string & spec) {
+static bool isNonUriPath(const std::string & spec)
+{
return
// is not a URL
spec.find("://") == std::string::npos
@@ -1319,6 +1320,26 @@ std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Para
return std::make_shared<LocalStore>(params);
else if (pathExists(settings.nixDaemonSocketFile))
return std::make_shared<UDSRemoteStore>(params);
+ #if __linux__
+ else if (!pathExists(stateDir) && params.empty() && getuid() != 0) {
+ /* If /nix doesn't exist, there is no daemon socket, and
+ we're not root, then automatically set up a chroot
+ store in ~/.local/share/nix/root. */
+ auto chrootStore = getDataDir() + "/nix/root";
+ if (!pathExists(chrootStore)) {
+ try {
+ createDirs(chrootStore);
+ } catch (Error & e) {
+ return std::make_shared<LocalStore>(params);
+ }
+ warn("'/nix' does not exist, so Nix will use '%s' as a chroot store", chrootStore);
+ } else
+ debug("'/nix' does not exist, so Nix will use '%s' as a chroot store", chrootStore);
+ Store::Params params2;
+ params2["root"] = chrootStore;
+ return std::make_shared<LocalStore>(params2);
+ }
+ #endif
else
return std::make_shared<LocalStore>(params);
} else if (uri == "daemon") {
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index fdd036f9a..07c017719 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -25,6 +25,8 @@ public:
/* Return a short one-line description of the command. */
virtual std::string description() { return ""; }
+ virtual bool forceImpureByDefault() { return false; }
+
/* Return documentation about this command, in Markdown format. */
virtual std::string doc() { return ""; }
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
index f4706e3ed..a53e9802e 100644
--- a/src/libutil/error.hh
+++ b/src/libutil/error.hh
@@ -98,6 +98,15 @@ struct ErrPos {
}
};
+std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos);
+
+void printCodeLines(std::ostream & out,
+ const std::string & prefix,
+ const ErrPos & errPos,
+ const LinesOfCode & loc);
+
+void printAtPos(const ErrPos & pos, std::ostream & out);
+
struct Trace {
std::optional<ErrPos> pos;
hintformat hint;
diff --git a/src/libutil/hilite.cc b/src/libutil/hilite.cc
index a5991ca39..e5088230d 100644
--- a/src/libutil/hilite.cc
+++ b/src/libutil/hilite.cc
@@ -8,9 +8,9 @@ std::string hiliteMatches(
std::string_view prefix,
std::string_view postfix)
{
- // Avoid copy on zero matches
+ // Avoid extra work on zero matches
if (matches.size() == 0)
- return (std::string) s;
+ return std::string(s);
std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {
return a.position() < b.position();
diff --git a/src/libutil/json.cc b/src/libutil/json.cc
index 3a981376f..b0a5d7e75 100644
--- a/src/libutil/json.cc
+++ b/src/libutil/json.cc
@@ -1,6 +1,7 @@
#include "json.hh"
#include <iomanip>
+#include <cstdint>
#include <cstring>
namespace nix {
diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh
index f9578afc7..bf26321db 100644
--- a/src/libutil/ref.hh
+++ b/src/libutil/ref.hh
@@ -7,7 +7,7 @@
namespace nix {
/* A simple non-nullable reference-counted pointer. Actually a wrapper
- around std::shared_ptr that prevents non-null constructions. */
+ around std::shared_ptr that prevents null constructions. */
template<typename T>
class ref
{
diff --git a/src/libutil/url.cc b/src/libutil/url.cc
index f6232d255..5b7abeb49 100644
--- a/src/libutil/url.cc
+++ b/src/libutil/url.cc
@@ -1,6 +1,7 @@
#include "url.hh"
#include "url-parts.hh"
#include "util.hh"
+#include "split.hh"
namespace nix {
@@ -136,4 +137,21 @@ bool ParsedURL::operator ==(const ParsedURL & other) const
&& fragment == other.fragment;
}
+/**
+ * Parse a URL scheme of the form '(applicationScheme\+)?transportScheme'
+ * into a tuple '(applicationScheme, transportScheme)'
+ *
+ * > parseUrlScheme("http") == ParsedUrlScheme{ {}, "http"}
+ * > parseUrlScheme("tarball+http") == ParsedUrlScheme{ {"tarball"}, "http"}
+ */
+ParsedUrlScheme parseUrlScheme(std::string_view scheme)
+{
+ auto application = splitPrefixTo(scheme, '+');
+ auto transport = scheme;
+ return ParsedUrlScheme {
+ .application = application,
+ .transport = transport,
+ };
+}
+
}
diff --git a/src/libutil/url.hh b/src/libutil/url.hh
index 6e77142e3..2a9fb34c1 100644
--- a/src/libutil/url.hh
+++ b/src/libutil/url.hh
@@ -27,4 +27,19 @@ std::map<std::string, std::string> decodeQuery(const std::string & query);
ParsedURL parseURL(const std::string & url);
+/*
+ * Although that’s not really standardized anywhere, an number of tools
+ * use a scheme of the form 'x+y' in urls, where y is the “transport layer”
+ * scheme, and x is the “application layer” scheme.
+ *
+ * For example git uses `git+https` to designate remotes using a Git
+ * protocol over http.
+ */
+struct ParsedUrlScheme {
+ std::optional<std::string_view> application;
+ std::string_view transport;
+};
+
+ParsedUrlScheme parseUrlScheme(std::string_view scheme);
+
}
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index d4d78329d..28df30fef 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -29,6 +29,7 @@
#ifdef __APPLE__
#include <sys/syscall.h>
+#include <mach-o/dyld.h>
#endif
#ifdef __linux__
@@ -574,6 +575,20 @@ Path getHome()
static Path homeDir = []()
{
auto homeDir = getEnv("HOME");
+ if (homeDir) {
+ // Only use $HOME if doesn't exist or is owned by the current user.
+ struct stat st;
+ int result = stat(homeDir->c_str(), &st);
+ if (result != 0) {
+ if (errno != ENOENT) {
+ warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno);
+ homeDir.reset();
+ }
+ } else if (st.st_uid != geteuid()) {
+ warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file", *homeDir);
+ homeDir.reset();
+ }
+ }
if (!homeDir) {
std::vector<char> buf(16384);
struct passwd pwbuf;
@@ -619,6 +634,27 @@ Path getDataDir()
}
+std::optional<Path> getSelfExe()
+{
+ static auto cached = []() -> std::optional<Path>
+ {
+ #if __linux__
+ return readLink("/proc/self/exe");
+ #elif __APPLE__
+ char buf[1024];
+ uint32_t size = sizeof(buf);
+ if (_NSGetExecutablePath(buf, &size) == 0)
+ return buf;
+ else
+ return std::nullopt;
+ #else
+ return std::nullopt;
+ #endif
+ }();
+ return cached;
+}
+
+
Paths createDirs(const Path & path)
{
Paths created;
@@ -1818,7 +1854,7 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
if (chmod(path.c_str(), mode) == -1)
throw SysError("changing permissions on '%1%'", path);
- if (listen(fdSocket.get(), 5) == -1)
+ if (listen(fdSocket.get(), 100) == -1)
throw SysError("cannot listen on socket '%1%'", path);
return fdSocket;
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 09ccfa591..d3ed15b0b 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -149,10 +149,14 @@ std::vector<Path> getConfigDirs();
/* Return $XDG_DATA_HOME or $HOME/.local/share. */
Path getDataDir();
+/* Return the path of the current executable. */
+std::optional<Path> getSelfExe();
+
/* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */
Paths createDirs(const Path & path);
-inline Paths createDirs(PathView path) {
+inline Paths createDirs(PathView path)
+{
return createDirs(Path(path));
}
@@ -700,4 +704,19 @@ template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
std::string showBytes(uint64_t bytes);
+/* Provide an addition operator between strings and string_views
+ inexplicably omitted from the standard library. */
+inline std::string operator + (const std::string & s1, std::string_view s2)
+{
+ auto s = s1;
+ s.append(s2);
+ return s;
+}
+
+inline std::string operator + (std::string && s, std::string_view s2)
+{
+ s.append(s2);
+ return std::move(s);
+}
+
}
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 426f23905..519855ea3 100644
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -543,6 +543,8 @@ static void main_nix_build(int argc, char * * argv)
restoreProcessContext();
+ logger->stop();
+
execvp(shell->c_str(), argPtrs.data());
throw SysError("executing shell '%s'", *shell);
@@ -601,6 +603,8 @@ static void main_nix_build(int argc, char * * argv)
outPaths.push_back(outputPath);
}
+ logger->stop();
+
for (auto & path : outPaths)
std::cout << store->printStorePath(path) << '\n';
}
diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc
index af6f1c88c..e413faffe 100644
--- a/src/nix-collect-garbage/nix-collect-garbage.cc
+++ b/src/nix-collect-garbage/nix-collect-garbage.cc
@@ -37,6 +37,7 @@ void removeOldGenerations(std::string dir)
link = readLink(path);
} catch (SysError & e) {
if (e.errNo == ENOENT) continue;
+ throw;
}
if (link.find("link") != std::string::npos) {
printInfo(format("removing old generations of profile %1%") % path);
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index c412bb814..a69d3700d 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -1485,7 +1485,7 @@ static int main_nix_env(int argc, char * * argv)
if (globals.profile == "")
globals.profile = getDefaultProfile();
- op(globals, opFlags, opArgs);
+ op(globals, std::move(opFlags), std::move(opArgs));
globals.state->printStats();
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index 9163eefd0..b453ea1ca 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -1093,7 +1093,7 @@ static int main_nix_store(int argc, char * * argv)
if (op != opDump && op != opRestore) /* !!! hack */
store = openStore();
- op(opFlags, opArgs);
+ op(std::move(opFlags), std::move(opArgs));
return 0;
}
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index 895a7de76..c66b24e5c 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -509,7 +509,7 @@ struct CmdFlakeCheck : FlakeCommand
std::string_view replacement =
name == "defaultPackage" ? "packages.<system>.default" :
- name == "defaultApps" ? "apps.<system>.default" :
+ name == "defaultApp" ? "apps.<system>.default" :
name == "defaultTemplate" ? "templates.default" :
name == "defaultBundler" ? "bundlers.<system>.default" :
name == "overlay" ? "overlays.default" :
@@ -1090,9 +1090,13 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
else if (attrPath.size() > 0 && attrPathS[0] == "legacyPackages") {
if (attrPath.size() == 1)
recurse();
- else if (!showLegacy)
- logger->warn(fmt("%s: " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix));
- else {
+ else if (!showLegacy){
+ if (!json)
+ logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix));
+ else {
+ logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS)));
+ }
+ } else {
if (visitor.isDerivation())
showDerivation();
else if (attrPath.size() <= 2)
diff --git a/src/nix/flake.md b/src/nix/flake.md
index aa3f9f303..a1ab43281 100644
--- a/src/nix/flake.md
+++ b/src/nix/flake.md
@@ -181,9 +181,17 @@ Currently the `type` attribute can be one of the following:
* `tarball`: Tarballs. The location of the tarball is specified by the
attribute `url`.
- In URL form, the schema must be `http://`, `https://` or `file://`
- URLs and the extension must be `.zip`, `.tar`, `.tgz`, `.tar.gz`,
- `.tar.xz`, `.tar.bz2` or `.tar.zst`.
+ In URL form, the schema must be `tarball+http://`, `tarball+https://` or `tarball+file://`.
+ If the extension corresponds to a known archive format (`.zip`, `.tar`,
+ `.tgz`, `.tar.gz`, `.tar.xz`, `.tar.bz2` or `.tar.zst`), then the `tarball+`
+ can be dropped.
+
+* `file`: Plain files or directory tarballs, either over http(s) or from the local
+ disk.
+
+ In URL form, the schema must be `file+http://`, `file+https://` or `file+file://`.
+ If the extension doesn’t correspond to a known archive format (as defined by the
+ `tarball` fetcher), then the `file+` prefix can be dropped.
* `github`: A more efficient way to fetch repositories from
GitHub. The following attributes are required:
diff --git a/src/nix/key-generate-secret.md b/src/nix/key-generate-secret.md
index 4938f637c..609b1abcc 100644
--- a/src/nix/key-generate-secret.md
+++ b/src/nix/key-generate-secret.md
@@ -30,7 +30,7 @@ convert-secret-to-public` to get the corresponding public key for
verifying signed store paths.
The mandatory argument `--key-name` specifies a key name (such as
-`cache.example.org-1). It is used to look up keys on the client when
+`cache.example.org-1`). It is used to look up keys on the client when
it verifies signatures. It can be anything, but it’s suggested to use
the host name of your cache (e.g. `cache.example.org`) with a suffix
denoting the number of the key (to be incremented every time you need
diff --git a/src/nix/main.cc b/src/nix/main.cc
index dadb54306..17c92ebc6 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -266,6 +266,11 @@ void mainWrapped(int argc, char * * argv)
programPath = argv[0];
auto programName = std::string(baseNameOf(programPath));
+ if (argc > 0 && std::string_view(argv[0]) == "__build-remote") {
+ programName = "build-remote";
+ argv++; argc--;
+ }
+
{
auto legacy = (*RegisterLegacyCommand::commands)[programName];
if (legacy) return legacy(argc, argv);
@@ -380,6 +385,9 @@ void mainWrapped(int argc, char * * argv)
settings.ttlPositiveNarInfoCache = 0;
}
+ if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) {
+ evalSettings.pureEval = false;
+ }
args.command->second->prepare();
args.command->second->run();
}
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index 1aae347df..3814e7d5a 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -263,7 +263,8 @@ builtPathsPerInstallable(
struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{
- std::optional<int> priority;
+ std::optional<int64_t> priority;
+
CmdProfileInstall() {
addFlag({
.longName = "priority",
diff --git a/src/nix/registry.md b/src/nix/registry.md
index d5c9ef442..bd3575d1b 100644
--- a/src/nix/registry.md
+++ b/src/nix/registry.md
@@ -29,7 +29,7 @@ highest precedence:
can be specified using the NixOS option `nix.registry`.
* The user registry `~/.config/nix/registry.json`. This registry can
- be modified by commands such as `nix flake pin`.
+ be modified by commands such as `nix registry pin`.
* Overrides specified on the command line using the option
`--override-flake`.
diff --git a/src/nix/run.cc b/src/nix/run.cc
index 25a8fa8d3..45d2dfd0d 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -47,7 +47,7 @@ void runProgramInStore(ref<Store> store,
Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
for (auto & arg : args) helperArgs.push_back(arg);
- execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
+ execv(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data());
throw SysError("could not execute chroot helper");
}
diff --git a/src/nix/search.cc b/src/nix/search.cc
index 87dc1c0de..bdd45cbed 100644
--- a/src/nix/search.cc
+++ b/src/nix/search.cc
@@ -18,16 +18,26 @@ using namespace nix;
std::string wrap(std::string prefix, std::string s)
{
- return prefix + s + ANSI_NORMAL;
+ return concatStrings(prefix, s, ANSI_NORMAL);
}
struct CmdSearch : InstallableCommand, MixJSON
{
std::vector<std::string> res;
+ std::vector<std::string> excludeRes;
CmdSearch()
{
expectArgs("regex", &res);
+ addFlag(Flag {
+ .longName = "exclude",
+ .shortName = 'e',
+ .description = "Hide packages whose attribute path, name or description contain *regex*.",
+ .labels = {"regex"},
+ .handler = {[this](std::string s) {
+ excludeRes.push_back(s);
+ }},
+ });
}
std::string description() override
@@ -62,11 +72,16 @@ struct CmdSearch : InstallableCommand, MixJSON
res.push_back("^");
std::vector<std::regex> regexes;
+ std::vector<std::regex> excludeRegexes;
regexes.reserve(res.size());
+ excludeRegexes.reserve(excludeRes.size());
for (auto & re : res)
regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase));
+ for (auto & re : excludeRes)
+ excludeRegexes.emplace_back(re, std::regex::extended | std::regex::icase);
+
auto state = getEvalState();
auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;
@@ -106,6 +121,14 @@ struct CmdSearch : InstallableCommand, MixJSON
std::vector<std::smatch> nameMatches;
bool found = false;
+ for (auto & regex : excludeRegexes) {
+ if (
+ std::regex_search(attrPath2, regex)
+ || std::regex_search(name.name, regex)
+ || std::regex_search(description, regex))
+ return;
+ }
+
for (auto & regex : regexes) {
found = false;
auto addAll = [&found](std::sregex_iterator it, std::vector<std::smatch> & vec) {
@@ -133,15 +156,15 @@ struct CmdSearch : InstallableCommand, MixJSON
jsonElem.attr("version", name.version);
jsonElem.attr("description", description);
} else {
- auto name2 = hiliteMatches(name.name, std::move(nameMatches), ANSI_GREEN, "\e[0;2m");
+ auto name2 = hiliteMatches(name.name, nameMatches, ANSI_GREEN, "\e[0;2m");
if (results > 1) logger->cout("");
logger->cout(
"* %s%s",
- wrap("\e[0;1m", hiliteMatches(attrPath2, std::move(attrPathMatches), ANSI_GREEN, "\e[0;1m")),
+ wrap("\e[0;1m", hiliteMatches(attrPath2, attrPathMatches, ANSI_GREEN, "\e[0;1m")),
name.version != "" ? " (" + name.version + ")" : "");
if (description != "")
logger->cout(
- " %s", hiliteMatches(description, std::move(descriptionMatches), ANSI_GREEN, ANSI_NORMAL));
+ " %s", hiliteMatches(description, descriptionMatches, ANSI_GREEN, ANSI_NORMAL));
}
}
}
diff --git a/src/nix/search.md b/src/nix/search.md
index d182788a6..5a5b5ae05 100644
--- a/src/nix/search.md
+++ b/src/nix/search.md
@@ -43,12 +43,23 @@ R""(
# nix search nixpkgs 'firefox|chromium'
```
-* Search for packages containing `git'`and either `frontend` or `gui`:
+* Search for packages containing `git` and either `frontend` or `gui`:
```console
# nix search nixpkgs git 'frontend|gui'
```
+* Search for packages containing `neovim` but hide ones containing either `gui` or `python`:
+
+ ```console
+ # nix search nixpkgs neovim -e 'python|gui'
+ ```
+ or
+
+ ```console
+ # nix search nixpkgs neovim -e 'python' -e 'gui'
+ ```
+
# Description
`nix search` searches *installable* (which must be evaluatable, e.g. a
diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc
index 17a5a77ee..2d2453395 100644
--- a/src/nix/upgrade-nix.cc
+++ b/src/nix/upgrade-nix.cc
@@ -34,7 +34,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
std::string description() override
{
- return "upgrade Nix to the latest stable version";
+ return "upgrade Nix to the stable version declared in Nixpkgs";
}
std::string doc() override
diff --git a/src/nix/upgrade-nix.md b/src/nix/upgrade-nix.md
index 4d27daad9..084c80ba2 100644
--- a/src/nix/upgrade-nix.md
+++ b/src/nix/upgrade-nix.md
@@ -2,7 +2,7 @@ R""(
# Examples
-* Upgrade Nix to the latest stable version:
+* Upgrade Nix to the stable version declared in Nixpkgs:
```console
# nix upgrade-nix
@@ -16,8 +16,11 @@ R""(
# Description
-This command upgrades Nix to the latest version. By default, it
-locates the directory containing the `nix` binary in the `$PATH`
+This command upgrades Nix to the stable version declared in Nixpkgs.
+This stable version is defined in [nix-fallback-paths.nix](https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/tools/nix-fallback-paths.nix)
+and updated manually. It may not always be the latest tagged release.
+
+By default, it locates the directory containing the `nix` binary in the `$PATH`
environment variable. If that directory is a Nix profile, it will
upgrade the `nix` package in that profile to the latest stable binary
release.
diff --git a/tests/ca/content-addressed.nix b/tests/ca/content-addressed.nix
index 31c144ae0..81bc4bf5c 100644
--- a/tests/ca/content-addressed.nix
+++ b/tests/ca/content-addressed.nix
@@ -75,7 +75,7 @@ rec {
buildCommand = ''
mkdir -p $out/bin
echo ${rootCA} # Just to make it depend on it
- echo "" > $out/bin/${name}
+ echo "#! ${shell}" > $out/bin/${name}
chmod +x $out/bin/${name}
'';
};
diff --git a/tests/common.sh.in b/tests/common.sh.in
index 6cb579e0d..5efd025ee 100644
--- a/tests/common.sh.in
+++ b/tests/common.sh.in
@@ -50,6 +50,8 @@ export busybox="@sandbox_shell@"
export version=@PACKAGE_VERSION@
export system=@system@
+export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@
+
export IMPURE_VAR1=foo
export IMPURE_VAR2=bar
diff --git a/tests/fetchTree-file.sh b/tests/fetchTree-file.sh
new file mode 100644
index 000000000..f0c530466
--- /dev/null
+++ b/tests/fetchTree-file.sh
@@ -0,0 +1,105 @@
+source common.sh
+
+clearStore
+
+cd "$TEST_ROOT"
+
+test_fetch_file () {
+ echo foo > test_input
+
+ input_hash="$(nix hash path test_input)"
+
+ nix eval --impure --file - <<EOF
+ let
+ tree = builtins.fetchTree { type = "file"; url = "file://$PWD/test_input"; };
+ in
+ assert (tree.narHash == "$input_hash");
+ tree
+EOF
+}
+
+# Make sure that `http(s)` and `file` flake inputs are properly extracted when
+# they should be, and treated as opaque files when they should be
+test_file_flake_input () {
+ rm -fr "$TEST_ROOT/testFlake";
+ mkdir "$TEST_ROOT/testFlake";
+ pushd testFlake
+
+ mkdir inputs
+ echo foo > inputs/test_input_file
+ tar cfa test_input.tar.gz inputs
+ cp test_input.tar.gz test_input_no_ext
+ input_tarball_hash="$(nix hash path test_input.tar.gz)"
+ input_directory_hash="$(nix hash path inputs)"
+
+ cat <<EOF > flake.nix
+ {
+ inputs.no_ext_default_no_unpack = {
+ url = "file://$PWD/test_input_no_ext";
+ flake = false;
+ };
+ inputs.no_ext_explicit_unpack = {
+ url = "tarball+file://$PWD/test_input_no_ext";
+ flake = false;
+ };
+ inputs.tarball_default_unpack = {
+ url = "file://$PWD/test_input.tar.gz";
+ flake = false;
+ };
+ inputs.tarball_explicit_no_unpack = {
+ url = "file+file://$PWD/test_input.tar.gz";
+ flake = false;
+ };
+ outputs = { ... }: {};
+ }
+EOF
+
+ nix flake update
+ nix eval --file - <<EOF
+ with (builtins.fromJSON (builtins.readFile ./flake.lock));
+
+ # Url inputs whose extension doesn’t match a known archive format should
+ # not be unpacked by default
+ assert (nodes.no_ext_default_no_unpack.locked.type == "file");
+ assert (nodes.no_ext_default_no_unpack.locked.unpack or false == false);
+ assert (nodes.no_ext_default_no_unpack.locked.narHash == "$input_tarball_hash");
+
+ # For backwards compatibility, flake inputs that correspond to the
+ # old 'tarball' fetcher should still have their type set to 'tarball'
+ assert (nodes.tarball_default_unpack.locked.type == "tarball");
+ # Unless explicitely specified, the 'unpack' parameter shouldn’t appear here
+ # because that would break older Nix versions
+ assert (!nodes.tarball_default_unpack.locked ? unpack);
+ assert (nodes.tarball_default_unpack.locked.narHash == "$input_directory_hash");
+
+ # Explicitely passing the unpack parameter should enforce the desired behavior
+ assert (nodes.no_ext_explicit_unpack.locked.narHash == nodes.tarball_default_unpack.locked.narHash);
+ assert (nodes.tarball_explicit_no_unpack.locked.narHash == nodes.no_ext_default_no_unpack.locked.narHash);
+ true
+EOF
+ popd
+
+ [[ -z "${NIX_DAEMON_PACKAGE}" ]] && return 0
+
+ # Ensure that a lockfile generated by the current Nix for tarball inputs
+ # can still be read by an older Nix
+
+ cat <<EOF > flake.nix
+ {
+ inputs.tarball = {
+ url = "file://$PWD/test_input.tar.gz";
+ flake = false;
+ };
+ outputs = { self, tarball }: {
+ foo = builtins.readFile "${tarball}/test_input_file";
+ };
+ }
+ nix flake update
+
+ clearStore
+ "$NIX_DAEMON_PACKAGE/bin/nix" eval .#foo
+EOF
+}
+
+test_fetch_file
+test_file_flake_input
diff --git a/tests/flakes.sh b/tests/flakes.sh
index 9a1f0ab6a..36bffcf3b 100644
--- a/tests/flakes.sh
+++ b/tests/flakes.sh
@@ -32,7 +32,7 @@ for repo in $flake1Dir $flake2Dir $flake3Dir $flake7Dir $templatesDir $nonFlakeD
rm -rf $repo $repo.tmp
mkdir -p $repo
- # Give one repo a non-master initial branch.
+ # Give one repo a non-main initial branch.
extraArgs=
if [[ $repo == $flake2Dir ]]; then
extraArgs="--initial-branch=main"
@@ -173,11 +173,11 @@ nix build -o $TEST_ROOT/result $flake2Dir#bar --no-write-lock-file
nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes'
nix build -o $TEST_ROOT/result $flake2Dir#bar --commit-lock-file
[[ -e $flake2Dir/flake.lock ]]
-[[ -z $(git -C $flake2Dir diff master) ]]
+[[ -z $(git -C $flake2Dir diff main || echo failed) ]]
# Rerunning the build should not change the lockfile.
nix build -o $TEST_ROOT/result $flake2Dir#bar
-[[ -z $(git -C $flake2Dir diff master) ]]
+[[ -z $(git -C $flake2Dir diff main || echo failed) ]]
# Building with a lockfile should not require a fetch of the registry.
nix build -o $TEST_ROOT/result --flake-registry file:///no-registry.json $flake2Dir#bar --refresh
@@ -186,7 +186,7 @@ nix build -o $TEST_ROOT/result --no-use-registries $flake2Dir#bar --refresh
# Updating the flake should not change the lockfile.
nix flake lock $flake2Dir
-[[ -z $(git -C $flake2Dir diff master) ]]
+[[ -z $(git -C $flake2Dir diff main || echo failed) ]]
# Now we should be able to build the flake in pure mode.
nix build -o $TEST_ROOT/result flake2#bar
@@ -221,7 +221,7 @@ nix build -o $TEST_ROOT/result $flake3Dir#"sth sth"
nix build -o $TEST_ROOT/result $flake3Dir#"sth%20sth"
# Check whether it saved the lockfile
-(! [[ -z $(git -C $flake3Dir diff master) ]])
+[[ -n $(git -C $flake3Dir diff master) ]]
git -C $flake3Dir add flake.lock
@@ -321,10 +321,10 @@ nix build -o $TEST_ROOT/result flake4#xyzzy
# Test 'nix flake update' and --override-flake.
nix flake lock $flake3Dir
-[[ -z $(git -C $flake3Dir diff master) ]]
+[[ -z $(git -C $flake3Dir diff master || echo failed) ]]
nix flake update $flake3Dir --override-flake flake2 nixpkgs
-[[ ! -z $(git -C $flake3Dir diff master) ]]
+[[ ! -z $(git -C $flake3Dir diff master || echo failed) ]]
# Make branch "removeXyzzy" where flake3 doesn't have xyzzy anymore
git -C $flake3Dir checkout -b removeXyzzy
diff --git a/tests/fmt.sh b/tests/fmt.sh
index bc05118ff..254681ca2 100644
--- a/tests/fmt.sh
+++ b/tests/fmt.sh
@@ -18,7 +18,12 @@ cat << EOF > flake.nix
with import ./config.nix;
mkDerivation {
name = "formatter";
- buildCommand = "mkdir -p \$out/bin; cp \${./fmt.simple.sh} \$out/bin/formatter";
+ buildCommand = ''
+ mkdir -p \$out/bin
+ echo "#! ${shell}" > \$out/bin/formatter
+ cat \${./fmt.simple.sh} >> \$out/bin/formatter
+ chmod +x \$out/bin/formatter
+ '';
};
};
}
diff --git a/tests/github-flakes.nix b/tests/github-flakes.nix
index 7ac397d81..fc481c7e3 100644
--- a/tests/github-flakes.nix
+++ b/tests/github-flakes.nix
@@ -7,7 +7,7 @@ with import (nixpkgs + "/nixos/lib/testing-python.nix") {
let
- # Generate a fake root CA and a fake github.com certificate.
+ # Generate a fake root CA and a fake api.github.com / channels.nixos.org certificate.
cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; }
''
mkdir -p $out
@@ -18,7 +18,7 @@ let
openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \
-subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=github.com" -out server.csr
- openssl x509 -req -extfile <(printf "subjectAltName=DNS:api.github.com,DNS:github.com,DNS:raw.githubusercontent.com") \
+ openssl x509 -req -extfile <(printf "subjectAltName=DNS:api.github.com,DNS:channels.nixos.org") \
-days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt
'';
@@ -67,7 +67,7 @@ makeTest (
name = "github-flakes";
nodes =
- { # Impersonate github.com and api.github.com.
+ {
github =
{ config, pkgs, ... }:
{ networking.firewall.allowedTCPPorts = [ 80 443 ];
@@ -77,12 +77,12 @@ makeTest (
services.httpd.extraConfig = ''
ErrorLog syslog:local6
'';
- services.httpd.virtualHosts."github.com" =
+ services.httpd.virtualHosts."channels.nixos.org" =
{ forceSSL = true;
sslServerKey = "${cert}/server.key";
sslServerCert = "${cert}/server.crt";
servedDirs =
- [ { urlPath = "/NixOS/flake-registry/raw/master";
+ [ { urlPath = "/";
dir = registry;
}
];
@@ -103,13 +103,13 @@ makeTest (
{ config, lib, pkgs, nodes, ... }:
{ virtualisation.writableStore = true;
virtualisation.diskSize = 2048;
- virtualisation.pathsInNixDB = [ pkgs.hello pkgs.fuse ];
+ virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ];
virtualisation.memorySize = 4096;
nix.binaryCaches = lib.mkForce [ ];
nix.extraOptions = "experimental-features = nix-command flakes";
environment.systemPackages = [ pkgs.jq ];
networking.hosts.${(builtins.head nodes.github.config.networking.interfaces.eth1.ipv4.addresses).address} =
- [ "github.com" "api.github.com" "raw.githubusercontent.com" ];
+ [ "channels.nixos.org" "api.github.com" ];
security.pki.certificateFiles = [ "${cert}/ca.crt" ];
};
};
@@ -123,7 +123,7 @@ makeTest (
github.wait_for_unit("httpd.service")
- client.succeed("curl -v https://github.com/ >&2")
+ client.succeed("curl -v https://api.github.com/ >&2")
client.succeed("nix registry list | grep nixpkgs")
rev = client.succeed("nix flake info nixpkgs --json | jq -r .revision")
diff --git a/tests/lang/parse-fail-eof-in-string.nix b/tests/lang/parse-fail-eof-in-string.nix
new file mode 100644
index 000000000..19775d2ec
--- /dev/null
+++ b/tests/lang/parse-fail-eof-in-string.nix
@@ -0,0 +1,3 @@
+# https://github.com/NixOS/nix/issues/6562
+# Note that this file must not end with a newline.
+a 1"$ \ No newline at end of file
diff --git a/tests/local.mk b/tests/local.mk
index e3c4ff4eb..ae15c70f9 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -23,6 +23,7 @@ nix_tests = \
fetchGit.sh \
fetchurl.sh \
fetchPath.sh \
+ fetchTree-file.sh \
simple.sh \
referrers.sh \
optimise-store.sh \
@@ -113,4 +114,8 @@ tests-environment = NIX_REMOTE= $(bash) -e
clean-files += $(d)/common.sh $(d)/config.nix $(d)/ca/config.nix
-test-deps += tests/common.sh tests/config.nix tests/ca/config.nix tests/plugins/libplugintest.$(SO_EXT)
+test-deps += tests/common.sh tests/config.nix tests/ca/config.nix
+
+ifeq ($(BUILD_SHARED_LIBS), 1)
+ test-deps += tests/plugins/libplugintest.$(SO_EXT)
+endif
diff --git a/tests/nix-copy-closure.nix b/tests/nix-copy-closure.nix
index 1b63a3fca..ba8b2cfc9 100644
--- a/tests/nix-copy-closure.nix
+++ b/tests/nix-copy-closure.nix
@@ -14,7 +14,7 @@ makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; pkgD = pk
{ client =
{ config, lib, pkgs, ... }:
{ virtualisation.writableStore = true;
- virtualisation.pathsInNixDB = [ pkgA pkgD.drvPath ];
+ virtualisation.additionalPaths = [ pkgA pkgD.drvPath ];
nix.binaryCaches = lib.mkForce [ ];
};
@@ -22,7 +22,7 @@ makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; pkgD = pk
{ config, pkgs, ... }:
{ services.openssh.enable = true;
virtualisation.writableStore = true;
- virtualisation.pathsInNixDB = [ pkgB pkgC ];
+ virtualisation.additionalPaths = [ pkgB pkgC ];
};
};
diff --git a/tests/nss-preload.nix b/tests/nss-preload.nix
index 2610d2b30..64b655ba2 100644
--- a/tests/nss-preload.nix
+++ b/tests/nss-preload.nix
@@ -5,6 +5,42 @@ with import (nixpkgs + "/nixos/lib/testing-python.nix") {
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
};
+let
+ nix-fetch = pkgs.writeText "fetch.nix" ''
+ derivation {
+ # This derivation is an copy from what is available over at
+ # nix.git:corepkgs/fetchurl.nix
+ builder = "builtin:fetchurl";
+
+ # We're going to fetch data from the http_dns instance created before
+ # we expect the content to be the same as the content available there.
+ # ```
+ # $ nix-hash --type sha256 --to-base32 $(echo "hello world" | sha256sum | cut -d " " -f 1)
+ # 0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59
+ # ```
+ outputHash = "0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59";
+ outputHashAlgo = "sha256";
+ outputHashMode = "flat";
+
+ name = "example.com";
+ url = "http://example.com";
+
+ unpack = false;
+ executable = false;
+
+ system = "builtin";
+
+ preferLocalBuild = true;
+
+ impureEnvVars = [
+ "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
+ ];
+
+ urls = [ "http://example.com" ];
+ }
+ '';
+in
+
makeTest (
rec {
@@ -68,40 +104,6 @@ rec {
};
};
- nix-fetch = pkgs.writeText "fetch.nix" ''
- derivation {
- # This derivation is an copy from what is available over at
- # nix.git:corepkgs/fetchurl.nix
- builder = "builtin:fetchurl";
-
- # We're going to fetch data from the http_dns instance created before
- # we expect the content to be the same as the content available there.
- # ```
- # $ nix-hash --type sha256 --to-base32 $(echo "hello world" | sha256sum | cut -d " " -f 1)
- # 0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59
- # ```
- outputHash = "0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59";
- outputHashAlgo = "sha256";
- outputHashMode = "flat";
-
- name = "example.com";
- url = "http://example.com";
-
- unpack = false;
- executable = false;
-
- system = "builtin";
-
- preferLocalBuild = true;
-
- impureEnvVars = [
- "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
- ];
-
- urls = [ "http://example.com" ];
- }
- '';
-
testScript = { nodes, ... }: ''
http_dns.wait_for_unit("nginx")
http_dns.wait_for_open_port(80)
diff --git a/tests/plugins.sh b/tests/plugins.sh
index e22bf4408..6e278ad9d 100644
--- a/tests/plugins.sh
+++ b/tests/plugins.sh
@@ -2,6 +2,11 @@ source common.sh
set -o pipefail
+if [[ $BUILD_SHARED_LIBS != 1 ]]; then
+ echo "plugins are not supported"
+ exit 99
+fi
+
res=$(nix --option setting-set true --option plugin-files $PWD/plugins/libplugintest* eval --expr builtins.anotherNull)
[ "$res"x = "nullx" ]
diff --git a/tests/pure-eval.sh b/tests/pure-eval.sh
index 1a4568ea6..b83ab8afe 100644
--- a/tests/pure-eval.sh
+++ b/tests/pure-eval.sh
@@ -30,3 +30,5 @@ nix eval --store dummy:// --write-to $TEST_ROOT/eval-out --expr '{ x = "foo" + "
rm -rf $TEST_ROOT/eval-out
(! nix eval --store dummy:// --write-to $TEST_ROOT/eval-out --expr '{ "." = "bla"; }')
+
+(! nix eval --expr '~/foo')
diff --git a/tests/remote-builds.nix b/tests/remote-builds.nix
index b9e7352c0..7b2e6f708 100644
--- a/tests/remote-builds.nix
+++ b/tests/remote-builds.nix
@@ -61,7 +61,7 @@ in
}
];
virtualisation.writableStore = true;
- virtualisation.pathsInNixDB = [ config.system.build.extraUtils ];
+ virtualisation.additionalPaths = [ config.system.build.extraUtils ];
nix.binaryCaches = lib.mkForce [ ];
programs.ssh.extraConfig = "ConnectTimeout 30";
};
diff --git a/tests/repl.sh b/tests/repl.sh
index b6937b9e9..9e6a59f18 100644
--- a/tests/repl.sh
+++ b/tests/repl.sh
@@ -42,6 +42,11 @@ testRepl () {
echo "$replOutput"
echo "$replOutput" | grep -qs "while evaluating the file" \
|| fail "nix repl --show-trace doesn't show the trace"
+
+ nix repl "${nixArgs[@]}" --option pure-eval true 2>&1 <<< "builtins.currentSystem" \
+ | grep "attribute 'currentSystem' missing"
+ nix repl "${nixArgs[@]}" 2>&1 <<< "builtins.currentSystem" \
+ | grep "$(nix-instantiate --eval -E 'builtins.currentSystem')"
}
# Simple test, try building a drv
diff --git a/tests/search.sh b/tests/search.sh
index 52e12f381..1a98f5b49 100644
--- a/tests/search.sh
+++ b/tests/search.sh
@@ -28,11 +28,19 @@ nix search -f search.nix '' |grep -q hello
e=$'\x1b' # grep doesn't support \e, \033 or even \x1b
# Multiple overlapping regexes
-(( $(nix search -f search.nix '' 'oo' 'foo' 'oo' | grep "$e\[32;1mfoo$e\\[0;1m" | wc -l) == 1 ))
-(( $(nix search -f search.nix '' 'broken b' 'en bar' | grep "$e\[32;1mbroken bar$e\\[0m" | wc -l) == 1 ))
+(( $(nix search -f search.nix '' 'oo' 'foo' 'oo' | grep -c "$e\[32;1mfoo$e\\[0;1m") == 1 ))
+(( $(nix search -f search.nix '' 'broken b' 'en bar' | grep -c "$e\[32;1mbroken bar$e\\[0m") == 1 ))
# Multiple matches
# Searching for 'o' should yield the 'o' in 'broken bar', the 'oo' in foo and 'o' in hello
-(( $(nix search -f search.nix '' 'o' | grep -Eo "$e\[32;1mo{1,2}$e\[(0|0;1)m" | wc -l) == 3 ))
+(( $(nix search -f search.nix '' 'o' | grep -Eoc "$e\[32;1mo{1,2}$e\[(0|0;1)m") == 3 ))
# Searching for 'b' should yield the 'b' in bar and the two 'b's in 'broken bar'
+# NOTE: This does not work with `grep -c` because it counts the two 'b's in 'broken bar' as one matched line
(( $(nix search -f search.nix '' 'b' | grep -Eo "$e\[32;1mb$e\[(0|0;1)m" | wc -l) == 3 ))
+
+## Tests for --exclude
+(( $(nix search -f search.nix -e hello | grep -c hello) == 0 ))
+
+(( $(nix search -f search.nix foo --exclude 'foo|bar' | grep -Ec 'foo|bar') == 0 ))
+(( $(nix search -f search.nix foo -e foo --exclude bar | grep -Ec 'foo|bar') == 0 ))
+[[ $(nix search -f search.nix -e bar --json | jq -c 'keys') == '["foo","hello"]' ]]
diff --git a/tests/setuid.nix b/tests/setuid.nix
index 35eb304ed..a83b1fc3a 100644
--- a/tests/setuid.nix
+++ b/tests/setuid.nix
@@ -10,12 +10,12 @@ with import (nixpkgs + "/nixos/lib/testing-python.nix") {
makeTest {
name = "setuid";
- machine =
+ nodes.machine =
{ config, lib, pkgs, ... }:
{ virtualisation.writableStore = true;
nix.binaryCaches = lib.mkForce [ ];
nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
- virtualisation.pathsInNixDB = [ pkgs.stdenv pkgs.pkgsi686Linux.stdenv ];
+ virtualisation.additionalPaths = [ pkgs.stdenv pkgs.pkgsi686Linux.stdenv ];
};
testScript = { nodes }: ''
diff --git a/tests/sourcehut-flakes.nix b/tests/sourcehut-flakes.nix
index 6a1930904..daa259dd6 100644
--- a/tests/sourcehut-flakes.nix
+++ b/tests/sourcehut-flakes.nix
@@ -59,7 +59,7 @@ let
echo 'ref: refs/heads/master' > $out/HEAD
mkdir -p $out/info
- echo -e '${nixpkgs.rev}\trefs/heads/master' > $out/info/refs
+ echo -e '${nixpkgs.rev}\trefs/heads/master\n${nixpkgs.rev}\trefs/tags/foo-bar' > $out/info/refs
'';
in
@@ -106,7 +106,7 @@ makeTest (
{
virtualisation.writableStore = true;
virtualisation.diskSize = 2048;
- virtualisation.pathsInNixDB = [ pkgs.hello pkgs.fuse ];
+ virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ];
virtualisation.memorySize = 4096;
nix.binaryCaches = lib.mkForce [ ];
nix.extraOptions = ''
@@ -132,6 +132,17 @@ makeTest (
client.succeed("curl -v https://git.sr.ht/ >&2")
client.succeed("nix registry list | grep nixpkgs")
+ # Test that it resolves HEAD
+ rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs --json | jq -r .revision")
+ assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
+ # Test that it resolves branches
+ rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/master --json | jq -r .revision")
+ assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
+ # Test that it resolves tags
+ rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/foo-bar --json | jq -r .revision")
+ assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
+
+ # Registry and pinning test
rev = client.succeed("nix flake info nixpkgs --json | jq -r .revision")
assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"