Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b48d8ef15 |
@@ -1,30 +0,0 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterStruct: true
|
||||
AfterClass: true
|
||||
AfterFunction: true
|
||||
AfterUnion: true
|
||||
SplitEmptyRecord: false
|
||||
PointerAlignment: Middle
|
||||
FixNamespaceComments: false
|
||||
SortIncludes: Never
|
||||
#IndentPPDirectives: BeforeHash
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignEscapedNewlines: DontAlign
|
||||
ColumnLimit: 120
|
||||
BreakStringLiterals: false
|
||||
BitFieldColonSpacing: None
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackParameters: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
EmptyLineAfterAccessModifier: Leave # change to always/never later?
|
||||
EmptyLineBeforeAccessModifier: Leave
|
||||
#PackConstructorInitializers: BinPack
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'NixOS'
|
||||
steps:
|
||||
- uses: actions/labeler@v4
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sync-labels: false
|
||||
|
||||
-10
@@ -21,14 +21,12 @@ perl/Makefile.config
|
||||
/doc/manual/language.json
|
||||
/doc/manual/xp-features.json
|
||||
/doc/manual/src/SUMMARY.md
|
||||
/doc/manual/src/SUMMARY-rl-next.md
|
||||
/doc/manual/src/command-ref/new-cli
|
||||
/doc/manual/src/command-ref/conf-file.md
|
||||
/doc/manual/src/command-ref/experimental-features-shortlist.md
|
||||
/doc/manual/src/contributing/experimental-feature-descriptions.md
|
||||
/doc/manual/src/language/builtins.md
|
||||
/doc/manual/src/language/builtin-constants.md
|
||||
/doc/manual/src/release-notes/rl-next.md
|
||||
|
||||
# /scripts/
|
||||
/scripts/nix-profile.sh
|
||||
@@ -43,16 +41,13 @@ perl/Makefile.config
|
||||
/src/libexpr/parser-tab.hh
|
||||
/src/libexpr/parser-tab.output
|
||||
/src/libexpr/nix.tbl
|
||||
/src/libexpr/tests
|
||||
/tests/unit/libexpr/libnixexpr-tests
|
||||
|
||||
# /src/libstore/
|
||||
*.gen.*
|
||||
/src/libstore/tests
|
||||
/tests/unit/libstore/libnixstore-tests
|
||||
|
||||
# /src/libutil/
|
||||
/src/libutil/tests
|
||||
/tests/unit/libutil/libnixutil-tests
|
||||
|
||||
/src/nix/nix
|
||||
@@ -95,7 +90,6 @@ perl/Makefile.config
|
||||
/tests/functional/ca/config.nix
|
||||
/tests/functional/dyn-drv/config.nix
|
||||
/tests/functional/repl-result-out
|
||||
/tests/functional/debugger-test-out
|
||||
/tests/functional/test-libstoreconsumer/test-libstoreconsumer
|
||||
|
||||
# /tests/functional/lang/
|
||||
@@ -143,12 +137,8 @@ compile_commands.json
|
||||
nix-rust/target
|
||||
|
||||
result
|
||||
result-*
|
||||
|
||||
.vscode/
|
||||
|
||||
# clangd and possibly more
|
||||
.cache/
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
include mk/build-dir.mk
|
||||
|
||||
-include $(buildprefix)Makefile.config
|
||||
clean-files += $(buildprefix)Makefile.config
|
||||
|
||||
ifeq ($(ENABLE_BUILD), yes)
|
||||
makefiles = \
|
||||
mk/precompiled-headers.mk \
|
||||
local.mk \
|
||||
@@ -21,19 +15,17 @@ makefiles = \
|
||||
misc/zsh/local.mk \
|
||||
misc/systemd/local.mk \
|
||||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk
|
||||
endif
|
||||
misc/upstart/local.mk \
|
||||
doc/manual/local.mk \
|
||||
doc/internal-api/local.mk
|
||||
|
||||
ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes)
|
||||
UNIT_TEST_ENV = _NIX_TEST_UNIT_DATA=unit-test-data
|
||||
-include Makefile.config
|
||||
|
||||
ifeq ($(tests), yes)
|
||||
makefiles += \
|
||||
tests/unit/libutil/local.mk \
|
||||
tests/unit/libutil-support/local.mk \
|
||||
tests/unit/libstore/local.mk
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_TESTS), yes)
|
||||
makefiles += \
|
||||
tests/unit/libstore/local.mk \
|
||||
tests/unit/libstore-support/local.mk \
|
||||
tests/unit/libexpr/local.mk \
|
||||
tests/unit/libexpr-support/local.mk \
|
||||
@@ -41,23 +33,12 @@ makefiles += \
|
||||
tests/functional/ca/local.mk \
|
||||
tests/functional/dyn-drv/local.mk \
|
||||
tests/functional/test-libstoreconsumer/local.mk \
|
||||
tests/functional/repl_characterization/local.mk \
|
||||
tests/functional/plugins/local.mk
|
||||
else
|
||||
makefiles += \
|
||||
mk/disable-tests.mk
|
||||
endif
|
||||
|
||||
# Some makefiles require access to built programs and must be included late.
|
||||
makefiles-late =
|
||||
|
||||
ifeq ($(ENABLE_BUILD), yes)
|
||||
makefiles-late += doc/manual/local.mk
|
||||
makefiles-late += doc/internal-api/local.mk
|
||||
endif
|
||||
|
||||
# Miscellaneous global Flags
|
||||
|
||||
OPTIMIZE = 1
|
||||
|
||||
ifeq ($(OPTIMIZE), 1)
|
||||
@@ -69,4 +50,4 @@ endif
|
||||
|
||||
include mk/lib.mk
|
||||
|
||||
GLOBAL_CXXFLAGS += -g -Wall -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src
|
||||
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++2a -I src
|
||||
|
||||
+1
-2
@@ -46,6 +46,5 @@ sandbox_shell = @sandbox_shell@
|
||||
storedir = @storedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
system = @system@
|
||||
ENABLE_BUILD = @ENABLE_BUILD@
|
||||
ENABLE_TESTS = @ENABLE_TESTS@
|
||||
tests = @tests@
|
||||
internal_api_docs = @internal_api_docs@
|
||||
|
||||
@@ -59,18 +59,12 @@ index b5d71e62..aed7b0bf 100644
|
||||
GC_bool found_me = FALSE;
|
||||
size_t nthreads = 0;
|
||||
int i;
|
||||
@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void)
|
||||
@@ -851,6 +853,31 @@ GC_INNER void GC_push_all_stacks(void)
|
||||
hi = p->altstack + p->altstack_size;
|
||||
/* FIXME: Need to scan the normal stack too, but how ? */
|
||||
/* FIXME: Assume stack grows down */
|
||||
+ } else {
|
||||
+#ifdef HAVE_PTHREAD_ATTR_GET_NP
|
||||
+ if (!pthread_attr_init(&pattr)
|
||||
+ || !pthread_attr_get_np(p->id, &pattr))
|
||||
+#else /* HAVE_PTHREAD_GETATTR_NP */
|
||||
+ if (pthread_getattr_np(p->id, &pattr))
|
||||
+#endif
|
||||
+ {
|
||||
+ if (pthread_getattr_np(p->id, &pattr)) {
|
||||
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
||||
+ }
|
||||
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
diff --git a/include/gc_allocator.h b/include/gc_allocator.h
|
||||
index 597c7f13..587286be 100644
|
||||
--- a/include/gc_allocator.h
|
||||
+++ b/include/gc_allocator.h
|
||||
@@ -312,6 +312,7 @@ public:
|
||||
|
||||
template<>
|
||||
class traceable_allocator<void> {
|
||||
+public:
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef void* pointer;
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#! /bin/sh -e
|
||||
rm -f aclocal.m4
|
||||
mkdir -p config
|
||||
exec autoreconf -vfi
|
||||
+4
-16
@@ -47,10 +47,6 @@ AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier ('cpu-os')])
|
||||
# State should be stored in /nix/var, unless the user overrides it explicitly.
|
||||
test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
|
||||
|
||||
# Assign a default value to C{,XX}FLAGS as the default configure script sets them
|
||||
# to -O2 otherwise, which we don't want to have hardcoded
|
||||
CFLAGS=${CFLAGS-""}
|
||||
CXXFLAGS=${CXXFLAGS-""}
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
@@ -72,9 +68,6 @@ case "$host_os" in
|
||||
esac
|
||||
|
||||
|
||||
ENSURE_NO_GCC_BUG_80431
|
||||
|
||||
|
||||
# Check for pubsetbuf.
|
||||
AC_MSG_CHECKING([for pubsetbuf])
|
||||
AC_LANG_PUSH(C++)
|
||||
@@ -159,17 +152,12 @@ if test "x$GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC" = xyes; then
|
||||
LDFLAGS="-latomic $LDFLAGS"
|
||||
fi
|
||||
|
||||
# Running the functional tests without building Nix is useful for testing
|
||||
# different pre-built versions of Nix against each other.
|
||||
AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]),
|
||||
ENABLE_BUILD=$enableval, ENABLE_BUILD=yes)
|
||||
AC_SUBST(ENABLE_BUILD)
|
||||
# Building without tests is useful for bootstrapping with a smaller footprint
|
||||
# or running the tests in a separate derivation. Otherwise, we do compile and
|
||||
# run them.
|
||||
AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[Do not build the tests]),
|
||||
ENABLE_TESTS=$enableval, ENABLE_TESTS=yes)
|
||||
AC_SUBST(ENABLE_TESTS)
|
||||
tests=$enableval, tests=yes)
|
||||
AC_SUBST(tests)
|
||||
|
||||
# Building without API docs is the default as Nix' C++ interfaces are internal and unstable.
|
||||
AC_ARG_ENABLE(internal_api_docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]),
|
||||
@@ -301,10 +289,10 @@ if test "$gc" = yes; then
|
||||
fi
|
||||
|
||||
|
||||
if test "$ENABLE_TESTS" = yes; then
|
||||
if test "$tests" = yes; then
|
||||
|
||||
# Look for gtest.
|
||||
PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main])
|
||||
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||
|
||||
|
||||
# Look for rapidcheck.
|
||||
|
||||
@@ -58,23 +58,6 @@ INPUT = \
|
||||
src/nix-env \
|
||||
src/nix-store
|
||||
|
||||
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
|
||||
# in the source code. If set to NO, only conditional compilation will be
|
||||
# performed. Macro expansion can be done in a controlled way by setting
|
||||
# EXPAND_ONLY_PREDEF to YES.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
# EXPAND_AS_DEFINED tags.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
|
||||
# The INCLUDE_PATH tag can be used to specify one or more directories that
|
||||
# contain include files that are not input files but should be processed by the
|
||||
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
|
||||
@@ -82,16 +65,3 @@ EXPAND_ONLY_PREDEF = YES
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH = @RAPIDCHECK_HEADERS@
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
# macro definition that is found in the sources will be used. Use the PREDEFINED
|
||||
# tag if you want to use a different macro definition that overrules the
|
||||
# definition found in the source code.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_AS_DEFINED = \
|
||||
DECLARE_COMMON_SERIALISER \
|
||||
DECLARE_WORKER_SERIALISER \
|
||||
DECLARE_SERVE_SERIALISER \
|
||||
LENGTH_PREFIXED_PROTO_HELPER
|
||||
|
||||
@@ -70,7 +70,6 @@ let
|
||||
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
|
||||
'';
|
||||
|
||||
# TODO: move this confusing special case out of here when implementing #8496
|
||||
maybeStoreDocs = optionalString
|
||||
(details ? doc)
|
||||
(replaceStrings ["@stores@"] [storeDocs] details.doc);
|
||||
@@ -79,10 +78,6 @@ let
|
||||
# Options
|
||||
|
||||
${showOptions details.flags toplevel.flags}
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.
|
||||
'';
|
||||
|
||||
showOptions = options: commonOptions:
|
||||
@@ -155,7 +150,7 @@ let
|
||||
To use this store, you need to make sure the corresponding experimental feature,
|
||||
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
|
||||
is enabled.
|
||||
For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md):
|
||||
For example, include the following in [`nix.conf`](#):
|
||||
|
||||
```
|
||||
extra-experimental-features = ${experimentalFeature}
|
||||
|
||||
+17
-40
@@ -1,10 +1,5 @@
|
||||
ifeq ($(doc_generate),yes)
|
||||
|
||||
# The version of Nix used to generate the doc. Can also be
|
||||
# `$(nix_INSTALL_PATH)` or just `nix` (to grap ambient from the `PATH`),
|
||||
# if one prefers.
|
||||
doc_nix = $(nix_PATH)
|
||||
|
||||
MANUAL_SRCS := \
|
||||
$(call rwildcard, $(d)/src, *.md) \
|
||||
$(call rwildcard, $(d)/src, */*.md)
|
||||
@@ -37,7 +32,7 @@ dummy-env = env -i \
|
||||
NIX_STATE_DIR=/dummy \
|
||||
NIX_CONFIG='cores = 0'
|
||||
|
||||
nix-eval = $(dummy-env) $(doc_nix) eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
|
||||
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
|
||||
|
||||
# re-implement mdBook's include directive to make it usable for terminal output and for proper @docroot@ substitution
|
||||
define process-includes
|
||||
@@ -97,76 +92,58 @@ $(d)/nix-profiles.5: $(d)/src/command-ref/files/profiles.md
|
||||
$(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@
|
||||
@rm $^.tmp
|
||||
|
||||
$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/SUMMARY-rl-next.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md
|
||||
$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md
|
||||
@cp $< $@
|
||||
@$(call process-includes,$@,$@)
|
||||
|
||||
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(doc_nix)
|
||||
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(bindir)/nix
|
||||
@rm -rf $@ $@.tmp
|
||||
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix true (builtins.readFile $<)'
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(doc_nix)
|
||||
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix
|
||||
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
||||
$(trace-gen) $(nix-eval) --expr '(import doc/manual/utils.nix).showSettings { inlineHTML = true; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/nix.json: $(doc_nix)
|
||||
$(trace-gen) $(dummy-env) $(doc_nix) __dump-cli > $@.tmp
|
||||
$(d)/nix.json: $(bindir)/nix
|
||||
$(trace-gen) $(dummy-env) $(bindir)/nix __dump-cli > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/conf-file.json: $(doc_nix)
|
||||
$(trace-gen) $(dummy-env) $(doc_nix) show-config --json --experimental-features nix-command > $@.tmp
|
||||
$(d)/conf-file.json: $(bindir)/nix
|
||||
$(trace-gen) $(dummy-env) $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/src/contributing/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(doc_nix)
|
||||
$(d)/src/contributing/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(bindir)/nix
|
||||
@rm -rf $@ $@.tmp
|
||||
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features.nix (builtins.fromJSON (builtins.readFile $<))'
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/src/command-ref/experimental-features-shortlist.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features-shortlist.nix $(doc_nix)
|
||||
$(d)/src/command-ref/experimental-features-shortlist.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features-shortlist.nix $(bindir)/nix
|
||||
@rm -rf $@ $@.tmp
|
||||
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features-shortlist.nix (builtins.fromJSON (builtins.readFile $<))'
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/xp-features.json: $(doc_nix)
|
||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(doc_nix) __dump-xp-features > $@.tmp
|
||||
$(d)/xp-features.json: $(bindir)/nix
|
||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(doc_nix)
|
||||
$(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
||||
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<)).builtins' >> $@.tmp;
|
||||
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(doc_nix)
|
||||
$(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(bindir)/nix
|
||||
@cat doc/manual/src/language/builtin-constants-prefix.md > $@.tmp
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtin-constants.nix (builtins.fromJSON (builtins.readFile $<)).constants' >> $@.tmp;
|
||||
@cat doc/manual/src/language/builtin-constants-suffix.md >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(d)/language.json: $(doc_nix)
|
||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(doc_nix) __dump-language > $@.tmp
|
||||
$(d)/language.json: $(bindir)/nix
|
||||
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-language > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
# Generate "Upcoming release" notes (or clear it and remove from menu)
|
||||
$(d)/src/release-notes/rl-next.md: $(d)/rl-next $(d)/rl-next/*
|
||||
@if type -p changelog-d > /dev/null; then \
|
||||
echo " GEN " $@; \
|
||||
changelog-d doc/manual/rl-next > $@; \
|
||||
else \
|
||||
echo " NULL " $@; \
|
||||
true > $@; \
|
||||
fi
|
||||
|
||||
$(d)/src/SUMMARY-rl-next.md: $(d)/src/release-notes/rl-next.md
|
||||
$(trace-gen) true
|
||||
@if [ -s $< ]; then \
|
||||
echo ' - [Upcoming release](release-notes/rl-next.md)' > $@; \
|
||||
else \
|
||||
true > $@; \
|
||||
fi
|
||||
|
||||
# Generate the HTML manual.
|
||||
.PHONY: manual-html
|
||||
manual-html: $(docdir)/manual/index.html
|
||||
@@ -196,7 +173,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)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md $(d)/src/release-notes/rl-next.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/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md
|
||||
$(trace-gen) \
|
||||
tmp="$$(mktemp -d)"; \
|
||||
cp -r doc/manual "$$tmp"; \
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
synopsis: Concise error printing in `nix repl`
|
||||
prs: 9928
|
||||
---
|
||||
|
||||
Previously, if an element of a list or attribute set threw an error while
|
||||
evaluating, `nix repl` would print the entire error (including source location
|
||||
information) inline. This output was clumsy and difficult to parse:
|
||||
|
||||
```
|
||||
nix-repl> { err = builtins.throw "uh oh!"; }
|
||||
{ err = «error:
|
||||
… while calling the 'throw' builtin
|
||||
at «string»:1:9:
|
||||
1| { err = builtins.throw "uh oh!"; }
|
||||
| ^
|
||||
|
||||
error: uh oh!»; }
|
||||
```
|
||||
|
||||
Now, only the error message is displayed, making the output much more readable.
|
||||
```
|
||||
nix-repl> { err = builtins.throw "uh oh!"; }
|
||||
{ err = «error: uh oh!»; }
|
||||
```
|
||||
|
||||
However, if the whole expression being evaluated throws an error, source
|
||||
locations and (if applicable) a stack trace are printed, just like you'd expect:
|
||||
|
||||
```
|
||||
nix-repl> builtins.throw "uh oh!"
|
||||
error:
|
||||
… while calling the 'throw' builtin
|
||||
at «string»:1:1:
|
||||
1| builtins.throw "uh oh!"
|
||||
| ^
|
||||
|
||||
error: uh oh!
|
||||
```
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
organization: NixOS
|
||||
repository: nix
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
synopsis: "`--debugger` can now access bindings from `let` expressions"
|
||||
prs: 9918
|
||||
issues: 8827.
|
||||
---
|
||||
|
||||
Breakpoints and errors in the bindings of a `let` expression can now access
|
||||
those bindings in the debugger. Previously, only the body of `let` expressions
|
||||
could access those bindings.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
synopsis: Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set
|
||||
prs: 9914
|
||||
---
|
||||
|
||||
If the `debugger-on-trace` option is set and `--debugger` is given,
|
||||
`builtins.trace` calls will behave similarly to `builtins.break` and will enter
|
||||
the debug REPL. This is useful for determining where warnings are being emitted
|
||||
from.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
synopsis: Fix handling of truncated `.drv` files.
|
||||
prs: 9673
|
||||
---
|
||||
|
||||
Previously a `.drv` that was truncated in the middle of a string would case nix to enter an infinite loop, eventually exhausting all memory and crashing.
|
||||
@@ -1,8 +0,0 @@
|
||||
synopsis: Disallow empty search regex in `nix search`
|
||||
prs: #9481
|
||||
description: {
|
||||
|
||||
[`nix search`](@docroot@/command-ref/new-cli/nix3-search.md) now requires a search regex to be passed. To show all packages, use `^`.
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
synopsis: The `--debugger` will start more reliably in `let` expressions and function calls
|
||||
prs: 9917
|
||||
issues: 6649
|
||||
---
|
||||
|
||||
Previously, if you attempted to evaluate this file with the debugger:
|
||||
|
||||
```nix
|
||||
let
|
||||
a = builtins.trace "before inner break" (
|
||||
builtins.break "hello"
|
||||
);
|
||||
b = builtins.trace "before outer break" (
|
||||
builtins.break a
|
||||
);
|
||||
in
|
||||
b
|
||||
```
|
||||
|
||||
Nix would correctly enter the debugger at `builtins.break a`, but if you asked
|
||||
it to `:continue`, it would skip over the `builtins.break "hello"` expression
|
||||
entirely.
|
||||
|
||||
Now, Nix will correctly enter the debugger at both breakpoints.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
synopsis: Reduce eval memory usage and wall time
|
||||
prs: 9658
|
||||
---
|
||||
|
||||
Reduce the size of the `Env` struct used in the evaluator by a pointer, or 8 bytes on most modern machines.
|
||||
This reduces memory usage during eval by around 2% and wall time by around 3%.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
synopsis: Add new `eval-system` setting
|
||||
prs: 4093
|
||||
---
|
||||
|
||||
Add a new `eval-system` option.
|
||||
Unlike `system`, it just overrides the value of `builtins.currentSystem`.
|
||||
This is more useful than overriding `system`, because you can build these derivations on remote builders which can work on the given system.
|
||||
In contrast, `system` also effects scheduling which will cause Nix to build those derivations locally even if that doesn't make sense.
|
||||
|
||||
`eval-system` only takes effect if it is non-empty.
|
||||
If empty (the default) `system` is used as before, so there is no breakage.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
synopsis: "`inherit (x) ...` evaluates `x` only once"
|
||||
prs: 9847
|
||||
---
|
||||
|
||||
`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute.
|
||||
This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
synopsis: Fix `nix-env --query --drv-path --json`
|
||||
prs: 9257
|
||||
---
|
||||
|
||||
Fixed a bug where `nix-env --query` ignored `--drv-path` when `--json` was set.
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
synopsis: "`nix flake check` logs the checks"
|
||||
issues: 8882
|
||||
prs: 8893
|
||||
---
|
||||
|
||||
`nix flake check` now logs the checks it runs and the derivations it evaluates:
|
||||
|
||||
```
|
||||
$ nix flake check -v
|
||||
evaluating flake...
|
||||
checking flake output 'checks'...
|
||||
checking derivation 'checks.aarch64-darwin.ghciwatch-tests'...
|
||||
derivation evaluated to /nix/store/nh7dlvsrhds4cxl91mvgj4h5cbq6skmq-ghciwatch-test-0.3.0.drv
|
||||
checking derivation 'checks.aarch64-darwin.ghciwatch-clippy'...
|
||||
derivation evaluated to /nix/store/9cb5a6wmp6kf6hidqw9wphidvb8bshym-ghciwatch-clippy-0.3.0.drv
|
||||
checking derivation 'checks.aarch64-darwin.ghciwatch-doc'...
|
||||
derivation evaluated to /nix/store/8brdd3jbawfszpbs7vdpsrhy80as1il8-ghciwatch-doc-0.3.0.drv
|
||||
checking derivation 'checks.aarch64-darwin.ghciwatch-fmt'...
|
||||
derivation evaluated to /nix/store/wjhs0l1njl5pyji53xlmfjrlya0wmz8p-ghciwatch-fmt-0.3.0.drv
|
||||
checking derivation 'checks.aarch64-darwin.ghciwatch-audit'...
|
||||
derivation evaluated to /nix/store/z0mps8dyj2ds7c0fn0819y5h5611033z-ghciwatch-audit-0.3.0.drv
|
||||
checking flake output 'packages'...
|
||||
checking derivation 'packages.aarch64-darwin.default'...
|
||||
derivation evaluated to /nix/store/41abbdyglw5x9vcsvd89xan3ydjf8d7r-ghciwatch-0.3.0.drv
|
||||
checking flake output 'apps'...
|
||||
checking flake output 'devShells'...
|
||||
checking derivation 'devShells.aarch64-darwin.default'...
|
||||
derivation evaluated to /nix/store/bc935gz7dylzmcpdb5cczr8gngv8pmdb-nix-shell.drv
|
||||
running 5 flake checks...
|
||||
warning: The check omitted these incompatible systems: aarch64-linux, x86_64-darwin, x86_64-linux
|
||||
Use '--all-systems' to check all.
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
synopsis: Coercion errors include the failing value
|
||||
issues: #561
|
||||
prs: #9754
|
||||
---
|
||||
|
||||
The `error: cannot coerce a <TYPE> to a string` message now includes the value
|
||||
which caused the error.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
error: cannot coerce a set to a string
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
error: cannot coerce a set to a string: { aesSupport = «thunk»;
|
||||
avx2Support = «thunk»; avx512Support = «thunk»; avxSupport = «thunk»;
|
||||
canExecute = «thunk»; config = «thunk»; darwinArch = «thunk»; darwinMinVersion
|
||||
= «thunk»; darwinMinVersionVariable = «thunk»; darwinPlatform = «thunk»; «84
|
||||
attributes elided»}
|
||||
```
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
synopsis: Type errors include the failing value
|
||||
issues: #561
|
||||
prs: #9753
|
||||
---
|
||||
|
||||
In errors like `value is an integer while a list was expected`, the message now
|
||||
includes the failing value.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
error: value is a set while a string was expected
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
error: expected a string but found a set: { ghc810 = «thunk»;
|
||||
ghc8102Binary = «thunk»; ghc8107 = «thunk»; ghc8107Binary = «thunk»;
|
||||
ghc865Binary = «thunk»; ghc90 = «thunk»; ghc902 = «thunk»; ghc92 = «thunk»;
|
||||
ghc924Binary = «thunk»; ghc925 = «thunk»; «17 attributes elided»}
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
synopsis: "Visual clutter in `--debugger` is reduced"
|
||||
prs: 9919
|
||||
---
|
||||
|
||||
Before:
|
||||
```
|
||||
info: breakpoint reached
|
||||
|
||||
|
||||
Starting REPL to allow you to inspect the current state of the evaluator.
|
||||
|
||||
Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help.
|
||||
|
||||
nix-repl> :continue
|
||||
error: uh oh
|
||||
|
||||
|
||||
Starting REPL to allow you to inspect the current state of the evaluator.
|
||||
|
||||
Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help.
|
||||
|
||||
nix-repl>
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
info: breakpoint reached
|
||||
|
||||
Nix 2.20.0pre20231222_dirty debugger
|
||||
Type :? for help.
|
||||
nix-repl> :continue
|
||||
error: uh oh
|
||||
|
||||
nix-repl>
|
||||
```
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
synopsis: "In the debugger, `while evaluating the attribute` errors now include position information"
|
||||
prs: 9915
|
||||
---
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
0: while evaluating the attribute 'python311.pythonForBuild.pkgs'
|
||||
0x600001522598
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
0: while evaluating the attribute 'python311.pythonForBuild.pkgs'
|
||||
/nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27
|
||||
|
||||
131|
|
||||
132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs;
|
||||
| ^
|
||||
133| in
|
||||
```
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
synopsis: Source locations are printed more consistently in errors
|
||||
issues: 561
|
||||
prs: 9555
|
||||
---
|
||||
|
||||
Source location information is now included in error messages more
|
||||
consistently. Given this code:
|
||||
|
||||
```nix
|
||||
let
|
||||
attr = {foo = "bar";};
|
||||
key = {};
|
||||
in
|
||||
attr.${key}
|
||||
```
|
||||
|
||||
Previously, Nix would show this unhelpful message when attempting to evaluate
|
||||
it:
|
||||
|
||||
```
|
||||
error:
|
||||
… while evaluating an attribute name
|
||||
|
||||
error: value is a set while a string was expected
|
||||
```
|
||||
|
||||
Now, the error message displays where the problematic value was found:
|
||||
|
||||
```
|
||||
error:
|
||||
… while evaluating an attribute name
|
||||
|
||||
at bad.nix:4:11:
|
||||
|
||||
3| key = {};
|
||||
4| in attr.${key}
|
||||
| ^
|
||||
5|
|
||||
|
||||
error: expected a string but found a set: { }
|
||||
```
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
synopsis: Some stack overflow segfaults are fixed
|
||||
issues: 9616
|
||||
prs: 9617
|
||||
---
|
||||
|
||||
The number of nested function calls has been restricted, to detect and report
|
||||
infinite function call recursions. The default maximum call depth is 10,000 and
|
||||
can be set with [the `max-call-depth`
|
||||
option](@docroot@/command-ref/conf-file.md#conf-max-call-depth).
|
||||
|
||||
This fixes segfaults or the following unhelpful error message in many cases:
|
||||
|
||||
error: stack overflow (possible infinite recursion)
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
|
||||
Segmentation fault: 11
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
|
||||
error: stack overflow
|
||||
|
||||
at «string»:1:14:
|
||||
1| (x: x x) (x: x x)
|
||||
| ^
|
||||
```
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
synopsis: Better error reporting for `with` expressions
|
||||
prs: 9658
|
||||
---
|
||||
|
||||
`with` expressions using non-attrset values to resolve variables are now reported with proper positions.
|
||||
|
||||
Previously an incorrect `with` expression would report no position at all, making it hard to determine where the error originated:
|
||||
|
||||
```
|
||||
nix-repl> with 1; a
|
||||
error:
|
||||
… <borked>
|
||||
|
||||
at «none»:0: (source not available)
|
||||
|
||||
error: value is an integer while a set was expected
|
||||
```
|
||||
|
||||
Now position information is preserved and reported as with most other errors:
|
||||
|
||||
```
|
||||
nix-repl> with 1; a
|
||||
error:
|
||||
… while evaluating the first subexpression of a with expression
|
||||
at «string»:1:1:
|
||||
1| with 1; a
|
||||
| ^
|
||||
|
||||
error: expected a set but found an integer: 1
|
||||
```
|
||||
@@ -109,7 +109,6 @@
|
||||
- [CLI guideline](contributing/cli-guideline.md)
|
||||
- [C++ style guide](contributing/cxx.md)
|
||||
- [Release Notes](release-notes/release-notes.md)
|
||||
{{#include ./SUMMARY-rl-next.md}}
|
||||
- [Release 2.18 (2023-09-20)](release-notes/rl-2.18.md)
|
||||
- [Release 2.17 (2023-07-24)](release-notes/rl-2.17.md)
|
||||
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
|
||||
|
||||
@@ -35,51 +35,13 @@ standard input.
|
||||
|
||||
- `--parse`\
|
||||
Just parse the input files, and print their abstract syntax trees on
|
||||
standard output as a Nix expression.
|
||||
standard output in ATerm format.
|
||||
|
||||
- `--eval`\
|
||||
Just parse and evaluate the input files, and print the resulting
|
||||
values on standard output. No instantiation of store derivations
|
||||
takes place.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This option produces output which can be parsed as a Nix expression which
|
||||
> will produce a different result than the input expression when evaluated.
|
||||
> For example, these two Nix expressions print the same result despite
|
||||
> having different meaning:
|
||||
>
|
||||
> ```console
|
||||
> $ nix-instantiate --eval --expr '{ a = {}; }'
|
||||
> { a = <CODE>; }
|
||||
> $ nix-instantiate --eval --expr '{ a = <CODE>; }'
|
||||
> { a = <CODE>; }
|
||||
> ```
|
||||
>
|
||||
> For human-readable output, `nix eval` (experimental) is more informative:
|
||||
>
|
||||
> ```console
|
||||
> $ nix-instantiate --eval --expr 'a: a'
|
||||
> <LAMBDA>
|
||||
> $ nix eval --expr 'a: a'
|
||||
> «lambda @ «string»:1:1»
|
||||
> ```
|
||||
>
|
||||
> For machine-readable output, the `--xml` option produces unambiguous
|
||||
> output:
|
||||
>
|
||||
> ```console
|
||||
> $ nix-instantiate --eval --xml --expr '{ foo = <CODE>; }'
|
||||
> <?xml version='1.0' encoding='utf-8'?>
|
||||
> <expr>
|
||||
> <attrs>
|
||||
> <attr column="3" line="1" name="foo">
|
||||
> <unevaluated />
|
||||
> </attr>
|
||||
> </attrs>
|
||||
> </expr>
|
||||
> ```
|
||||
|
||||
- `--find-file`\
|
||||
Look up the given files in Nix’s search path (as specified by the
|
||||
`NIX_PATH` environment variable). If found, print the corresponding
|
||||
@@ -99,11 +61,11 @@ standard input.
|
||||
|
||||
- `--json`\
|
||||
When used with `--eval`, print the resulting value as an JSON
|
||||
representation of the abstract syntax tree rather than as a Nix expression.
|
||||
representation of the abstract syntax tree rather than as an ATerm.
|
||||
|
||||
- `--xml`\
|
||||
When used with `--eval`, print the resulting value as an XML
|
||||
representation of the abstract syntax tree rather than as a Nix expression.
|
||||
representation of the abstract syntax tree rather than as an ATerm.
|
||||
The schema is the same as that used by the [`toXML`
|
||||
built-in](../language/builtins.md).
|
||||
|
||||
@@ -171,24 +133,28 @@ $ nix-instantiate --eval --xml --expr '1 + 2'
|
||||
The difference between non-strict and strict evaluation:
|
||||
|
||||
```console
|
||||
$ nix-instantiate --eval --xml --expr '{ x = {}; }'
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<attrs>
|
||||
<attr column="3" line="1" name="x">
|
||||
<unevaluated />
|
||||
</attr>
|
||||
</attrs>
|
||||
</expr>
|
||||
$ nix-instantiate --eval --xml --expr 'rec { x = "foo"; y = x; }'
|
||||
...
|
||||
<attr name="x">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
<attr name="y">
|
||||
<unevaluated />
|
||||
</attr>
|
||||
...
|
||||
```
|
||||
|
||||
$ nix-instantiate --eval --xml --strict --expr '{ x = {}; }'
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<attrs>
|
||||
<attr column="3" line="1" name="x">
|
||||
<attrs>
|
||||
</attrs>
|
||||
</attr>
|
||||
</attrs>
|
||||
</expr>
|
||||
Note that `y` is left unevaluated (the XML representation doesn’t
|
||||
attempt to show non-normal forms).
|
||||
|
||||
```console
|
||||
$ nix-instantiate --eval --xml --strict --expr 'rec { x = "foo"; y = x; }'
|
||||
...
|
||||
<attr name="x">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
<attr name="y">
|
||||
<string value="foo" />
|
||||
</attr>
|
||||
...
|
||||
```
|
||||
|
||||
@@ -235,14 +235,14 @@ package like Terraform:
|
||||
|
||||
```bash
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash --packages 'terraform.withPlugins (plugins: [ plugins.openstack ])'
|
||||
#! nix-shell -i bash --packages "terraform.withPlugins (plugins: [ plugins.openstack ])"
|
||||
|
||||
terraform apply
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> You must use single or double quotes (`'`, `"`) when passing a simple Nix expression
|
||||
> You must use double quotes (`"`) when passing a simple Nix expression
|
||||
> in a nix-shell shebang.
|
||||
|
||||
Finally, using the merging of multiple nix-shell shebangs the following
|
||||
@@ -251,7 +251,7 @@ branch):
|
||||
|
||||
```haskell
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i runghc --packages 'haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])'
|
||||
#! nix-shell -i runghc --packages "haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])"
|
||||
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz
|
||||
|
||||
import Network.Curl.Download
|
||||
|
||||
@@ -203,7 +203,3 @@ Most Nix commands accept the following command-line options:
|
||||
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 the build.
|
||||
Also note the warning under `nix-store --repair-path`.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.
|
||||
|
||||
@@ -31,7 +31,7 @@ This shell also adds `./outputs/bin/nix` to your `$PATH` so you can run `nix` im
|
||||
To get a shell with one of the other [supported compilation environments](#compilation-environments):
|
||||
|
||||
```console
|
||||
$ nix develop .#native-clangStdenvPackages
|
||||
$ nix develop .#native-clang11StdenvPackages
|
||||
```
|
||||
|
||||
> **Note**
|
||||
@@ -42,8 +42,8 @@ $ nix develop .#native-clangStdenvPackages
|
||||
To build Nix itself in this shell:
|
||||
|
||||
```console
|
||||
[nix-shell]$ autoreconfPhase
|
||||
[nix-shell]$ configurePhase
|
||||
[nix-shell]$ ./bootstrap.sh
|
||||
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
|
||||
[nix-shell]$ make -j $NIX_BUILD_CORES
|
||||
```
|
||||
|
||||
@@ -75,7 +75,7 @@ $ nix-shell
|
||||
To get a shell with one of the other [supported compilation environments](#compilation-environments):
|
||||
|
||||
```console
|
||||
$ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages
|
||||
$ nix-shell --attr devShells.x86_64-linux.native-clang11StdenvPackages
|
||||
```
|
||||
|
||||
> **Note**
|
||||
@@ -86,7 +86,7 @@ $ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages
|
||||
To build Nix itself in this shell:
|
||||
|
||||
```console
|
||||
[nix-shell]$ autoreconfPhase
|
||||
[nix-shell]$ ./bootstrap.sh
|
||||
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
|
||||
[nix-shell]$ make -j $NIX_BUILD_CORES
|
||||
```
|
||||
@@ -146,31 +146,6 @@ $ nix build .#packages.aarch64-linux.default
|
||||
Cross-compiled builds are available for ARMv6 (`armv6l-linux`) and ARMv7 (`armv7l-linux`).
|
||||
Add more [system types](#system-type) to `crossSystems` in `flake.nix` to bootstrap Nix on unsupported platforms.
|
||||
|
||||
### Building for multiple platforms at once
|
||||
|
||||
It is useful to perform multiple cross and native builds on the same source tree,
|
||||
for example to ensure that better support for one platform doesn't break the build for another.
|
||||
In order to facilitate this, Nix has some support for being built out of tree – that is, placing build artefacts in a different directory than the source code:
|
||||
|
||||
1. Create a directory for the build, e.g.
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
```
|
||||
|
||||
2. Run the configure script from that directory, e.g.
|
||||
|
||||
```bash
|
||||
cd build
|
||||
../configure <configure flags>
|
||||
```
|
||||
|
||||
3. Run make from the source directory, but with the build directory specified, e.g.
|
||||
|
||||
```bash
|
||||
make builddir=build <make flags>
|
||||
```
|
||||
|
||||
## System type
|
||||
|
||||
Nix uses a string with he following format to identify the *system type* or *platform* it runs on:
|
||||
@@ -310,40 +285,3 @@ can build it yourself:
|
||||
|
||||
Metrics about the change in line/function coverage over time are also
|
||||
[available](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts).
|
||||
|
||||
## Add a release note
|
||||
|
||||
`doc/manual/rl-next` contains release notes entries for all unreleased changes.
|
||||
|
||||
User-visible changes should come with a release note.
|
||||
|
||||
### Add an entry
|
||||
|
||||
Here's what a complete entry looks like. The file name is not incorporated in the document.
|
||||
|
||||
```
|
||||
---
|
||||
synopsis: Basically a title
|
||||
issues: 1234
|
||||
prs: 1238
|
||||
---
|
||||
|
||||
Here's one or more paragraphs that describe the change.
|
||||
|
||||
- It's markdown
|
||||
- Add references to the manual using @docroot@
|
||||
```
|
||||
|
||||
Significant changes should add the following header, which moves them to the top.
|
||||
|
||||
```
|
||||
significance: significant
|
||||
```
|
||||
|
||||
<!-- Keep an eye on https://codeberg.org/fgaz/changelog-d/issues/1 -->
|
||||
See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog).
|
||||
|
||||
### Build process
|
||||
|
||||
Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`.
|
||||
Set `buildUnreleasedNotes = true;` in `flake.nix` to build the release notes on the fly.
|
||||
|
||||
@@ -2,48 +2,10 @@
|
||||
|
||||
## Unit-tests
|
||||
|
||||
The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
|
||||
|
||||
[googletest]: https://google.github.io/googletest/
|
||||
[rapidcheck]: https://github.com/emil-e/rapidcheck
|
||||
|
||||
### Source and header layout
|
||||
|
||||
> An example of some files, demonstrating much of what is described below
|
||||
>
|
||||
> ```
|
||||
> src
|
||||
> ├── libexpr
|
||||
> │ ├── value/context.hh
|
||||
> │ ├── value/context.cc
|
||||
> │ │
|
||||
> │ …
|
||||
> └── tests
|
||||
> │ ├── value/context.hh
|
||||
> │ ├── value/context.cc
|
||||
> │ │
|
||||
> │ …
|
||||
> │
|
||||
> ├── unit-test-data
|
||||
> │ ├── libstore
|
||||
> │ │ ├── worker-protocol/content-address.bin
|
||||
> │ │ …
|
||||
> │ …
|
||||
> …
|
||||
> ```
|
||||
|
||||
The unit tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_shortname}/tests` within the directory for the library (`src/${library_shortname}`).
|
||||
|
||||
The data is in `unit-test-data`, with one subdir per library, with the same name as where the code goes.
|
||||
For example, `libnixstore` code is in `src/libstore`, and its test data is in `unit-test-data/libstore`.
|
||||
The path to the `unit-test-data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
|
||||
|
||||
> **Note**
|
||||
> Due to the way googletest works, downstream unit test executables will actually include and re-run upstream library tests.
|
||||
> Therefore it is important that the same value for `_NIX_TEST_UNIT_DATA` be used with the tests for each library.
|
||||
> That is why we have the test data nested within a single `unit-test-data` directory.
|
||||
|
||||
### Running tests
|
||||
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
|
||||
under `tests/unit/{library_name}/tests` using the
|
||||
[googletest](https://google.github.io/googletest/) and
|
||||
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
|
||||
|
||||
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`.
|
||||
Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable.
|
||||
@@ -60,24 +22,6 @@ It is important that these testing libraries don't contain any actual tests them
|
||||
On some platforms they would be run as part of every test executable that uses them, which is redundant.
|
||||
On other platforms they wouldn't be run at all.
|
||||
|
||||
### Characterization testing
|
||||
|
||||
See [below](#characterization-testing-1) for a broader discussion of characterization testing.
|
||||
|
||||
Like with the functional characterization, `_NIX_TEST_ACCEPT=1` is also used.
|
||||
For example:
|
||||
```shell-session
|
||||
$ _NIX_TEST_ACCEPT=1 make libstore-tests-exe_RUN
|
||||
...
|
||||
[ SKIPPED ] WorkerProtoTest.string_read
|
||||
[ SKIPPED ] WorkerProtoTest.string_write
|
||||
[ SKIPPED ] WorkerProtoTest.storePath_read
|
||||
[ SKIPPED ] WorkerProtoTest.storePath_write
|
||||
...
|
||||
```
|
||||
will regenerate the "golden master" expected result for the `libnixstore` characterization tests.
|
||||
The characterization tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
|
||||
|
||||
## Functional tests
|
||||
|
||||
The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
After cloning Nix's Git repository, issue the following commands:
|
||||
|
||||
```console
|
||||
$ autoreconf -vfi
|
||||
$ ./bootstrap.sh
|
||||
$ ./configure options...
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
@@ -261,9 +261,6 @@ Derivations can declare some infrequently used optional attributes.
|
||||
useful for very trivial derivations (such as `writeText` in Nixpkgs)
|
||||
that are cheaper to build than to substitute from a binary cache.
|
||||
|
||||
You may disable the effects of this attibute by enabling the
|
||||
`always-allow-substitutes` configuration option in Nix.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> You need to have a builder configured which satisfies the
|
||||
@@ -274,21 +271,18 @@ Derivations can declare some infrequently used optional attributes.
|
||||
|
||||
- [`__structuredAttrs`]{#adv-attr-structuredAttrs}\
|
||||
If the special attribute `__structuredAttrs` is set to `true`, the other derivation
|
||||
attributes are serialised into a file in JSON format. The environment variable
|
||||
`NIX_ATTRS_JSON_FILE` points to the exact location of that file both in a build
|
||||
and a [`nix-shell`](../command-ref/nix-shell.md). This obviates the need for
|
||||
[`passAsFile`](#adv-attr-passAsFile) since JSON files have no size restrictions,
|
||||
unlike process environments.
|
||||
attributes are serialised in JSON format and made available to the
|
||||
builder via the file `.attrs.json` in the builder’s temporary
|
||||
directory. This obviates the need for [`passAsFile`](#adv-attr-passAsFile) since JSON files
|
||||
have no size restrictions, unlike process environments.
|
||||
|
||||
It also makes it possible to tweak derivation settings in a structured way; see
|
||||
[`outputChecks`](#adv-attr-outputChecks) for example.
|
||||
|
||||
As a convenience to Bash builders,
|
||||
Nix writes a script that initialises shell variables
|
||||
corresponding to all attributes that are representable in Bash. The
|
||||
environment variable `NIX_ATTRS_SH_FILE` points to the exact
|
||||
location of the script, both in a build and a
|
||||
[`nix-shell`](../command-ref/nix-shell.md). This includes non-nested
|
||||
Nix writes a script named `.attrs.sh` to the builder’s directory
|
||||
that initialises shell variables corresponding to all attributes
|
||||
that are representable in Bash. This includes non-nested
|
||||
(associative) arrays. For example, the attribute `hardening.format = true`
|
||||
ends up as the Bash associative array element `${hardening[format]}`.
|
||||
|
||||
|
||||
@@ -123,11 +123,6 @@ The builder is executed as follows:
|
||||
|
||||
- `NIX_STORE` is set to the path of the top-level Nix store
|
||||
directory (typically, `/nix/store`).
|
||||
|
||||
- `NIX_ATTRS_JSON_FILE` & `NIX_ATTRS_SH_FILE` if `__structuredAttrs`
|
||||
is set to `true` for the dervation. A detailed explanation of this
|
||||
behavior can be found in the
|
||||
[section about structured attrs](./advanced-attributes.md#adv-attr-structuredAttrs).
|
||||
|
||||
- For each output declared in `outputs`, the corresponding
|
||||
environment variable is set to point to the intended path in the
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Release X.Y (202?-??-??)
|
||||
Generated
+21
-4
@@ -16,18 +16,34 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709884566,
|
||||
"narHash": "sha256-NSYJg2sfdO/XS3L8XN/59Zhzn0dqWm7XtVnKI2mHq3w=",
|
||||
"lastModified": 1700748986,
|
||||
"narHash": "sha256-/nqLrNU297h3PCw4QyDpZKZEUHmialJdZW2ceYFobds=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2be119add7b37dc535da2dd4cba68e2cf8d1517e",
|
||||
"rev": "9ba29e2346bc542e9909d1021e8fd7d4b3f64db0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11-small",
|
||||
"ref": "nixos-23.05-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -51,6 +67,7 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
{
|
||||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11-small";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small";
|
||||
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
||||
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||
|
||||
outputs = { self, nixpkgs, nixpkgs-regression, flake-compat }:
|
||||
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src, flake-compat }:
|
||||
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
inherit (lib) fileset;
|
||||
|
||||
officialRelease = true;
|
||||
|
||||
# Set to true to build the release notes for the next release.
|
||||
buildUnreleasedNotes = false;
|
||||
|
||||
version = lib.fileContents ./.version + versionSuffix;
|
||||
versionSuffix =
|
||||
if officialRelease
|
||||
@@ -28,12 +25,9 @@
|
||||
darwinSystems = [ "x86_64-darwin" "aarch64-darwin" ];
|
||||
systems = linuxSystems ++ darwinSystems;
|
||||
|
||||
crossSystems = [
|
||||
"armv6l-linux" "armv7l-linux"
|
||||
"x86_64-freebsd13" "x86_64-netbsd"
|
||||
];
|
||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||
|
||||
stdenvs = [ "gccStdenv" "clangStdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
|
||||
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
|
||||
|
||||
forAllSystems = lib.genAttrs systems;
|
||||
|
||||
@@ -48,23 +42,54 @@
|
||||
})
|
||||
stdenvs);
|
||||
|
||||
# Experimental fileset library: https://github.com/NixOS/nixpkgs/pull/222981
|
||||
# Not an "idiomatic" flake input because:
|
||||
# - Propagation to dependent locks: https://github.com/NixOS/nix/issues/7730
|
||||
# - Subflake would download redundant and huge parent flake
|
||||
# - No git tree hash support: https://github.com/NixOS/nix/issues/6044
|
||||
inherit (import (builtins.fetchTarball { url = "https://github.com/NixOS/nix/archive/1bdcd7fc8a6a40b2e805bad759b36e64e911036b.tar.gz"; sha256 = "sha256:14ljlpdsp4x7h1fkhbmc4bd3vsqnx8zdql4h3037wh09ad6a0893"; }))
|
||||
fileset;
|
||||
|
||||
baseFiles =
|
||||
# .gitignore has already been processed, so any changes in it are irrelevant
|
||||
# at this point. It is not represented verbatim for test purposes because
|
||||
# that would interfere with repo semantics.
|
||||
fileset.fileFilter (f: f.name != ".gitignore") ./.;
|
||||
|
||||
nixSrc = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.intersect baseFiles (fileset.unions [
|
||||
./.version
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
./bootstrap.sh
|
||||
./configure.ac
|
||||
./doc
|
||||
./local.mk
|
||||
./m4
|
||||
./Makefile
|
||||
./Makefile.config.in
|
||||
./misc
|
||||
./mk
|
||||
./precompiled-headers.h
|
||||
./src
|
||||
./tests/functional
|
||||
./tests/unit
|
||||
./COPYING
|
||||
./scripts/local.mk
|
||||
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
|
||||
# TODO: do we really need README.md? It doesn't seem used in the build.
|
||||
./README.md
|
||||
]);
|
||||
};
|
||||
|
||||
# Memoize nixpkgs for different platforms for efficiency.
|
||||
nixpkgsFor = forAllSystems
|
||||
(system: let
|
||||
make-pkgs = crossSystem: stdenv: import nixpkgs {
|
||||
localSystem = {
|
||||
inherit system;
|
||||
};
|
||||
crossSystem = if crossSystem == null then null else {
|
||||
system = crossSystem;
|
||||
} // lib.optionalAttrs (crossSystem == "x86_64-freebsd13") {
|
||||
useLLVM = true;
|
||||
};
|
||||
inherit system crossSystem;
|
||||
overlays = [
|
||||
(overlayFor (p: p.${stdenv}))
|
||||
];
|
||||
|
||||
config.permittedInsecurePackages = [ "nix-2.13.6" ];
|
||||
};
|
||||
stdenvs = forAllStdenvs (make-pkgs null);
|
||||
native = stdenvs.stdenvPackages;
|
||||
@@ -74,6 +99,112 @@
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
});
|
||||
|
||||
commonDeps =
|
||||
{ pkgs
|
||||
, isStatic ? pkgs.stdenv.hostPlatform.isStatic
|
||||
}:
|
||||
with pkgs; rec {
|
||||
# Use "busybox-sandbox-shell" if present,
|
||||
# if not (legacy) fallback and hope it's sufficient.
|
||||
sh = pkgs.busybox-sandbox-shell or (busybox.override {
|
||||
useMusl = true;
|
||||
enableStatic = true;
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_FEATURE_FANCY_ECHO y
|
||||
CONFIG_FEATURE_SH_MATH y
|
||||
CONFIG_FEATURE_SH_MATH_64 y
|
||||
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
|
||||
CONFIG_ASH_ALIAS y
|
||||
CONFIG_ASH_BASH_COMPAT y
|
||||
CONFIG_ASH_CMDCMD y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_GETOPTS y
|
||||
CONFIG_ASH_INTERNAL_GLOB y
|
||||
CONFIG_ASH_JOB_CONTROL y
|
||||
CONFIG_ASH_PRINTF y
|
||||
CONFIG_ASH_TEST y
|
||||
'';
|
||||
});
|
||||
|
||||
configureFlags =
|
||||
lib.optionals stdenv.isLinux [
|
||||
"--with-boost=${boost}/lib"
|
||||
"--with-sandbox-shell=${sh}/bin/busybox"
|
||||
]
|
||||
++ lib.optionals (stdenv.isLinux && !(isStatic && stdenv.system == "aarch64-linux")) [
|
||||
"LDFLAGS=-fuse-ld=gold"
|
||||
];
|
||||
|
||||
testConfigureFlags = [
|
||||
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
|
||||
];
|
||||
|
||||
internalApiDocsConfigureFlags = [
|
||||
"--enable-internal-api-docs"
|
||||
];
|
||||
|
||||
nativeBuildDeps =
|
||||
[
|
||||
buildPackages.bison
|
||||
buildPackages.flex
|
||||
(lib.getBin buildPackages.lowdown-nix)
|
||||
buildPackages.mdbook
|
||||
buildPackages.mdbook-linkcheck
|
||||
buildPackages.autoconf-archive
|
||||
buildPackages.autoreconfHook
|
||||
buildPackages.pkg-config
|
||||
|
||||
# Tests
|
||||
buildPackages.git
|
||||
buildPackages.mercurial # FIXME: remove? only needed for tests
|
||||
buildPackages.jq # Also for custom mdBook preprocessor.
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
|
||||
|
||||
buildDeps =
|
||||
[ curl
|
||||
bzip2 xz brotli editline
|
||||
openssl sqlite
|
||||
libarchive
|
||||
boost
|
||||
lowdown-nix
|
||||
]
|
||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
|
||||
|
||||
checkDeps = [
|
||||
gtest
|
||||
rapidcheck
|
||||
];
|
||||
|
||||
internalApiDocsDeps = [
|
||||
buildPackages.doxygen
|
||||
];
|
||||
|
||||
awsDeps = lib.optional (stdenv.isLinux || stdenv.isDarwin)
|
||||
(aws-sdk-cpp.override {
|
||||
apis = ["s3" "transfer"];
|
||||
customMemoryManagement = false;
|
||||
});
|
||||
|
||||
propagatedDeps =
|
||||
[ ((boehmgc.override {
|
||||
enableLargeConfig = true;
|
||||
}).overrideAttrs(o: {
|
||||
patches = (o.patches or []) ++ [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
];
|
||||
})
|
||||
)
|
||||
nlohmann_json
|
||||
];
|
||||
};
|
||||
|
||||
installScriptFor = systems:
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
runCommand "installer-script"
|
||||
@@ -108,40 +239,37 @@
|
||||
echo "file installer $out/install" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
|
||||
testNixVersions = pkgs: client: daemon: let
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
pname =
|
||||
"nix-tests"
|
||||
+ lib.optionalString
|
||||
(lib.versionAtLeast daemon.version "2.4pre20211005" &&
|
||||
lib.versionAtLeast client.version "2.4pre20211005")
|
||||
"-${client.version}-against-${daemon.version}";
|
||||
|
||||
inherit fileset;
|
||||
};
|
||||
in nix.overrideAttrs (prevAttrs: {
|
||||
testNixVersions = pkgs: client: daemon: with commonDeps { inherit pkgs; }; with pkgs.lib; pkgs.stdenv.mkDerivation {
|
||||
NIX_DAEMON_PACKAGE = daemon;
|
||||
NIX_CLIENT_PACKAGE = client;
|
||||
name =
|
||||
"nix-tests"
|
||||
+ optionalString
|
||||
(versionAtLeast daemon.version "2.4pre20211005" &&
|
||||
versionAtLeast client.version "2.4pre20211005")
|
||||
"-${client.version}-against-${daemon.version}";
|
||||
inherit version;
|
||||
|
||||
src = nixSrc;
|
||||
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps ++ awsDeps ++ checkDeps;
|
||||
propagatedBuildInputs = propagatedDeps;
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
configureFlags = testConfigureFlags; # otherwise configure fails
|
||||
dontBuild = true;
|
||||
doInstallCheck = true;
|
||||
|
||||
configureFlags = prevAttrs.configureFlags ++ [
|
||||
# We don't need the actual build here.
|
||||
"--disable-build"
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
'';
|
||||
|
||||
installCheckPhase = lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
|
||||
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
||||
'' + ''
|
||||
mkdir -p src/nix-channel
|
||||
make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
|
||||
'';
|
||||
});
|
||||
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
|
||||
};
|
||||
|
||||
binaryTarball = nix: pkgs:
|
||||
let
|
||||
@@ -218,62 +346,166 @@
|
||||
'';
|
||||
|
||||
overlayFor = getStdenv: final: prev:
|
||||
let
|
||||
currentStdenv = getStdenv final;
|
||||
comDeps = with final; commonDeps {
|
||||
inherit pkgs;
|
||||
inherit (currentStdenv.hostPlatform) isStatic;
|
||||
};
|
||||
in {
|
||||
let currentStdenv = getStdenv final; in
|
||||
{
|
||||
nixStable = prev.nix;
|
||||
|
||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||
nixUnstable = prev.nixUnstable;
|
||||
|
||||
changelog-d = final.buildPackages.callPackage ./misc/changelog-d.nix { };
|
||||
boehmgc-nix = (final.boehmgc.override {
|
||||
enableLargeConfig = true;
|
||||
}).overrideAttrs (o: {
|
||||
patches = (o.patches or [ ]) ++ [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
nix =
|
||||
with final;
|
||||
with commonDeps {
|
||||
inherit pkgs;
|
||||
inherit (currentStdenv.hostPlatform) isStatic;
|
||||
};
|
||||
let
|
||||
canRunInstalled = currentStdenv.buildPlatform.canExecute currentStdenv.hostPlatform;
|
||||
in currentStdenv.mkDerivation (finalAttrs: {
|
||||
name = "nix-${version}";
|
||||
inherit version;
|
||||
|
||||
# https://github.com/ivmai/bdwgc/pull/586
|
||||
./boehmgc-traceable_allocator-public.diff
|
||||
];
|
||||
src = nixSrc;
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps
|
||||
# There have been issues building these dependencies
|
||||
++ lib.optionals (currentStdenv.hostPlatform == currentStdenv.buildPlatform) awsDeps
|
||||
++ lib.optionals finalAttrs.doCheck checkDeps;
|
||||
|
||||
propagatedBuildInputs = propagatedDeps;
|
||||
|
||||
disallowedReferences = [ boost ];
|
||||
|
||||
preConfigure = lib.optionalString (! currentStdenv.hostPlatform.isStatic)
|
||||
''
|
||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||
# https://github.com/NixOS/nixpkgs/issues/45462
|
||||
mkdir -p $out/lib
|
||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||
rm -f $out/lib/*.a
|
||||
${lib.optionalString currentStdenv.hostPlatform.isLinux ''
|
||||
chmod u+w $out/lib/*.so.*
|
||||
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
''}
|
||||
${lib.optionalString currentStdenv.hostPlatform.isDarwin ''
|
||||
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
|
||||
''}
|
||||
'';
|
||||
|
||||
configureFlags = configureFlags ++
|
||||
[ "--sysconfdir=/etc" ] ++
|
||||
lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell" ++
|
||||
[ (lib.enableFeature finalAttrs.doCheck "tests") ] ++
|
||||
lib.optionals finalAttrs.doCheck testConfigureFlags ++
|
||||
lib.optional (!canRunInstalled) "--disable-doc-gen";
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
|
||||
|
||||
doCheck = true;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $doc/nix-support
|
||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||
${lib.optionalString currentStdenv.hostPlatform.isStatic ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||
''}
|
||||
${lib.optionalString currentStdenv.isDarwin ''
|
||||
install_name_tool \
|
||||
-change ${boost}/lib/libboost_context.dylib \
|
||||
$out/lib/libboost_context.dylib \
|
||||
$out/lib/libnixutil.dylib
|
||||
''}
|
||||
'';
|
||||
|
||||
doInstallCheck = finalAttrs.doCheck;
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
|
||||
|
||||
separateDebugInfo = !currentStdenv.hostPlatform.isStatic;
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation {
|
||||
name = "nix-perl-${version}";
|
||||
|
||||
src = self;
|
||||
|
||||
nativeBuildInputs =
|
||||
[ buildPackages.autoconf-archive
|
||||
buildPackages.autoreconfHook
|
||||
buildPackages.pkg-config
|
||||
];
|
||||
|
||||
buildInputs =
|
||||
[ nix
|
||||
curl
|
||||
bzip2
|
||||
xz
|
||||
pkgs.perl
|
||||
boost
|
||||
]
|
||||
++ 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}"
|
||||
];
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
postUnpack = "sourceRoot=$sourceRoot/perl";
|
||||
});
|
||||
|
||||
meta.platforms = lib.platforms.unix;
|
||||
});
|
||||
|
||||
default-busybox-sandbox-shell = final.busybox.override {
|
||||
useMusl = true;
|
||||
enableStatic = true;
|
||||
enableMinimal = true;
|
||||
extraConfig = ''
|
||||
CONFIG_FEATURE_FANCY_ECHO y
|
||||
CONFIG_FEATURE_SH_MATH y
|
||||
CONFIG_FEATURE_SH_MATH_64 y
|
||||
lowdown-nix = with final; currentStdenv.mkDerivation rec {
|
||||
name = "lowdown-0.9.0";
|
||||
|
||||
CONFIG_ASH y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
|
||||
src = lowdown-src;
|
||||
|
||||
CONFIG_ASH_ALIAS y
|
||||
CONFIG_ASH_BASH_COMPAT y
|
||||
CONFIG_ASH_CMDCMD y
|
||||
CONFIG_ASH_ECHO y
|
||||
CONFIG_ASH_GETOPTS y
|
||||
CONFIG_ASH_INTERNAL_GLOB y
|
||||
CONFIG_ASH_JOB_CONTROL y
|
||||
CONFIG_ASH_PRINTF y
|
||||
CONFIG_ASH_TEST y
|
||||
outputs = [ "out" "bin" "dev" ];
|
||||
|
||||
nativeBuildInputs = [ buildPackages.which ];
|
||||
|
||||
configurePhase = ''
|
||||
${if (currentStdenv.isDarwin && currentStdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||
./configure \
|
||||
PREFIX=${placeholder "dev"} \
|
||||
BINDIR=${placeholder "bin"}/bin
|
||||
'';
|
||||
};
|
||||
|
||||
nix = final.callPackage ./package.nix {
|
||||
inherit versionSuffix fileset;
|
||||
stdenv = currentStdenv;
|
||||
boehmgc = final.boehmgc-nix;
|
||||
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
|
||||
};
|
||||
};
|
||||
|
||||
nixos-lib = import (nixpkgs + "/nixos/lib") { };
|
||||
|
||||
# https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests
|
||||
runNixOSTestFor = system: test: nixos-lib.runTest {
|
||||
imports = [ test ];
|
||||
hostPkgs = nixpkgsFor.${system}.native;
|
||||
defaults = {
|
||||
nixpkgs.pkgs = nixpkgsFor.${system}.native;
|
||||
};
|
||||
_module.args.nixpkgs = nixpkgs;
|
||||
};
|
||||
|
||||
in {
|
||||
# A Nixpkgs overlay that overrides the 'nix' and
|
||||
# 'nix.perl-bindings' packages.
|
||||
@@ -284,6 +516,21 @@
|
||||
# Binary package for various platforms.
|
||||
build = forAllSystems (system: self.packages.${system}.nix);
|
||||
|
||||
buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
|
||||
|
||||
buildCross = forAllCrossSystems (crossSystem:
|
||||
lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
|
||||
|
||||
buildNoGc = forAllSystems (system: self.packages.${system}.nix.overrideAttrs (a: { configureFlags = (a.configureFlags or []) ++ ["--enable-gc=no"];}));
|
||||
|
||||
buildNoTests = forAllSystems (system:
|
||||
self.packages.${system}.nix.overrideAttrs (a: {
|
||||
doCheck =
|
||||
assert ! a?dontCheck;
|
||||
false;
|
||||
})
|
||||
);
|
||||
|
||||
# Perl bindings for various platforms.
|
||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
|
||||
|
||||
@@ -292,53 +539,126 @@
|
||||
# the installation script.
|
||||
binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native);
|
||||
|
||||
binaryTarballCross = lib.genAttrs ["x86_64-linux"] (system:
|
||||
forAllCrossSystems (crossSystem:
|
||||
binaryTarball
|
||||
self.packages.${system}."nix-${crossSystem}"
|
||||
nixpkgsFor.${system}.cross.${crossSystem}));
|
||||
|
||||
# The first half of the installation script. This is uploaded
|
||||
# to https://nixos.org/nix/install. It downloads the binary
|
||||
# tarball for the user's system and calls the second half of the
|
||||
# installation script.
|
||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" "armv6l-linux" "armv7l-linux" ];
|
||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
||||
|
||||
# docker image with Nix inside
|
||||
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||
|
||||
# API docs for Nix's unstable internal C++ interfaces.
|
||||
internal-api-docs = let
|
||||
nixpkgs = nixpkgsFor.x86_64-linux.native;
|
||||
inherit (nixpkgs) pkgs;
|
||||
# Line coverage analysis.
|
||||
coverage =
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
with commonDeps { inherit pkgs; };
|
||||
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
inherit versionSuffix fileset officialRelease buildUnreleasedNotes;
|
||||
inherit (pkgs) changelog-d;
|
||||
internalApiDocs = true;
|
||||
boehmgc = pkgs.boehmgc-nix;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
|
||||
releaseTools.coverageAnalysis {
|
||||
name = "nix-coverage-${version}";
|
||||
|
||||
src = nixSrc;
|
||||
|
||||
configureFlags = testConfigureFlags;
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps ++ propagatedDeps ++ awsDeps ++ checkDeps;
|
||||
|
||||
dontInstall = false;
|
||||
|
||||
doInstallCheck = true;
|
||||
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
|
||||
|
||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||
|
||||
hardeningDisable = ["fortify"];
|
||||
|
||||
NIX_CFLAGS_COMPILE = "-DCOVERAGE=1";
|
||||
};
|
||||
in
|
||||
nix.overrideAttrs (prev: {
|
||||
# This Hydra job is just for the internal API docs.
|
||||
# We don't need the build artifacts here.
|
||||
|
||||
# API docs for Nix's unstable internal C++ interfaces.
|
||||
internal-api-docs =
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
with commonDeps { inherit pkgs; };
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "nix-internal-api-docs";
|
||||
inherit version;
|
||||
|
||||
src = nixSrc;
|
||||
|
||||
configureFlags = testConfigureFlags ++ internalApiDocsConfigureFlags;
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps ++ propagatedDeps
|
||||
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
|
||||
|
||||
dontBuild = true;
|
||||
doCheck = false;
|
||||
doInstallCheck = false;
|
||||
});
|
||||
|
||||
installTargets = [ "internal-api-html" ];
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
|
||||
# System tests.
|
||||
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
|
||||
tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix;
|
||||
|
||||
# Make sure that nix-env still produces the exact same result
|
||||
# on a particular version of Nixpkgs.
|
||||
evalNixpkgs =
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
runCommand "eval-nixos" { buildInputs = [ nix ]; }
|
||||
''
|
||||
type -p nix-env
|
||||
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
|
||||
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
|
||||
[[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
|
||||
mkdir $out
|
||||
'';
|
||||
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix;
|
||||
|
||||
nixpkgsLibTests =
|
||||
forAllSystems (system:
|
||||
import (nixpkgs + "/lib/tests/release.nix")
|
||||
{ pkgs = nixpkgsFor.${system}.native;
|
||||
nixVersions = [ self.packages.${system}.nix ];
|
||||
}
|
||||
);
|
||||
tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix;
|
||||
|
||||
tests.nix-copy = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy.nix;
|
||||
|
||||
tests.nssPreload = runNixOSTestFor "x86_64-linux" ./tests/nixos/nss-preload.nix;
|
||||
|
||||
tests.githubFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/github-flakes.nix;
|
||||
|
||||
tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix;
|
||||
|
||||
tests.tarballFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/tarball-flakes.nix;
|
||||
|
||||
tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix;
|
||||
|
||||
tests.setuid = lib.genAttrs
|
||||
["i686-linux" "x86_64-linux"]
|
||||
(system: runNixOSTestFor system ./tests/nixos/setuid.nix);
|
||||
|
||||
|
||||
# Make sure that nix-env still produces the exact same result
|
||||
# on a particular version of Nixpkgs.
|
||||
tests.evalNixpkgs =
|
||||
with nixpkgsFor.x86_64-linux.native;
|
||||
runCommand "eval-nixos" { buildInputs = [ nix ]; }
|
||||
''
|
||||
type -p nix-env
|
||||
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
|
||||
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
|
||||
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
|
||||
mkdir $out
|
||||
'';
|
||||
|
||||
tests.nixpkgsLibTests =
|
||||
forAllSystems (system:
|
||||
import (nixpkgs + "/lib/tests/release.nix")
|
||||
{ pkgs = nixpkgsFor.${system}.native;
|
||||
nixVersions = [ self.packages.${system}.nix ];
|
||||
}
|
||||
);
|
||||
|
||||
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
|
||||
pkgs = nixpkgsFor.x86_64-linux.native;
|
||||
nixpkgs = nixpkgs-regression;
|
||||
};
|
||||
|
||||
installTests = forAllSystems (system:
|
||||
@@ -354,6 +674,12 @@
|
||||
# `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work
|
||||
# againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable;
|
||||
} "touch $out");
|
||||
|
||||
installerTests = import ./tests/installer {
|
||||
binaryTarballs = self.hydraJobs.binaryTarball;
|
||||
inherit nixpkgsFor;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
checks = forAllSystems (system: {
|
||||
@@ -361,11 +687,6 @@
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
installTests = self.hydraJobs.installTests.${system};
|
||||
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
||||
rl-next =
|
||||
let pkgs = nixpkgsFor.${system}.native;
|
||||
in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } ''
|
||||
LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out
|
||||
'';
|
||||
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
||||
dockerImage = self.hydraJobs.dockerImage.${system};
|
||||
});
|
||||
@@ -404,26 +725,27 @@
|
||||
|
||||
devShells = let
|
||||
makeShell = pkgs: stdenv:
|
||||
let
|
||||
nix = pkgs.callPackage ./package.nix {
|
||||
inherit stdenv versionSuffix fileset;
|
||||
boehmgc = pkgs.boehmgc-nix;
|
||||
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
|
||||
};
|
||||
in
|
||||
nix.overrideAttrs (prev: {
|
||||
nativeBuildInputs = prev.nativeBuildInputs
|
||||
++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear
|
||||
++ lib.optional
|
||||
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
|
||||
pkgs.buildPackages.clang-tools;
|
||||
with commonDeps { inherit pkgs; };
|
||||
stdenv.mkDerivation {
|
||||
name = "nix";
|
||||
|
||||
src = null;
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
strictDeps = false;
|
||||
nativeBuildInputs = nativeBuildDeps
|
||||
++ (lib.optionals stdenv.cc.isClang [ pkgs.bear pkgs.clang-tools ]);
|
||||
|
||||
shellHook = ''
|
||||
buildInputs = buildDeps ++ propagatedDeps
|
||||
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
|
||||
|
||||
configureFlags = configureFlags
|
||||
++ testConfigureFlags ++ internalApiDocsConfigureFlags;
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
shellHook =
|
||||
''
|
||||
PATH=$prefix/bin:$PATH
|
||||
unset PYTHONPATH
|
||||
export MANPATH=$out/share/man:$MANPATH
|
||||
@@ -431,7 +753,7 @@
|
||||
# Make bash completion work.
|
||||
XDG_DATA_DIRS+=:$out/share
|
||||
'';
|
||||
});
|
||||
};
|
||||
in
|
||||
forAllSystems (system:
|
||||
let
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
clean-files += Makefile.config
|
||||
|
||||
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch
|
||||
# Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers.
|
||||
ERROR_SWITCH_ENUM = -Werror=switch-enum
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
# Ensure that this bug is not present in the C++ toolchain we are using.
|
||||
#
|
||||
# URL for bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431
|
||||
#
|
||||
# The test program is from that issue, with only a slight modification
|
||||
# to set an exit status instead of printing strings.
|
||||
AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
|
||||
[
|
||||
AC_MSG_CHECKING([that GCC bug 80431 is fixed])
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <cstdio>
|
||||
|
||||
static bool a = true;
|
||||
static bool b = true;
|
||||
|
||||
struct Options { };
|
||||
|
||||
struct Option
|
||||
{
|
||||
Option(Options * options)
|
||||
{
|
||||
a = false;
|
||||
}
|
||||
|
||||
~Option()
|
||||
{
|
||||
b = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct MyOptions : Options { };
|
||||
|
||||
struct MyOptions2 : virtual MyOptions
|
||||
{
|
||||
Option foo{this};
|
||||
};
|
||||
]],
|
||||
[[
|
||||
{
|
||||
MyOptions2 opts;
|
||||
}
|
||||
return (a << 1) | b;
|
||||
]])],
|
||||
[status_80431=0],
|
||||
[status_80431=$?],
|
||||
[
|
||||
# Assume we're bug-free when cross-compiling
|
||||
])
|
||||
AC_LANG_POP(C++)
|
||||
AS_CASE([$status_80431],
|
||||
[0],[
|
||||
AC_MSG_RESULT(yes)
|
||||
],
|
||||
[2],[
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_ERROR(Cannot build Nix with C++ compiler with this bug)
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT(unexpected result $status_80431: not expected failure with bug, ignoring)
|
||||
])
|
||||
])
|
||||
@@ -1,179 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash ../shell.nix -I nixpkgs=channel:nixos-unstable-small
|
||||
# ^^^^^^^
|
||||
# Only used for bash. shell.nix goes to the flake.
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
|
||||
# This does double duty for
|
||||
# - including rl-next
|
||||
# - marking where to insert new links (right after)
|
||||
SUMMARY_MARKER_LINE='{{#include ./SUMMARY-rl-next.md}}'
|
||||
|
||||
# --- LIB ---
|
||||
|
||||
log() {
|
||||
echo 1>&2 "release-notes:" "$@"
|
||||
}
|
||||
logcmd() {
|
||||
local cmd="$1"
|
||||
shift
|
||||
logcmd2 "$cmd" "${*@Q}" "$cmd" "$@"
|
||||
}
|
||||
logcmd2() {
|
||||
local fakecmd="$1"
|
||||
local fakeargs="$2"
|
||||
shift
|
||||
shift
|
||||
printf 1>&2 "release-notes: \033[34;1m$fakecmd\033[0m "
|
||||
echo "$fakeargs" 1>&2
|
||||
"$@"
|
||||
}
|
||||
die() {
|
||||
# ANSI red
|
||||
printf 1>&2 "release-notes: \033[31;1merror:\033[0m"
|
||||
echo 1>&2 "" "$@"
|
||||
exit 1
|
||||
}
|
||||
confirm() {
|
||||
local answer
|
||||
echo 1>&2 "$@" "[y/n]"
|
||||
read -r answer
|
||||
case "$answer" in
|
||||
y|Y|yes|Yes|YES)
|
||||
return 0
|
||||
;;
|
||||
n|N|no|No|NO)
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
echo 1>&2 "please answer y or n"
|
||||
confirm "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
report_done() {
|
||||
logcmd2 "git" "show" git -c pager.show=false show
|
||||
printf 1>&2 "release-notes: \033[32;1mdone\033[0m\n"
|
||||
}
|
||||
|
||||
# --- PARSE ARGS ---
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
die "Release notes takes no arguments, but make sure to set VERSION."
|
||||
fi
|
||||
|
||||
# --- CHECKS ---
|
||||
|
||||
if [[ ! -e flake.nix ]] || [[ ! -e .git ]]; then
|
||||
die "must run in repo root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# repo must be clean
|
||||
if ! git diff --quiet; then
|
||||
die "repo is dirty, please commit or stash changes"
|
||||
fi
|
||||
|
||||
if ! git diff --quiet --cached; then
|
||||
die "repo has staged changes, please commit or stash them"
|
||||
fi
|
||||
|
||||
if ! grep "$SUMMARY_MARKER_LINE" doc/manual/src/SUMMARY.md.in >/dev/null; then
|
||||
# would have been nice to catch this early, but won't be worth the extra infra
|
||||
die "SUMMARY.md.in is missing the marker line '$SUMMARY_MARKER_LINE', which would be used for inserting a new release notes page. Please fix the script."
|
||||
fi
|
||||
|
||||
if [[ ! -n "${VERSION:-}" ]]; then
|
||||
die "please set the VERSION environment variable before invoking this script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# version_major_minor: MAJOR.MINOR
|
||||
# version_full: MAJOR.MINOR.PATCH
|
||||
# IS_PATCH: true if this is a patch release; append instead of create
|
||||
if grep -E '^[0-9]+\.[0-9]+$' <<< "$VERSION" >/dev/null; then
|
||||
log 'is minor'
|
||||
IS_PATCH=false
|
||||
version_full="$VERSION.0"
|
||||
version_major_minor="$VERSION"
|
||||
elif grep -E '^[0-9]+\.[0-9]+\.0$' <<< "$VERSION" >/dev/null; then
|
||||
log 'is minor (.0)'
|
||||
IS_PATCH=false
|
||||
version_full="$VERSION"
|
||||
version_major_minor="$(echo "$VERSION" | sed -e 's/\.0$//')"
|
||||
elif grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' <<< "$VERSION" >/dev/null; then
|
||||
log 'is patch'
|
||||
IS_PATCH=true
|
||||
version_full="$VERSION"
|
||||
version_major_minor="$(echo "$VERSION" | sed -e 's/\.[0-9]*$//')"
|
||||
else
|
||||
die "VERSION must be MAJOR.MINOR[.PATCH], where each is a number, e.g. 2.20 or 2.20.1 (VERSION was set to $VERSION)"
|
||||
fi
|
||||
|
||||
unset VERSION
|
||||
|
||||
log "version_major_minor=$version_major_minor"
|
||||
log "version_full=$version_full"
|
||||
log "IS_PATCH=$IS_PATCH"
|
||||
|
||||
basename=rl-${version_major_minor}.md
|
||||
file=doc/manual/src/release-notes/$basename
|
||||
|
||||
if ! $IS_PATCH; then
|
||||
if [[ -e $file ]]; then
|
||||
die "release notes file $file already exists. If you'd like to make a minor release, pass a patch version, e.g. 2.20.1"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- DEFAULTS ---
|
||||
|
||||
if [[ ! -n "${DATE:-}" ]]; then
|
||||
DATE="$(date +%Y-%m-%d)"
|
||||
log "DATE not set, using $DATE"
|
||||
fi
|
||||
|
||||
case "$DATE" in
|
||||
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])
|
||||
;;
|
||||
*)
|
||||
die "DATE must be YYYY-MM-DD, e.g. 2021-12-31 (DATE was set to $DATE)"
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- DO THE WORK ---
|
||||
|
||||
# menu
|
||||
title="Release $version_major_minor ($DATE)"
|
||||
# section on page
|
||||
section_title="Release $version_full ($DATE)"
|
||||
|
||||
(
|
||||
# TODO add minor number, and append?
|
||||
echo "# $section_title"
|
||||
echo
|
||||
changelog-d doc/manual/rl-next | sed -e 's/ *$//'
|
||||
) | tee -a $file
|
||||
|
||||
log "Wrote $file"
|
||||
|
||||
if ! $IS_PATCH; then
|
||||
NEW_SUMMARY_LINE=" - [$title](release-notes/$basename)"
|
||||
|
||||
# find the marker line, insert new link after it
|
||||
escaped_marker="$(echo "$SUMMARY_MARKER_LINE" | sed -e 's/\//\\\//g' -e 's/ /\\ /g')"
|
||||
escaped_line="$(echo "$NEW_SUMMARY_LINE" | sed -e 's/\//\\\//g' -e 's/ /\\ /g')"
|
||||
logcmd sed -i -e "/$escaped_marker/a $escaped_line" doc/manual/src/SUMMARY.md.in
|
||||
fi
|
||||
|
||||
for f in doc/manual/rl-next/*.md; do
|
||||
if [[ config != "$(basename $f)" ]]; then
|
||||
logcmd git rm $f
|
||||
fi
|
||||
done
|
||||
|
||||
logcmd git add $file doc/manual/src/SUMMARY.md.in
|
||||
logcmd git status
|
||||
logcmd git commit -m "release notes: $version_full"
|
||||
|
||||
report_done
|
||||
@@ -24,23 +24,34 @@ release:
|
||||
* In a checkout of the Nix repo, make sure you're on `master` and run
|
||||
`git pull`.
|
||||
|
||||
* Compile the release notes by running
|
||||
* Move the contents of `doc/manual/src/release-notes/rl-next.md`
|
||||
(except the first line) to
|
||||
`doc/manual/src/release-notes/rl-$VERSION.md` (where `$VERSION` is
|
||||
the contents of `.version` *without* the patch level, e.g. `2.12`
|
||||
rather than `2.12.0`).
|
||||
|
||||
* Add a header to `doc/manual/src/release-notes/rl-$VERSION.md` like
|
||||
|
||||
```
|
||||
# Release 2.12 (2022-12-06)
|
||||
```
|
||||
|
||||
* Proof-read / edit / rearrange the release notes. Breaking changes
|
||||
and highlights should go to the top.
|
||||
|
||||
* Add a link to the release notes to `doc/manual/src/SUMMARY.md.in`
|
||||
(*not* `SUMMARY.md`), e.g.
|
||||
|
||||
```
|
||||
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
|
||||
```
|
||||
|
||||
* Run
|
||||
|
||||
```console
|
||||
$ git checkout -b release-notes
|
||||
$ VERSION=X.YY ./maintainers/release-notes
|
||||
```
|
||||
|
||||
where `X.YY` is *without* the patch level, e.g. `2.12` rather than ~~`2.12.0`~~.
|
||||
|
||||
A commit is created.
|
||||
|
||||
* Proof-read / edit / rearrange the release notes if needed. Breaking changes
|
||||
and highlights should go to the top.
|
||||
|
||||
* Push.
|
||||
|
||||
```console
|
||||
$ git add doc/manual/src/release-notes/rl-$VERSION.md
|
||||
$ git commit -a -m 'Release notes'
|
||||
$ git push --set-upstream $REMOTE release-notes
|
||||
```
|
||||
|
||||
@@ -56,17 +67,15 @@ release:
|
||||
$ git checkout -b $VERSION-maintenance
|
||||
```
|
||||
|
||||
* Mark the release as official:
|
||||
* Mark the release as stable:
|
||||
|
||||
```console
|
||||
$ sed -e 's/officialRelease = false;/officialRelease = true;/' -i flake.nix
|
||||
$ git cherry-pick f673551e71942a52b6d7ae66af8b67140904a76a
|
||||
```
|
||||
|
||||
This removes the link to `rl-next.md` from the manual and sets
|
||||
`officialRelease = true` in `flake.nix`.
|
||||
|
||||
* Commit
|
||||
|
||||
* Push the release branch:
|
||||
|
||||
```console
|
||||
@@ -150,30 +159,6 @@ release:
|
||||
|
||||
## Creating a point release
|
||||
|
||||
* Checkout.
|
||||
|
||||
```console
|
||||
$ git checkout XX.YY-maintenance
|
||||
```
|
||||
|
||||
* Determine the next patch version.
|
||||
|
||||
```console
|
||||
$ export VERSION=XX.YY.ZZ
|
||||
```
|
||||
|
||||
* Update release notes.
|
||||
|
||||
```console
|
||||
$ ./maintainers/release-notes
|
||||
```
|
||||
|
||||
* Push.
|
||||
|
||||
```console
|
||||
$ git push
|
||||
```
|
||||
|
||||
* Wait for the desired evaluation of the maintenance jobset to finish
|
||||
building.
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{ mkDerivation, aeson, base, bytestring, cabal-install-parsers
|
||||
, Cabal-syntax, containers, directory, filepath, frontmatter
|
||||
, generic-lens-lite, lib, mtl, optparse-applicative, parsec, pretty
|
||||
, regex-applicative, text, pkgs
|
||||
}:
|
||||
let rev = "f30f6969e9cd8b56242309639d58acea21c99d06";
|
||||
in
|
||||
mkDerivation {
|
||||
pname = "changelog-d";
|
||||
version = "0.1";
|
||||
src = pkgs.fetchurl {
|
||||
name = "changelog-d-${rev}.tar.gz";
|
||||
url = "https://codeberg.org/roberth/changelog-d/archive/${rev}.tar.gz";
|
||||
hash = "sha256-8a2+i5u7YoszAgd5OIEW0eYUcP8yfhtoOIhLJkylYJ4=";
|
||||
} // { inherit rev; };
|
||||
isLibrary = false;
|
||||
isExecutable = true;
|
||||
libraryHaskellDepends = [
|
||||
aeson base bytestring cabal-install-parsers Cabal-syntax containers
|
||||
directory filepath frontmatter generic-lens-lite mtl parsec pretty
|
||||
regex-applicative text
|
||||
];
|
||||
executableHaskellDepends = [
|
||||
base bytestring Cabal-syntax directory filepath
|
||||
optparse-applicative
|
||||
];
|
||||
doHaddock = false;
|
||||
description = "Concatenate changelog entries into a single one";
|
||||
license = lib.licenses.gpl3Plus;
|
||||
mainProgram = "changelog-d";
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
# Taken temporarily from <nixpkgs/pkgs/by-name/ch/changelog-d/package.nix>
|
||||
{
|
||||
callPackage,
|
||||
lib,
|
||||
haskell,
|
||||
haskellPackages,
|
||||
}:
|
||||
|
||||
let
|
||||
hsPkg = haskellPackages.callPackage ./changelog-d.cabal.nix { };
|
||||
|
||||
addCompletions = haskellPackages.generateOptparseApplicativeCompletions ["changelog-d"];
|
||||
|
||||
haskellModifications =
|
||||
lib.flip lib.pipe [
|
||||
addCompletions
|
||||
haskell.lib.justStaticExecutables
|
||||
];
|
||||
|
||||
mkDerivationOverrides = finalAttrs: oldAttrs: {
|
||||
|
||||
version = oldAttrs.version + "-git-${lib.strings.substring 0 7 oldAttrs.src.rev}";
|
||||
|
||||
meta = oldAttrs.meta // {
|
||||
homepage = "https://codeberg.org/roberth/changelog-d";
|
||||
maintainers = [ lib.maintainers.roberth ];
|
||||
};
|
||||
|
||||
};
|
||||
in
|
||||
(haskellModifications hsPkg).overrideAttrs mkDerivationOverrides
|
||||
@@ -1,10 +0,0 @@
|
||||
# Initialise support for build directories.
|
||||
builddir ?=
|
||||
|
||||
ifdef builddir
|
||||
buildprefix = $(builddir)/
|
||||
buildprefixrel = $(builddir)
|
||||
else
|
||||
buildprefix =
|
||||
buildprefixrel = .
|
||||
endif
|
||||
@@ -1,11 +0,0 @@
|
||||
# Default installation paths.
|
||||
prefix ?= /usr/local
|
||||
libdir ?= $(prefix)/lib
|
||||
bindir ?= $(prefix)/bin
|
||||
libexecdir ?= $(prefix)/libexec
|
||||
datadir ?= $(prefix)/share
|
||||
localstatedir ?= $(prefix)/var
|
||||
sysconfdir ?= $(prefix)/etc
|
||||
mandir ?= $(prefix)/share/man
|
||||
|
||||
DESTDIR ?=
|
||||
@@ -43,6 +43,27 @@ define newline
|
||||
endef
|
||||
|
||||
|
||||
# Default installation paths.
|
||||
prefix ?= /usr/local
|
||||
libdir ?= $(prefix)/lib
|
||||
bindir ?= $(prefix)/bin
|
||||
libexecdir ?= $(prefix)/libexec
|
||||
datadir ?= $(prefix)/share
|
||||
localstatedir ?= $(prefix)/var
|
||||
sysconfdir ?= $(prefix)/etc
|
||||
mandir ?= $(prefix)/share/man
|
||||
|
||||
|
||||
# Initialise support for build directories.
|
||||
builddir ?=
|
||||
|
||||
ifdef builddir
|
||||
buildprefix = $(builddir)/
|
||||
else
|
||||
buildprefix =
|
||||
endif
|
||||
|
||||
|
||||
# Pass -fPIC if we're building dynamic libraries.
|
||||
BUILD_SHARED_LIBS ?= 1
|
||||
|
||||
@@ -73,8 +94,6 @@ ifeq ($(BUILD_DEBUG), 1)
|
||||
endif
|
||||
|
||||
|
||||
include mk/build-dir.mk
|
||||
include mk/install-dirs.mk
|
||||
include mk/functions.mk
|
||||
include mk/tracing.mk
|
||||
include mk/clean.mk
|
||||
@@ -93,7 +112,7 @@ define include-sub-makefile
|
||||
include $(1)
|
||||
endef
|
||||
|
||||
$(foreach mf, $(makefiles), $(eval $(call include-sub-makefile,$(mf))))
|
||||
$(foreach mf, $(makefiles), $(eval $(call include-sub-makefile, $(mf))))
|
||||
|
||||
|
||||
# Instantiate stuff.
|
||||
@@ -113,10 +132,6 @@ $(foreach test-group, $(install-tests-groups), \
|
||||
$(eval $(call run-install-test,$(test))) \
|
||||
$(eval $(test-group).test-group: $(test).test)))
|
||||
|
||||
# Include makefiles requiring built programs.
|
||||
$(foreach mf, $(makefiles-late), $(eval $(call include-sub-makefile,$(mf))))
|
||||
|
||||
|
||||
$(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file))))))
|
||||
|
||||
|
||||
|
||||
+1
-4
@@ -6,9 +6,6 @@ programs-list :=
|
||||
# - $(1)_NAME: the name of the program (e.g. ‘foo’); defaults to
|
||||
# $(1).
|
||||
#
|
||||
# - $(1)_ENV: environment variables to set when running the program
|
||||
# from the Makefile using the $(1)_RUN target.
|
||||
#
|
||||
# - $(1)_DIR: the directory where the (non-installed) program will be
|
||||
# placed.
|
||||
#
|
||||
@@ -90,6 +87,6 @@ define build-program
|
||||
# Phony target to run this program (typically as a dependency of 'check').
|
||||
.PHONY: $(1)_RUN
|
||||
$(1)_RUN: $$($(1)_PATH)
|
||||
$(trace-test) $$($(1)_ENV) $$($(1)_PATH)
|
||||
$(trace-test) $$($(1)_PATH)
|
||||
|
||||
endef
|
||||
|
||||
+4
-4
@@ -10,10 +10,10 @@ endef
|
||||
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
|
||||
$(buildprefix)%.h: %.h.in
|
||||
$(trace-gen) rm -f $@ && cd $(buildprefixrel) && ./config.status --quiet --header=$(@:$(buildprefix)%=%)
|
||||
%.h: %.h.in
|
||||
$(trace-gen) rm -f $@ && ./config.status --quiet --header=$@
|
||||
|
||||
$(buildprefix)%: %.in
|
||||
$(trace-gen) rm -f $@ && cd $(buildprefixrel) && ./config.status --quiet --file=$(@:$(buildprefix)%=%)
|
||||
%: %.in
|
||||
$(trace-gen) rm -f $@ && ./config.status --quiet --file=$@
|
||||
|
||||
endif
|
||||
|
||||
-260
@@ -1,260 +0,0 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
stdenv,
|
||||
autoconf-archive,
|
||||
autoreconfHook,
|
||||
aws-sdk-cpp,
|
||||
boehmgc,
|
||||
nlohmann_json,
|
||||
bison,
|
||||
changelog-d,
|
||||
boost,
|
||||
brotli,
|
||||
bzip2,
|
||||
curl,
|
||||
doxygen,
|
||||
editline,
|
||||
fileset,
|
||||
flex,
|
||||
git,
|
||||
gtest,
|
||||
jq,
|
||||
libarchive,
|
||||
libcpuid,
|
||||
libseccomp,
|
||||
libsodium,
|
||||
lowdown,
|
||||
mdbook,
|
||||
mdbook-linkcheck,
|
||||
mercurial,
|
||||
openssl,
|
||||
pkg-config,
|
||||
rapidcheck,
|
||||
sqlite,
|
||||
util-linuxMinimal ? utillinuxMinimal,
|
||||
utillinuxMinimal ? null,
|
||||
xz,
|
||||
|
||||
busybox-sandbox-shell,
|
||||
|
||||
pname ? "nix",
|
||||
versionSuffix ? "",
|
||||
officialRelease ? true,
|
||||
# Set to true to build the release notes for the next release.
|
||||
buildUnreleasedNotes ? false,
|
||||
internalApiDocs ? false,
|
||||
|
||||
# Not a real argument, just the only way to approximate let-binding some
|
||||
# stuff for argument defaults.
|
||||
__forDefaults ? {
|
||||
canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
|
||||
},
|
||||
}: let
|
||||
inherit (__forDefaults) canRunInstalled;
|
||||
|
||||
version = lib.fileContents ./.version + versionSuffix;
|
||||
|
||||
aws-sdk-cpp-nix = aws-sdk-cpp.override {
|
||||
apis = [ "s3" "transfer" ];
|
||||
customMemoryManagement = false;
|
||||
};
|
||||
|
||||
testConfigureFlags = [
|
||||
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
|
||||
];
|
||||
|
||||
# The internal API docs need these for the build, but if we're not building
|
||||
# Nix itself, then these don't need to be propagated.
|
||||
maybePropagatedInputs = [
|
||||
boehmgc
|
||||
nlohmann_json
|
||||
];
|
||||
|
||||
# .gitignore has already been processed, so any changes in it are irrelevant
|
||||
# at this point. It is not represented verbatim for test purposes because
|
||||
# that would interfere with repo semantics.
|
||||
baseFiles = fileset.fileFilter (f: f.name != ".gitignore") ./.;
|
||||
|
||||
configureFiles = fileset.unions [
|
||||
./.version
|
||||
./configure.ac
|
||||
./m4
|
||||
# TODO: do we really need README.md? It doesn't seem used in the build.
|
||||
./README.md
|
||||
];
|
||||
|
||||
topLevelBuildFiles = fileset.unions [
|
||||
./local.mk
|
||||
./Makefile
|
||||
./Makefile.config.in
|
||||
./mk
|
||||
];
|
||||
|
||||
functionalTestFiles = fileset.unions [
|
||||
./tests/functional
|
||||
./tests/unit
|
||||
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
|
||||
];
|
||||
|
||||
in stdenv.mkDerivation (finalAttrs: {
|
||||
inherit pname version;
|
||||
|
||||
src = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.intersection baseFiles (fileset.unions ([
|
||||
configureFiles
|
||||
topLevelBuildFiles
|
||||
functionalTestFiles
|
||||
] ++ lib.optionals (!finalAttrs.dontBuild || internalApiDocs) [
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
./doc
|
||||
./misc
|
||||
./precompiled-headers.h
|
||||
./src
|
||||
./COPYING
|
||||
./scripts/local.mk
|
||||
]));
|
||||
};
|
||||
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
outputs = [ "out" ]
|
||||
++ lib.optionals (!finalAttrs.dontBuild) [ "dev" "doc" ];
|
||||
|
||||
dontBuild = false;
|
||||
|
||||
nativeBuildInputs = [
|
||||
bison
|
||||
flex
|
||||
] ++ [
|
||||
(lib.getBin lowdown)
|
||||
mdbook
|
||||
mdbook-linkcheck
|
||||
autoconf-archive
|
||||
autoreconfHook
|
||||
pkg-config
|
||||
|
||||
# Tests
|
||||
git
|
||||
mercurial
|
||||
jq
|
||||
] ++ lib.optional stdenv.hostPlatform.isLinux util-linuxMinimal
|
||||
++ lib.optional (!officialRelease && buildUnreleasedNotes) changelog-d
|
||||
++ lib.optional internalApiDocs doxygen
|
||||
;
|
||||
|
||||
buildInputs = [
|
||||
curl
|
||||
bzip2
|
||||
xz
|
||||
brotli
|
||||
editline
|
||||
openssl
|
||||
sqlite
|
||||
libarchive
|
||||
boost
|
||||
lowdown
|
||||
libsodium
|
||||
]
|
||||
++ lib.optionals stdenv.isLinux [ libseccomp ]
|
||||
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
|
||||
# There have been issues building these dependencies
|
||||
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform) aws-sdk-cpp-nix
|
||||
++ lib.optionals (finalAttrs.dontBuild) maybePropagatedInputs
|
||||
;
|
||||
|
||||
checkInputs = [
|
||||
gtest
|
||||
rapidcheck
|
||||
];
|
||||
|
||||
propagatedBuildInputs = lib.optionals (!finalAttrs.dontBuild) maybePropagatedInputs;
|
||||
|
||||
disallowedReferences = [
|
||||
boost
|
||||
];
|
||||
|
||||
preConfigure = lib.optionalString (!finalAttrs.dontBuild && !stdenv.hostPlatform.isStatic) ''
|
||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||
# https://github.com/NixOS/nixpkgs/issues/45462
|
||||
mkdir -p $out/lib
|
||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||
rm -f $out/lib/*.a
|
||||
'' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isLinux) ''
|
||||
chmod u+w $out/lib/*.so.*
|
||||
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
'' + lib.optionalString (!finalAttrs.dontBuild && stdenv.hostPlatform.isDarwin) ''
|
||||
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
|
||||
'' + ''
|
||||
# Workaround https://github.com/NixOS/nixpkgs/issues/294890.
|
||||
if [[ -n "''${doCheck:-}" ]]; then
|
||||
appendToVar configureFlags "--enable-tests"
|
||||
else
|
||||
appendToVar configureFlags "--disable-tests"
|
||||
fi
|
||||
'';
|
||||
|
||||
configureFlags = lib.optionals stdenv.isLinux [
|
||||
"--with-boost=${boost}/lib"
|
||||
"--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox"
|
||||
] ++ lib.optionals (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) [
|
||||
"LDFLAGS=-fuse-ld=gold"
|
||||
] ++ [ "--sysconfdir=/etc" ]
|
||||
++ lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell"
|
||||
++ lib.optionals (finalAttrs.doCheck || internalApiDocs) testConfigureFlags
|
||||
++ lib.optional (!canRunInstalled) "--disable-doc-gen"
|
||||
++ [ (lib.enableFeature internalApiDocs "internal-api-docs") ]
|
||||
;
|
||||
|
||||
installTargets = lib.optional internalApiDocs "internal-api-html";
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1";
|
||||
|
||||
doCheck = true;
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
postInstall = lib.optionalString (!finalAttrs.dontBuild) ''
|
||||
mkdir -p $doc/nix-support
|
||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||
'' + lib.optionalString stdenv.hostPlatform.isStatic ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||
'' + lib.optionalString stdenv.isDarwin ''
|
||||
install_name_tool \
|
||||
-change ${boost}/lib/libboost_context.dylib \
|
||||
$out/lib/libboost_context.dylib \
|
||||
$out/lib/libnixutil.dylib
|
||||
'' + lib.optionalString internalApiDocs ''
|
||||
mkdir -p $out/nix-support
|
||||
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> "$out/nix-support/hydra-build-products"
|
||||
'';
|
||||
|
||||
doInstallCheck = finalAttrs.doCheck;
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
|
||||
|
||||
preInstallCheck = lib.optionalString stdenv.hostPlatform.isDarwin ''
|
||||
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
||||
'';
|
||||
|
||||
separateDebugInfo = !stdenv.hostPlatform.isStatic && !finalAttrs.dontBuild;
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
meta.platforms = lib.platforms.unix;
|
||||
|
||||
passthru.perl-bindings = pkgs.callPackage ./perl {
|
||||
inherit fileset stdenv;
|
||||
};
|
||||
})
|
||||
+1
-7
@@ -1,12 +1,6 @@
|
||||
makefiles = local.mk
|
||||
|
||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a
|
||||
|
||||
# A convenience for concurrent development of Nix and its Perl bindings.
|
||||
# Not needed in a standalone build of the Perl bindings.
|
||||
ifneq ("$(wildcard ../src)", "")
|
||||
GLOBAL_CXXFLAGS += -I ../src
|
||||
endif
|
||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src
|
||||
|
||||
-include Makefile.config
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
{ lib, fileset
|
||||
, stdenv
|
||||
, perl, perlPackages
|
||||
, autoconf-archive, autoreconfHook, pkg-config
|
||||
, nix, curl, bzip2, xz, boost, libsodium, darwin
|
||||
}:
|
||||
|
||||
perl.pkgs.toPerlModule (stdenv.mkDerivation {
|
||||
name = "nix-perl-${nix.version}";
|
||||
|
||||
src = fileset.toSource {
|
||||
root = ../.;
|
||||
fileset = fileset.unions [
|
||||
../.version
|
||||
../m4
|
||||
../mk
|
||||
./MANIFEST
|
||||
./Makefile
|
||||
./Makefile.config.in
|
||||
./configure.ac
|
||||
./lib
|
||||
./local.mk
|
||||
];
|
||||
};
|
||||
|
||||
nativeBuildInputs =
|
||||
[ autoconf-archive
|
||||
autoreconfHook
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs =
|
||||
[ nix
|
||||
curl
|
||||
bzip2
|
||||
xz
|
||||
perl
|
||||
boost
|
||||
]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||
|
||||
configureFlags = [
|
||||
"--with-dbi=${perlPackages.DBI}/${perl.libPrefix}"
|
||||
"--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}"
|
||||
];
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
postUnpack = "sourceRoot=$sourceRoot/perl";
|
||||
})
|
||||
@@ -3,13 +3,11 @@
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# System specific settings
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}"
|
||||
export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||
|
||||
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
# create by default; set 0 to DIY, use a symlink, etc.
|
||||
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
|
||||
NIX_FIRST_BUILD_UID="301"
|
||||
NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||
|
||||
# caution: may update times on / if not run as normal non-root user
|
||||
read_only_root() {
|
||||
@@ -102,7 +100,7 @@ poly_extra_try_me_commands() {
|
||||
poly_configure_nix_daemon_service() {
|
||||
task "Setting up the nix-daemon LaunchDaemon"
|
||||
_sudo "to set up the nix-daemon as a LaunchDaemon" \
|
||||
/usr/bin/install -m "u=rw,go=r" "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
|
||||
/usr/bin/install -m -rw-r--r-- "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
|
||||
|
||||
_sudo "to load the LaunchDaemon plist for nix-daemon" \
|
||||
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||
|
||||
@@ -25,9 +25,9 @@ readonly RED='\033[31m'
|
||||
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
|
||||
readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
|
||||
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
||||
# each system specific installer must set these:
|
||||
# NIX_FIRST_BUILD_UID
|
||||
# NIX_BUILD_USER_NAME_TEMPLATE
|
||||
# darwin installer needs to override these
|
||||
NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
|
||||
NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||
# Please don't change this. We don't support it, because the
|
||||
# default shell profile that comes with Nix doesn't support it.
|
||||
readonly NIX_ROOT="/nix"
|
||||
@@ -699,12 +699,6 @@ EOF
|
||||
fi
|
||||
}
|
||||
|
||||
check_required_system_specific_settings() {
|
||||
if [ -z "${NIX_FIRST_BUILD_UID+x}" ] || [ -z "${NIX_BUILD_USER_NAME_TEMPLATE+x}" ]; then
|
||||
failure "Internal error: System specific installer for $(uname) ($1) does not export required settings."
|
||||
fi
|
||||
}
|
||||
|
||||
welcome_to_nix() {
|
||||
local -r NIX_UID_RANGES="${NIX_FIRST_BUILD_UID}..$((NIX_FIRST_BUILD_UID + NIX_USER_COUNT - 1))"
|
||||
local -r RANGE_TEXT=$(echo -ne "${BLUE}(uids [${NIX_UID_RANGES}])${ESC}")
|
||||
@@ -724,9 +718,7 @@ manager. This will happen in a few stages:
|
||||
if you are ready to continue.
|
||||
|
||||
3. Create the system users ${RANGE_TEXT} and groups ${GROUP_TEXT}
|
||||
that the Nix daemon uses to run builds. To create system users
|
||||
in a different range, exit and run this tool again with
|
||||
NIX_FIRST_BUILD_UID set.
|
||||
that the Nix daemon uses to run builds.
|
||||
|
||||
4. Perform the basic installation of the Nix files daemon.
|
||||
|
||||
@@ -968,16 +960,13 @@ main() {
|
||||
if is_os_darwin; then
|
||||
# shellcheck source=./install-darwin-multi-user.sh
|
||||
. "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
|
||||
check_required_system_specific_settings "install-darwin-multi-user.sh"
|
||||
elif is_os_linux; then
|
||||
# shellcheck source=./install-systemd-multi-user.sh
|
||||
. "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" # most of this works on non-systemd distros also
|
||||
check_required_system_specific_settings "install-systemd-multi-user.sh"
|
||||
else
|
||||
failure "Sorry, I don't know what to do on $(uname)"
|
||||
fi
|
||||
|
||||
|
||||
welcome_to_nix
|
||||
|
||||
if ! is_root; then
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
# System specific settings
|
||||
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
|
||||
export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||
|
||||
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
|
||||
readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
|
||||
|
||||
|
||||
@@ -19,14 +19,6 @@ set __ETC_PROFILE_NIX_SOURCED 1
|
||||
|
||||
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if test -z "$XDG_DATA_DIRS"
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:/nix/var/nix/profiles/default/share"
|
||||
end
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if test -n "$NIX_SSH_CERT_FILE"
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
|
||||
@@ -30,14 +30,6 @@ fi
|
||||
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if [ -z "${XDG_DATA_DIRS-}" ]; then
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
fi
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
|
||||
@@ -20,14 +20,6 @@ if test -n "$HOME" && test -n "$USER"
|
||||
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
||||
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if test -z "$XDG_DATA_DIRS"
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
end
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if test -n "$NIX_SSH_CERT_FILE"
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
|
||||
@@ -32,14 +32,6 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||
|
||||
# Populate bash completions, .desktop files, etc
|
||||
if [ -z "${XDG_DATA_DIRS-}" ]; then
|
||||
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
|
||||
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
else
|
||||
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
|
||||
fi
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
@@ -137,8 +137,11 @@ static int main_build_remote(int argc, char * * argv)
|
||||
for (auto & m : machines) {
|
||||
debug("considering building on remote machine '%s'", m.storeUri);
|
||||
|
||||
if (m.enabled &&
|
||||
m.systemSupported(neededSystem) &&
|
||||
if (m.enabled
|
||||
&& (neededSystem == "builtin"
|
||||
|| std::find(m.systemTypes.begin(),
|
||||
m.systemTypes.end(),
|
||||
neededSystem) != m.systemTypes.end()) &&
|
||||
m.allSupported(requiredFeatures) &&
|
||||
m.mandatoryMet(requiredFeatures))
|
||||
{
|
||||
@@ -202,7 +205,7 @@ static int main_build_remote(int argc, char * * argv)
|
||||
else
|
||||
drvstr = "<unknown>";
|
||||
|
||||
auto error = HintFmt(errorText);
|
||||
auto error = hintformat(errorText);
|
||||
error
|
||||
% drvstr
|
||||
% neededSystem
|
||||
@@ -211,7 +214,7 @@ static int main_build_remote(int argc, char * * argv)
|
||||
|
||||
for (auto & m : machines)
|
||||
error
|
||||
% concatStringsSep<StringSet>(", ", m.systemTypes)
|
||||
% concatStringsSep<std::vector<std::string>>(", ", m.systemTypes)
|
||||
% m.maxJobs
|
||||
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
|
||||
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace nix {
|
||||
bool MY_TYPE ::operator COMPARATOR (const MY_TYPE & other) const \
|
||||
{ \
|
||||
const MY_TYPE* me = this; \
|
||||
auto fields1 = std::tie(*me->drvPath, me->FIELD); \
|
||||
auto fields1 = std::make_tuple<const CHILD_TYPE &, const FIELD_TYPE &>(*me->drvPath, me->FIELD); \
|
||||
me = &other; \
|
||||
auto fields2 = std::tie(*me->drvPath, me->FIELD); \
|
||||
auto fields2 = std::make_tuple<const CHILD_TYPE &, const FIELD_TYPE &>(*me->drvPath, me->FIELD); \
|
||||
return fields1 COMPARATOR fields2; \
|
||||
}
|
||||
#define CMP(CHILD_TYPE, MY_TYPE, FIELD) \
|
||||
|
||||
@@ -98,7 +98,7 @@ EvalCommand::EvalCommand()
|
||||
EvalCommand::~EvalCommand()
|
||||
{
|
||||
if (evalState)
|
||||
evalState->maybePrintStats();
|
||||
evalState->printStats();
|
||||
}
|
||||
|
||||
ref<Store> EvalCommand::getEvalStore()
|
||||
@@ -175,7 +175,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
|
||||
throw UsageError("'--all' does not expect arguments");
|
||||
// XXX: Only uses opaque paths, ignores all the realisations
|
||||
for (auto & p : store->queryAllValidPaths())
|
||||
paths.emplace_back(BuiltPath::Opaque{p});
|
||||
paths.push_back(BuiltPath::Opaque{p});
|
||||
} else {
|
||||
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
|
||||
if (recursive) {
|
||||
@@ -188,7 +188,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
|
||||
}
|
||||
store->computeFSClosure(pathsRoots, pathsClosure);
|
||||
for (auto & path : pathsClosure)
|
||||
paths.emplace_back(BuiltPath::Opaque{path});
|
||||
paths.push_back(BuiltPath::Opaque{path});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-66
@@ -34,28 +34,21 @@ struct NixMultiCommand : virtual MultiCommand, virtual Command
|
||||
// For the overloaded run methods
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
|
||||
|
||||
/**
|
||||
* A command that requires a \ref Store "Nix store".
|
||||
*/
|
||||
/* A command that requires a Nix store. */
|
||||
struct StoreCommand : virtual Command
|
||||
{
|
||||
StoreCommand();
|
||||
void run() override;
|
||||
ref<Store> getStore();
|
||||
virtual ref<Store> createStore();
|
||||
/**
|
||||
* Main entry point, with a `Store` provided
|
||||
*/
|
||||
virtual void run(ref<Store>) = 0;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Store> _store;
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that copies something between `--from` and `--to` \ref
|
||||
* Store stores.
|
||||
*/
|
||||
/* A command that copies something between `--from` and `--to`
|
||||
stores. */
|
||||
struct CopyCommand : virtual StoreCommand
|
||||
{
|
||||
std::string srcUri, dstUri;
|
||||
@@ -67,9 +60,6 @@ struct CopyCommand : virtual StoreCommand
|
||||
ref<Store> getDstStore();
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that needs to evaluate Nix language expressions.
|
||||
*/
|
||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
{
|
||||
bool startReplOnEvalErrors = false;
|
||||
@@ -89,10 +79,6 @@ private:
|
||||
std::shared_ptr<EvalState> evalState;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mixin class for commands that process flakes, adding a few standard
|
||||
* flake-related options/flags.
|
||||
*/
|
||||
struct MixFlakeOptions : virtual Args, EvalCommand
|
||||
{
|
||||
flake::LockFlags lockFlags;
|
||||
@@ -101,14 +87,6 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
||||
|
||||
MixFlakeOptions();
|
||||
|
||||
/**
|
||||
* The completion for some of these flags depends on the flake(s) in
|
||||
* question.
|
||||
*
|
||||
* This method should be implemented to gather all flakerefs the
|
||||
* command is operating with (presumably specified via some other
|
||||
* arguments) so that the completions for these flags can use them.
|
||||
*/
|
||||
virtual std::vector<std::string> getFlakesForCompletion()
|
||||
{ return {}; }
|
||||
|
||||
@@ -134,29 +112,15 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
|
||||
virtual Strings getDefaultFlakeAttrPathPrefixes();
|
||||
|
||||
/**
|
||||
* Complete an installable from the given prefix.
|
||||
*/
|
||||
void completeInstallable(std::string_view prefix);
|
||||
};
|
||||
|
||||
/**
|
||||
* A mixin class for commands that need a read-only flag.
|
||||
*
|
||||
* What exactly is "read-only" is unspecified, but it will usually be
|
||||
* the \ref Store "Nix store".
|
||||
*/
|
||||
struct MixReadOnlyOption : virtual Args
|
||||
{
|
||||
MixReadOnlyOption();
|
||||
};
|
||||
|
||||
/**
|
||||
* Like InstallablesCommand but the installables are not loaded.
|
||||
*
|
||||
* This is needed by `CmdRepl` which wants to load (and reload) the
|
||||
* installables itself.
|
||||
*/
|
||||
/* Like InstallablesCommand but the installables are not loaded */
|
||||
struct RawInstallablesCommand : virtual Args, SourceExprCommand
|
||||
{
|
||||
RawInstallablesCommand();
|
||||
@@ -165,7 +129,7 @@ struct RawInstallablesCommand : virtual Args, SourceExprCommand
|
||||
|
||||
void run(ref<Store> store) override;
|
||||
|
||||
// FIXME make const after `CmdRepl`'s override is fixed up
|
||||
// FIXME make const after CmdRepl's override is fixed up
|
||||
virtual void applyDefaultInstallables(std::vector<std::string> & rawInstallables);
|
||||
|
||||
bool readFromStdIn = false;
|
||||
@@ -176,11 +140,8 @@ private:
|
||||
|
||||
std::vector<std::string> rawInstallables;
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that operates on a list of "installables", which can be
|
||||
* store paths, attribute paths, Nix expressions, etc.
|
||||
*/
|
||||
/* A command that operates on a list of "installables", which can be
|
||||
store paths, attribute paths, Nix expressions, etc. */
|
||||
struct InstallablesCommand : RawInstallablesCommand
|
||||
{
|
||||
virtual void run(ref<Store> store, Installables && installables) = 0;
|
||||
@@ -188,9 +149,7 @@ struct InstallablesCommand : RawInstallablesCommand
|
||||
void run(ref<Store> store, std::vector<std::string> && rawInstallables) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that operates on exactly one "installable".
|
||||
*/
|
||||
/* A command that operates on exactly one "installable" */
|
||||
struct InstallableCommand : virtual Args, SourceExprCommand
|
||||
{
|
||||
InstallableCommand();
|
||||
@@ -216,12 +175,7 @@ struct MixOperateOnOptions : virtual Args
|
||||
MixOperateOnOptions();
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that operates on zero or more extant store paths.
|
||||
*
|
||||
* If the argument the user passes is a some sort of recipe for a path
|
||||
* not yet built, it must be built first.
|
||||
*/
|
||||
/* A command that operates on zero or more store paths. */
|
||||
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
|
||||
{
|
||||
private:
|
||||
@@ -253,9 +207,7 @@ struct StorePathsCommand : public BuiltPathsCommand
|
||||
void run(ref<Store> store, BuiltPaths && paths) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that operates on exactly one store path.
|
||||
*/
|
||||
/* A command that operates on exactly one store path. */
|
||||
struct StorePathCommand : public StorePathsCommand
|
||||
{
|
||||
virtual void run(ref<Store> store, const StorePath & storePath) = 0;
|
||||
@@ -263,9 +215,7 @@ struct StorePathCommand : public StorePathsCommand
|
||||
void run(ref<Store> store, StorePaths && storePaths) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper class for registering \ref Command commands globally.
|
||||
*/
|
||||
/* A helper class for registering commands globally. */
|
||||
struct RegisterCommand
|
||||
{
|
||||
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
|
||||
@@ -321,11 +271,7 @@ struct MixEnvironment : virtual Args {
|
||||
|
||||
MixEnvironment();
|
||||
|
||||
/***
|
||||
* Modify global environ based on `ignoreEnvironment`, `keep`, and
|
||||
* `unset`. It's expected that exec will be called before this class
|
||||
* goes out of scope, otherwise `environ` will become invalid.
|
||||
*/
|
||||
/* Modify global environ based on ignoreEnvironment, keep, and unset. It's expected that exec will be called before this class goes out of scope, otherwise environ will become invalid. */
|
||||
void setEnviron();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "util.hh"
|
||||
#include "editor-for.hh"
|
||||
#include "source-path.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
///@file
|
||||
|
||||
#include "types.hh"
|
||||
#include "source-path.hh"
|
||||
#include "input-accessor.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
|
||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||
assert(aOutputs);
|
||||
|
||||
state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos));
|
||||
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
|
||||
|
||||
return aOutputs->value;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "installable-value.hh"
|
||||
#include "eval-cache.hh"
|
||||
#include "fetch-to-store.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -45,7 +44,7 @@ ref<InstallableValue> InstallableValue::require(ref<Installable> installable)
|
||||
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
if (v.type() == nPath) {
|
||||
auto storePath = fetchToStore(*state->store, v.path());
|
||||
auto storePath = v.path().fetchToStore(state->store);
|
||||
return {{
|
||||
.path = DerivedPath::Opaque {
|
||||
.path = std::move(storePath),
|
||||
|
||||
@@ -669,12 +669,12 @@ BuiltPaths Installable::toBuiltPaths(
|
||||
|
||||
BuiltPaths res;
|
||||
for (auto & drvPath : Installable::toDerivations(store, installables, true))
|
||||
res.emplace_back(BuiltPath::Opaque{drvPath});
|
||||
res.push_back(BuiltPath::Opaque{drvPath});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
StorePathSet Installable::toStorePathSet(
|
||||
StorePathSet Installable::toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
@@ -688,27 +688,13 @@ StorePathSet Installable::toStorePathSet(
|
||||
return outPaths;
|
||||
}
|
||||
|
||||
StorePaths Installable::toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
const Installables & installables)
|
||||
{
|
||||
StorePaths outPaths;
|
||||
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
|
||||
auto thisOutPaths = path.outPaths();
|
||||
outPaths.insert(outPaths.end(), thisOutPaths.begin(), thisOutPaths.end());
|
||||
}
|
||||
return outPaths;
|
||||
}
|
||||
|
||||
StorePath Installable::toStorePath(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode, OperateOn operateOn,
|
||||
ref<Installable> installable)
|
||||
{
|
||||
auto paths = toStorePathSet(evalStore, store, mode, operateOn, {installable});
|
||||
auto paths = toStorePaths(evalStore, store, mode, operateOn, {installable});
|
||||
|
||||
if (paths.size() != 1)
|
||||
throw Error("argument '%s' should evaluate to one store path", installable->what());
|
||||
|
||||
@@ -166,14 +166,7 @@ struct Installable
|
||||
const Installables & installables,
|
||||
BuildMode bMode = bmNormal);
|
||||
|
||||
static std::set<StorePath> toStorePathSet(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
OperateOn operateOn,
|
||||
const Installables & installables);
|
||||
|
||||
static std::vector<StorePath> toStorePaths(
|
||||
static std::set<StorePath> toStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
Realise mode,
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread
|
||||
|
||||
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
|
||||
|
||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644))
|
||||
$(eval $(call install-file-in, $(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644))
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef READLINE
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#else
|
||||
// editline < 1.15.2 don't wrap their API for C++ usage
|
||||
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
|
||||
// This results in linker errors due to to name-mangling of editline C symbols.
|
||||
// For compatibility with these versions, we wrap the API here
|
||||
// (wrapping multiple times on newer versions is no problem).
|
||||
extern "C" {
|
||||
#include <editline.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
#include "repl-interacter.hh"
|
||||
#include "util.hh"
|
||||
#include "repl.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace {
|
||||
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
|
||||
volatile sig_atomic_t g_signal_received = 0;
|
||||
|
||||
void sigintHandler(int signo)
|
||||
{
|
||||
g_signal_received = signo;
|
||||
}
|
||||
};
|
||||
|
||||
static detail::ReplCompleterMixin * curRepl; // ugly
|
||||
|
||||
static char * completionCallback(char * s, int * match)
|
||||
{
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
if (possible.size() == 1) {
|
||||
*match = 1;
|
||||
auto * res = strdup(possible.begin()->c_str() + strlen(s));
|
||||
if (!res)
|
||||
throw Error("allocation failure");
|
||||
return res;
|
||||
} else if (possible.size() > 1) {
|
||||
auto checkAllHaveSameAt = [&](size_t pos) {
|
||||
auto & first = *possible.begin();
|
||||
for (auto & p : possible) {
|
||||
if (p.size() <= pos || p[pos] != first[pos])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
size_t start = strlen(s);
|
||||
size_t len = 0;
|
||||
while (checkAllHaveSameAt(start + len))
|
||||
++len;
|
||||
if (len > 0) {
|
||||
*match = 1;
|
||||
auto * res = strdup(std::string(*possible.begin(), start, len).c_str());
|
||||
if (!res)
|
||||
throw Error("allocation failure");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*match = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int listPossibleCallback(char * s, char *** avp)
|
||||
{
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
|
||||
if (possible.size() > (INT_MAX / sizeof(char *)))
|
||||
throw Error("too many completions");
|
||||
|
||||
int ac = 0;
|
||||
char ** vp = nullptr;
|
||||
|
||||
auto check = [&](auto * p) {
|
||||
if (!p) {
|
||||
if (vp) {
|
||||
while (--ac >= 0)
|
||||
free(vp[ac]);
|
||||
free(vp);
|
||||
}
|
||||
throw Error("allocation failure");
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
vp = check((char **) malloc(possible.size() * sizeof(char *)));
|
||||
|
||||
for (auto & p : possible)
|
||||
vp[ac++] = check(strdup(p.c_str()));
|
||||
|
||||
*avp = vp;
|
||||
|
||||
return ac;
|
||||
}
|
||||
|
||||
ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
|
||||
{
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#ifndef READLINE
|
||||
el_hist_size = 1000;
|
||||
#endif
|
||||
read_history(historyFile.c_str());
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = repl;
|
||||
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
|
||||
#ifndef READLINE
|
||||
rl_set_complete_func(completionCallback);
|
||||
rl_set_list_possib_func(listPossibleCallback);
|
||||
#endif
|
||||
return restoreRepl;
|
||||
}
|
||||
|
||||
static constexpr const char * promptForType(ReplPromptType promptType)
|
||||
{
|
||||
switch (promptType) {
|
||||
case ReplPromptType::ReplPrompt:
|
||||
return "nix-repl> ";
|
||||
case ReplPromptType::ContinuationPrompt:
|
||||
return " ";
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
auto setupSignals = [&]() {
|
||||
act.sa_handler = sigintHandler;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGINT, &act, &old))
|
||||
throw SysError("installing handler for SIGINT");
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
|
||||
throw SysError("unblocking SIGINT");
|
||||
};
|
||||
auto restoreSignals = [&]() {
|
||||
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
||||
throw SysError("restoring signals");
|
||||
|
||||
if (sigaction(SIGINT, &old, 0))
|
||||
throw SysError("restoring handler for SIGINT");
|
||||
};
|
||||
|
||||
setupSignals();
|
||||
char * s = readline(promptForType(promptType));
|
||||
Finally doFree([&]() { free(s); });
|
||||
restoreSignals();
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
input.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
input += s;
|
||||
input += '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
ReadlineLikeInteracter::~ReadlineLikeInteracter()
|
||||
{
|
||||
write_history(historyFile.c_str());
|
||||
}
|
||||
|
||||
AutomationInteracter::Guard AutomationInteracter::init(detail::ReplCompleterMixin *)
|
||||
{
|
||||
return Guard([] {});
|
||||
}
|
||||
|
||||
// ASCII ENQ character
|
||||
constexpr const char * automationPrompt = "\x05";
|
||||
|
||||
bool AutomationInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
std::cout << std::unitbuf;
|
||||
std::cout << automationPrompt;
|
||||
if (!std::getline(std::cin, input)) {
|
||||
// reset failure bits on EOF
|
||||
std::cin.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
/// @file
|
||||
|
||||
#include "finally.hh"
|
||||
#include "types.hh"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace detail {
|
||||
/** Provides the completion hooks for the repl, without exposing its complete
|
||||
* internals. */
|
||||
struct ReplCompleterMixin {
|
||||
virtual StringSet completePrefix(const std::string & prefix) = 0;
|
||||
};
|
||||
};
|
||||
|
||||
enum class ReplPromptType {
|
||||
ReplPrompt,
|
||||
ContinuationPrompt,
|
||||
};
|
||||
|
||||
class ReplInteracter
|
||||
{
|
||||
public:
|
||||
using Guard = Finally<std::function<void()>>;
|
||||
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
|
||||
/** Returns a boolean of whether the interacter got EOF */
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) = 0;
|
||||
virtual ~ReplInteracter(){};
|
||||
};
|
||||
|
||||
class ReadlineLikeInteracter : public virtual ReplInteracter
|
||||
{
|
||||
std::string historyFile;
|
||||
public:
|
||||
ReadlineLikeInteracter(std::string historyFile)
|
||||
: historyFile(historyFile)
|
||||
{
|
||||
}
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
virtual ~ReadlineLikeInteracter() override;
|
||||
};
|
||||
|
||||
class AutomationInteracter : public virtual ReplInteracter
|
||||
{
|
||||
public:
|
||||
AutomationInteracter() = default;
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) override;
|
||||
virtual bool getLine(std::string & input, ReplPromptType promptType) override;
|
||||
virtual ~AutomationInteracter() override = default;
|
||||
};
|
||||
|
||||
};
|
||||
+337
-98
@@ -3,8 +3,22 @@
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
|
||||
#include "box_ptr.hh"
|
||||
#include "repl-interacter.hh"
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef READLINE
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#else
|
||||
// editline < 1.15.2 don't wrap their API for C++ usage
|
||||
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
|
||||
// This results in linker errors due to to name-mangling of editline C symbols.
|
||||
// For compatibility with these versions, we wrap the API here
|
||||
// (wrapping multiple times on newer versions is no problem).
|
||||
extern "C" {
|
||||
#include <editline.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "repl.hh"
|
||||
|
||||
#include "ansicolor.hh"
|
||||
@@ -14,7 +28,6 @@
|
||||
#include "eval-inline.hh"
|
||||
#include "eval-settings.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "signals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "log-store.hh"
|
||||
#include "common-eval-args.hh"
|
||||
@@ -27,7 +40,7 @@
|
||||
#include "finally.hh"
|
||||
#include "markdown.hh"
|
||||
#include "local-fs-store.hh"
|
||||
#include "signals.hh"
|
||||
#include "progress-bar.hh"
|
||||
#include "print.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -37,30 +50,8 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Returned by `NixRepl::processLine`.
|
||||
*/
|
||||
enum class ProcessLineResult {
|
||||
/**
|
||||
* The user exited with `:quit`. The REPL should exit. The surrounding
|
||||
* program or evaluation (e.g., if the REPL was acting as the debugger)
|
||||
* should also exit.
|
||||
*/
|
||||
Quit,
|
||||
/**
|
||||
* The user exited with `:continue`. The REPL should exit, but the program
|
||||
* should continue running.
|
||||
*/
|
||||
Continue,
|
||||
/**
|
||||
* The user did not exit. The REPL should request another line of input.
|
||||
*/
|
||||
PromptAgain,
|
||||
};
|
||||
|
||||
struct NixRepl
|
||||
: AbstractNixRepl
|
||||
, detail::ReplCompleterMixin
|
||||
#if HAVE_BOEHMGC
|
||||
, gc
|
||||
#endif
|
||||
@@ -76,18 +67,19 @@ struct NixRepl
|
||||
int displ;
|
||||
StringSet varNames;
|
||||
|
||||
box_ptr<ReplInteracter> interacter;
|
||||
const Path historyFile;
|
||||
|
||||
NixRepl(const SearchPath & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues);
|
||||
virtual ~NixRepl() = default;
|
||||
virtual ~NixRepl();
|
||||
|
||||
ReplExitStatus mainLoop() override;
|
||||
void mainLoop() override;
|
||||
void initEnv() override;
|
||||
|
||||
virtual StringSet completePrefix(const std::string & prefix) override;
|
||||
StringSet completePrefix(const std::string & prefix);
|
||||
bool getLine(std::string & input, const std::string & prompt);
|
||||
StorePath getDerivationPath(Value & v);
|
||||
ProcessLineResult processLine(std::string line);
|
||||
bool processLine(std::string line);
|
||||
|
||||
void loadFile(const Path & path);
|
||||
void loadFlake(const std::string & flakeRef);
|
||||
@@ -99,18 +91,9 @@ struct NixRepl
|
||||
void evalString(std::string s, Value & v);
|
||||
void loadDebugTraceEnv(DebugTrace & dt);
|
||||
|
||||
void printValue(std::ostream & str,
|
||||
Value & v,
|
||||
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
||||
{
|
||||
::nix::printValue(*state, str, v, PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth,
|
||||
.prettyIndent = 2
|
||||
});
|
||||
}
|
||||
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::string removeWhitespace(std::string s)
|
||||
@@ -121,23 +104,23 @@ std::string removeWhitespace(std::string s)
|
||||
return s;
|
||||
}
|
||||
|
||||
static box_ptr<ReplInteracter> makeInteracter() {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ReplAutomation))
|
||||
return make_box_ptr<AutomationInteracter>();
|
||||
else
|
||||
return make_box_ptr<ReadlineLikeInteracter>(getDataDir() + "/nix/repl-history");
|
||||
}
|
||||
|
||||
NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<NixRepl::AnnotatedValues()> getValues)
|
||||
: AbstractNixRepl(state)
|
||||
, debugTraceIndex(0)
|
||||
, getValues(getValues)
|
||||
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
|
||||
, interacter(makeInteracter())
|
||||
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
|
||||
, historyFile(getDataDir() + "/nix/repl-history")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NixRepl::~NixRepl()
|
||||
{
|
||||
write_history(historyFile.c_str());
|
||||
}
|
||||
|
||||
void runNix(Path program, const Strings & args,
|
||||
const std::optional<std::string> & input = {})
|
||||
{
|
||||
@@ -154,6 +137,79 @@ void runNix(Path program, const Strings & args,
|
||||
return;
|
||||
}
|
||||
|
||||
static NixRepl * curRepl; // ugly
|
||||
|
||||
static char * completionCallback(char * s, int *match) {
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
if (possible.size() == 1) {
|
||||
*match = 1;
|
||||
auto *res = strdup(possible.begin()->c_str() + strlen(s));
|
||||
if (!res) throw Error("allocation failure");
|
||||
return res;
|
||||
} else if (possible.size() > 1) {
|
||||
auto checkAllHaveSameAt = [&](size_t pos) {
|
||||
auto &first = *possible.begin();
|
||||
for (auto &p : possible) {
|
||||
if (p.size() <= pos || p[pos] != first[pos])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
size_t start = strlen(s);
|
||||
size_t len = 0;
|
||||
while (checkAllHaveSameAt(start + len)) ++len;
|
||||
if (len > 0) {
|
||||
*match = 1;
|
||||
auto *res = strdup(std::string(*possible.begin(), start, len).c_str());
|
||||
if (!res) throw Error("allocation failure");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
*match = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int listPossibleCallback(char *s, char ***avp) {
|
||||
auto possible = curRepl->completePrefix(s);
|
||||
|
||||
if (possible.size() > (INT_MAX / sizeof(char*)))
|
||||
throw Error("too many completions");
|
||||
|
||||
int ac = 0;
|
||||
char **vp = nullptr;
|
||||
|
||||
auto check = [&](auto *p) {
|
||||
if (!p) {
|
||||
if (vp) {
|
||||
while (--ac >= 0)
|
||||
free(vp[ac]);
|
||||
free(vp);
|
||||
}
|
||||
throw Error("allocation failure");
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
vp = check((char **)malloc(possible.size() * sizeof(char*)));
|
||||
|
||||
for (auto & p : possible)
|
||||
vp[ac++] = check(strdup(p.c_str()));
|
||||
|
||||
*avp = vp;
|
||||
|
||||
return ac;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline.
|
||||
volatile sig_atomic_t g_signal_received = 0;
|
||||
|
||||
void sigintHandler(int signo) {
|
||||
g_signal_received = signo;
|
||||
}
|
||||
}
|
||||
|
||||
static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt)
|
||||
{
|
||||
if (dt.isError)
|
||||
@@ -163,10 +219,10 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
|
||||
// prefer direct pos, but if noPos then try the expr.
|
||||
auto pos = dt.pos
|
||||
? dt.pos
|
||||
: positions[dt.expr.getPos() ? dt.expr.getPos() : noPos];
|
||||
: static_cast<std::shared_ptr<AbstractPos>>(positions[dt.expr.getPos() ? dt.expr.getPos() : noPos]);
|
||||
|
||||
if (pos) {
|
||||
out << *pos;
|
||||
out << pos;
|
||||
if (auto loc = pos->getCodeLines()) {
|
||||
out << "\n";
|
||||
printCodeLines(out, "", *pos, *loc);
|
||||
@@ -177,51 +233,50 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool isFirstRepl = true;
|
||||
|
||||
ReplExitStatus NixRepl::mainLoop()
|
||||
void NixRepl::mainLoop()
|
||||
{
|
||||
if (isFirstRepl) {
|
||||
std::string_view debuggerNotice = "";
|
||||
if (state->debugRepl) {
|
||||
debuggerNotice = " debugger";
|
||||
}
|
||||
notice("Nix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
|
||||
}
|
||||
|
||||
isFirstRepl = false;
|
||||
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||
notice("Welcome to Nix " + nixVersion + ". Type :? for help.\n");
|
||||
|
||||
loadFiles();
|
||||
|
||||
auto _guard = interacter->init(static_cast<detail::ReplCompleterMixin *>(this));
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#ifndef READLINE
|
||||
el_hist_size = 1000;
|
||||
#endif
|
||||
read_history(historyFile.c_str());
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = this;
|
||||
Finally restoreRepl([&] { curRepl = oldRepl; });
|
||||
#ifndef READLINE
|
||||
rl_set_complete_func(completionCallback);
|
||||
rl_set_list_possib_func(listPossibleCallback);
|
||||
#endif
|
||||
|
||||
/* Stop the progress bar because it interferes with the display of
|
||||
the repl. */
|
||||
stopProgressBar();
|
||||
|
||||
std::string input;
|
||||
|
||||
while (true) {
|
||||
// Hide the progress bar while waiting for user input, so that it won't interfere.
|
||||
logger->pause();
|
||||
// When continuing input from previous lines, don't print a prompt, just align to the same
|
||||
// number of chars as the prompt.
|
||||
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
|
||||
// Ctrl-D should exit the debugger.
|
||||
if (!getLine(input, input.empty() ? "nix-repl> " : " ")) {
|
||||
// ctrl-D should exit the debugger.
|
||||
state->debugStop = false;
|
||||
state->debugQuit = true;
|
||||
logger->cout("");
|
||||
// TODO: Should Ctrl-D exit just the current debugger session or
|
||||
// the entire program?
|
||||
return ReplExitStatus::QuitAll;
|
||||
break;
|
||||
}
|
||||
logger->resume();
|
||||
try {
|
||||
switch (processLine(input)) {
|
||||
case ProcessLineResult::Quit:
|
||||
return ReplExitStatus::QuitAll;
|
||||
case ProcessLineResult::Continue:
|
||||
return ReplExitStatus::Continue;
|
||||
case ProcessLineResult::PromptAgain:
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (!removeWhitespace(input).empty() && !processLine(input)) return;
|
||||
} catch (ParseError & e) {
|
||||
if (e.msg().find("unexpected end of file") != std::string::npos) {
|
||||
// For parse errors on incomplete input, we continue waiting for the next line of
|
||||
@@ -251,6 +306,52 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool NixRepl::getLine(std::string & input, const std::string & prompt)
|
||||
{
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
auto setupSignals = [&]() {
|
||||
act.sa_handler = sigintHandler;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGINT, &act, &old))
|
||||
throw SysError("installing handler for SIGINT");
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask))
|
||||
throw SysError("unblocking SIGINT");
|
||||
};
|
||||
auto restoreSignals = [&]() {
|
||||
if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr))
|
||||
throw SysError("restoring signals");
|
||||
|
||||
if (sigaction(SIGINT, &old, 0))
|
||||
throw SysError("restoring handler for SIGINT");
|
||||
};
|
||||
|
||||
setupSignals();
|
||||
Finally resetTerminal([&]() { rl_deprep_terminal(); });
|
||||
char * s = readline(prompt.c_str());
|
||||
Finally doFree([&]() { free(s); });
|
||||
restoreSignals();
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
input.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
input += s;
|
||||
input += '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
{
|
||||
StringSet completions;
|
||||
@@ -312,6 +413,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
// Quietly ignore parse errors.
|
||||
} catch (EvalError & e) {
|
||||
// Quietly ignore evaluation errors.
|
||||
} catch (UndefinedVarError & e) {
|
||||
// Quietly ignore undefined variable errors.
|
||||
} catch (BadURL & e) {
|
||||
// Quietly ignore BadURL flake-related errors.
|
||||
}
|
||||
@@ -363,11 +466,10 @@ void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
|
||||
}
|
||||
}
|
||||
|
||||
ProcessLineResult NixRepl::processLine(std::string line)
|
||||
bool NixRepl::processLine(std::string line)
|
||||
{
|
||||
line = trim(line);
|
||||
if (line.empty())
|
||||
return ProcessLineResult::PromptAgain;
|
||||
if (line == "") return true;
|
||||
|
||||
_isInterrupted = false;
|
||||
|
||||
@@ -462,13 +564,13 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
else if (state->debugRepl && (command == ":s" || command == ":step")) {
|
||||
// set flag to stop at next DebugTrace; exit repl.
|
||||
state->debugStop = true;
|
||||
return ProcessLineResult::Continue;
|
||||
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 ProcessLineResult::Continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (command == ":a" || command == ":add") {
|
||||
@@ -605,13 +707,13 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
else if (command == ":p" || command == ":print") {
|
||||
Value v;
|
||||
evalString(arg, v);
|
||||
printValue(std::cout, v);
|
||||
std::cout << std::endl;
|
||||
printValue(std::cout, v, 1000000000) << std::endl;
|
||||
}
|
||||
|
||||
else if (command == ":q" || command == ":quit") {
|
||||
state->debugStop = false;
|
||||
return ProcessLineResult::Quit;
|
||||
state->debugQuit = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (command == ":doc") {
|
||||
@@ -667,12 +769,11 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
} else {
|
||||
Value v;
|
||||
evalString(line, v);
|
||||
printValue(std::cout, v, 1);
|
||||
std::cout << std::endl;
|
||||
printValue(std::cout, v, 1) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return ProcessLineResult::PromptAgain;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NixRepl::loadFile(const Path & path)
|
||||
@@ -786,7 +887,145 @@ void NixRepl::evalString(std::string s, Value & v)
|
||||
{
|
||||
Expr * e = parseString(s);
|
||||
e->eval(*state, *env, v);
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||
}
|
||||
|
||||
|
||||
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth)
|
||||
{
|
||||
ValuesSeen seen;
|
||||
return printValue(str, v, maxDepth, seen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: lot of cut&paste from Nix's eval.cc.
|
||||
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen)
|
||||
{
|
||||
str.flush();
|
||||
checkInterrupt();
|
||||
|
||||
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
str << ANSI_CYAN << v.integer << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
str << ANSI_CYAN;
|
||||
printLiteralBool(str, v.boolean);
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nString:
|
||||
str << ANSI_WARNING;
|
||||
printLiteralString(str, v.string.s);
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
str << ANSI_GREEN << v.path().to_string() << ANSI_NORMAL; // !!! escaping?
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
str << ANSI_CYAN "null" ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nAttrs: {
|
||||
seen.insert(&v);
|
||||
|
||||
bool isDrv = state->isDerivation(v);
|
||||
|
||||
if (isDrv) {
|
||||
str << "«derivation ";
|
||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||
NixStringContext context;
|
||||
if (i != v.attrs->end())
|
||||
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
else
|
||||
str << "???";
|
||||
str << "»";
|
||||
}
|
||||
|
||||
else if (maxDepth > 0) {
|
||||
str << "{ ";
|
||||
|
||||
typedef std::map<std::string, Value *> Sorted;
|
||||
Sorted sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace(state->symbols[i.name], i.value);
|
||||
|
||||
for (auto & i : sorted) {
|
||||
printAttributeName(str, i.first);
|
||||
str << " = ";
|
||||
if (seen.count(i.second))
|
||||
str << "«repeated»";
|
||||
else
|
||||
try {
|
||||
printValue(str, *i.second, maxDepth - 1, seen);
|
||||
} catch (AssertionError & e) {
|
||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||
}
|
||||
str << "; ";
|
||||
}
|
||||
|
||||
str << "}";
|
||||
} else
|
||||
str << "{ ... }";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nList:
|
||||
seen.insert(&v);
|
||||
|
||||
str << "[ ";
|
||||
if (maxDepth > 0)
|
||||
for (auto elem : v.listItems()) {
|
||||
if (seen.count(elem))
|
||||
str << "«repeated»";
|
||||
else
|
||||
try {
|
||||
printValue(str, *elem, maxDepth - 1, seen);
|
||||
} catch (AssertionError & e) {
|
||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||
}
|
||||
str << " ";
|
||||
}
|
||||
else
|
||||
str << "... ";
|
||||
str << "]";
|
||||
break;
|
||||
|
||||
case nFunction:
|
||||
if (v.isLambda()) {
|
||||
std::ostringstream s;
|
||||
s << state->positions[v.lambda.fun->pos];
|
||||
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOp()) {
|
||||
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOpApp()) {
|
||||
str << ANSI_BLUE "«primop-app»" ANSI_NORMAL;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
case nExternal:
|
||||
default:
|
||||
str << ANSI_RED "«unknown»" ANSI_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@@ -803,7 +1042,7 @@ std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
}
|
||||
|
||||
|
||||
ReplExitStatus AbstractNixRepl::runSimple(
|
||||
void AbstractNixRepl::runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv)
|
||||
{
|
||||
@@ -825,7 +1064,7 @@ ReplExitStatus AbstractNixRepl::runSimple(
|
||||
for (auto & [name, value] : extraEnv)
|
||||
repl->addVarToScope(repl->state->symbols.create(name), *value);
|
||||
|
||||
return repl->mainLoop();
|
||||
repl->mainLoop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-2
@@ -3,6 +3,11 @@
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#define GC_INCLUDE_NEW
|
||||
#include <gc/gc_cpp.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct AbstractNixRepl
|
||||
@@ -23,13 +28,13 @@ struct AbstractNixRepl
|
||||
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues);
|
||||
|
||||
static ReplExitStatus runSimple(
|
||||
static void runSimple(
|
||||
ref<EvalState> evalState,
|
||||
const ValMap & extraEnv);
|
||||
|
||||
virtual void initEnv() = 0;
|
||||
|
||||
virtual ReplExitStatus mainLoop() = 0;
|
||||
virtual void mainLoop() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
if (!attrIndex) {
|
||||
|
||||
if (v->type() != nAttrs)
|
||||
state.error<TypeError>(
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
showType(*v));
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
@@ -89,10 +89,10 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
else {
|
||||
|
||||
if (!v->isList())
|
||||
state.error<TypeError>(
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
showType(*v));
|
||||
if (*attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
|
||||
|
||||
|
||||
+15
-15
@@ -490,7 +490,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||
if (forceErrors)
|
||||
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name));
|
||||
else
|
||||
throw CachedEvalError(root->state, "cached failure of attribute '%s'", getAttrPathStr(name));
|
||||
throw CachedEvalError("cached failure of attribute '%s'", getAttrPathStr(name));
|
||||
} else
|
||||
return std::make_shared<AttrCursor>(root,
|
||||
std::make_pair(shared_from_this(), name), nullptr, std::move(attr));
|
||||
@@ -499,7 +499,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||
// evaluate to see whether 'name' exists
|
||||
} else
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +507,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
||||
|
||||
if (v.type() != nAttrs)
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
||||
|
||||
auto attr = v.attrs->get(name);
|
||||
|
||||
@@ -573,14 +573,14 @@ std::string AttrCursor::getString()
|
||||
debug("using cached string attribute '%s'", getAttrPathStr());
|
||||
return s->first;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a string", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow<TypeError>();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type() != nString && v.type() != nPath)
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
|
||||
|
||||
return v.type() == nString ? v.string.s : v.path().to_string();
|
||||
}
|
||||
@@ -615,7 +615,7 @@ string_t AttrCursor::getStringWithContext()
|
||||
return *s;
|
||||
}
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a string", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow<TypeError>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,7 +629,7 @@ string_t AttrCursor::getStringWithContext()
|
||||
else if (v.type() == nPath)
|
||||
return {v.path().to_string(), {}};
|
||||
else
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
|
||||
}
|
||||
|
||||
bool AttrCursor::getBool()
|
||||
@@ -642,14 +642,14 @@ bool AttrCursor::getBool()
|
||||
debug("using cached Boolean attribute '%s'", getAttrPathStr());
|
||||
return *b;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow<TypeError>();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type() != nBool)
|
||||
root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow<TypeError>();
|
||||
|
||||
return v.boolean;
|
||||
}
|
||||
@@ -664,14 +664,14 @@ NixInt AttrCursor::getInt()
|
||||
debug("using cached integer attribute '%s'", getAttrPathStr());
|
||||
return i->x;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow();
|
||||
throw TypeError("'%s' is not an integer", getAttrPathStr());
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type() != nInt)
|
||||
root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow();
|
||||
throw TypeError("'%s' is not an integer", getAttrPathStr());
|
||||
|
||||
return v.integer;
|
||||
}
|
||||
@@ -686,7 +686,7 @@ std::vector<std::string> AttrCursor::getListOfStrings()
|
||||
debug("using cached list of strings attribute '%s'", getAttrPathStr());
|
||||
return *l;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a list of strings", getAttrPathStr()).debugThrow();
|
||||
throw TypeError("'%s' is not a list of strings", getAttrPathStr());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +696,7 @@ std::vector<std::string> AttrCursor::getListOfStrings()
|
||||
root->state.forceValue(v, noPos);
|
||||
|
||||
if (v.type() != nList)
|
||||
root->state.error<TypeError>("'%s' is not a list", getAttrPathStr()).debugThrow();
|
||||
throw TypeError("'%s' is not a list", getAttrPathStr());
|
||||
|
||||
std::vector<std::string> res;
|
||||
|
||||
@@ -719,14 +719,14 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
||||
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
||||
return *attrs;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow<TypeError>();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type() != nAttrs)
|
||||
root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow<TypeError>();
|
||||
|
||||
std::vector<Symbol> attrs;
|
||||
for (auto & attr : *getValue().attrs)
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace nix::eval_cache {
|
||||
|
||||
MakeError(CachedEvalError, EvalError);
|
||||
|
||||
struct AttrDb;
|
||||
class AttrCursor;
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#include "eval-error.hh"
|
||||
#include "eval.hh"
|
||||
#include "value.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus)
|
||||
{
|
||||
error.withExitStatus(exitStatus);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::atPos(PosIdx pos)
|
||||
{
|
||||
error.err.pos = error.state.positions[pos];
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::atPos(Value & value, PosIdx fallback)
|
||||
{
|
||||
return atPos(value.determinePos(fallback));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text)
|
||||
{
|
||||
error.err.traces.push_front(
|
||||
Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text))});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withSuggestions(Suggestions & s)
|
||||
{
|
||||
error.err.suggestions = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr & expr)
|
||||
{
|
||||
// NOTE: This is abusing side-effects.
|
||||
// TODO: check compatibility with nested debugger calls.
|
||||
// TODO: What side-effects??
|
||||
error.state.debugTraces.push_front(DebugTrace{
|
||||
.pos = error.state.positions[expr.getPos()],
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = HintFmt("Fake frame for debugging purposes"),
|
||||
.isError = true});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint)
|
||||
{
|
||||
error.addTrace(error.state.positions[pos], hint);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<typename... Args>
|
||||
EvalErrorBuilder<T> &
|
||||
EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs)
|
||||
{
|
||||
|
||||
addTrace(error.state.positions[pos], HintFmt(std::string(formatString), formatArgs...));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void EvalErrorBuilder<T>::debugThrow()
|
||||
{
|
||||
if (error.state.debugRepl && !error.state.debugTraces.empty()) {
|
||||
const DebugTrace & last = error.state.debugTraces.front();
|
||||
const Env * env = &last.env;
|
||||
const Expr * expr = &last.expr;
|
||||
error.state.runDebugRepl(&error, *env, *expr);
|
||||
}
|
||||
|
||||
// `EvalState` is the only class that can construct an `EvalErrorBuilder`,
|
||||
// and it does so in dynamic storage. This is the final method called on
|
||||
// any such instance and must delete itself before throwing the underlying
|
||||
// error.
|
||||
auto error = std::move(this->error);
|
||||
delete this;
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
template class EvalErrorBuilder<EvalError>;
|
||||
template class EvalErrorBuilder<AssertionError>;
|
||||
template class EvalErrorBuilder<ThrownError>;
|
||||
template class EvalErrorBuilder<Abort>;
|
||||
template class EvalErrorBuilder<TypeError>;
|
||||
template class EvalErrorBuilder<UndefinedVarError>;
|
||||
template class EvalErrorBuilder<MissingArgumentError>;
|
||||
template class EvalErrorBuilder<InfiniteRecursionError>;
|
||||
template class EvalErrorBuilder<CachedEvalError>;
|
||||
template class EvalErrorBuilder<InvalidPathError>;
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "error.hh"
|
||||
#include "pos-idx.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct Env;
|
||||
struct Expr;
|
||||
struct Value;
|
||||
|
||||
class EvalState;
|
||||
template<class T>
|
||||
class EvalErrorBuilder;
|
||||
|
||||
class EvalError : public Error
|
||||
{
|
||||
template<class T>
|
||||
friend class EvalErrorBuilder;
|
||||
public:
|
||||
EvalState & state;
|
||||
|
||||
EvalError(EvalState & state, ErrorInfo && errorInfo)
|
||||
: Error(errorInfo)
|
||||
, state(state)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
explicit EvalError(EvalState & state, const std::string & formatString, const Args &... formatArgs)
|
||||
: Error(formatString, formatArgs...)
|
||||
, state(state)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
MakeError(ParseError, Error);
|
||||
MakeError(AssertionError, EvalError);
|
||||
MakeError(ThrownError, AssertionError);
|
||||
MakeError(Abort, EvalError);
|
||||
MakeError(TypeError, EvalError);
|
||||
MakeError(UndefinedVarError, EvalError);
|
||||
MakeError(MissingArgumentError, EvalError);
|
||||
MakeError(RestrictedPathError, Error);
|
||||
MakeError(CachedEvalError, EvalError);
|
||||
MakeError(InfiniteRecursionError, EvalError);
|
||||
|
||||
struct InvalidPathError : public EvalError
|
||||
{
|
||||
public:
|
||||
Path path;
|
||||
InvalidPathError(EvalState & state, const Path & path)
|
||||
: EvalError(state, "path '%s' is not valid", path)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* `EvalErrorBuilder`s may only be constructed by `EvalState`. The `debugThrow`
|
||||
* method must be the final method in any such `EvalErrorBuilder` usage, and it
|
||||
* handles deleting the object.
|
||||
*/
|
||||
template<class T>
|
||||
class EvalErrorBuilder final
|
||||
{
|
||||
friend class EvalState;
|
||||
|
||||
template<typename... Args>
|
||||
explicit EvalErrorBuilder(EvalState & state, const Args &... args)
|
||||
: error(T(state, args...))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
T error;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withExitStatus(unsigned int exitStatus);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(PosIdx pos);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(Value & value, PosIdx fallback = noPos);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withTrace(PosIdx pos, const std::string_view text);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrameTrace(PosIdx pos, const std::string_view text);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withSuggestions(Suggestions & s);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint);
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &
|
||||
addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs);
|
||||
|
||||
/**
|
||||
* Delete the `EvalErrorBuilder` and throw the underlying exception.
|
||||
*/
|
||||
[[gnu::noinline, gnu::noreturn]] void debugThrow();
|
||||
};
|
||||
|
||||
}
|
||||
+17
-17
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "print.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-error.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -75,6 +73,8 @@ Env & EvalState::allocEnv(size_t size)
|
||||
#endif
|
||||
env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||
|
||||
env->type = Env::Plain;
|
||||
|
||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||
|
||||
return *env;
|
||||
@@ -83,6 +83,13 @@ Env & EvalState::allocEnv(size_t size)
|
||||
|
||||
[[gnu::always_inline]]
|
||||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
{
|
||||
forceValue(v, [&]() { return pos; });
|
||||
}
|
||||
|
||||
|
||||
template<typename Callable>
|
||||
void EvalState::forceValue(Value & v, Callable getPos)
|
||||
{
|
||||
if (v.isThunk()) {
|
||||
Env * env = v.thunk.env;
|
||||
@@ -93,12 +100,13 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
expr->eval(*this, *env, v);
|
||||
} catch (...) {
|
||||
v.mkThunk(env, expr);
|
||||
tryFixupBlackHolePos(v, pos);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (v.isApp())
|
||||
callFunction(*v.app.left, *v.app.right, v, pos);
|
||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||
else if (v.isBlackhole())
|
||||
error("infinite recursion encountered").atPos(getPos()).template debugThrow<EvalError>();
|
||||
}
|
||||
|
||||
|
||||
@@ -113,14 +121,10 @@ template <typename Callable>
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx)
|
||||
{
|
||||
PosIdx pos = getPos();
|
||||
forceValue(v, pos);
|
||||
forceValue(v, noPos);
|
||||
if (v.type() != nAttrs) {
|
||||
error<TypeError>(
|
||||
"expected a set but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).withTrace(pos, errorCtx).debugThrow();
|
||||
PosIdx pos = getPos();
|
||||
error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,13 +132,9 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
forceValue(v, noPos);
|
||||
if (!v.isList()) {
|
||||
error<TypeError>(
|
||||
"expected a list but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).withTrace(pos, errorCtx).debugThrow();
|
||||
error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,12 +88,6 @@ std::string EvalSettings::resolvePseudoUrl(std::string_view url)
|
||||
return std::string(url);
|
||||
}
|
||||
|
||||
const std::string & EvalSettings::getCurrentSystem()
|
||||
{
|
||||
const auto & evalSystem = currentSystem.get();
|
||||
return evalSystem != "" ? evalSystem : settings.thisSystem.get();
|
||||
}
|
||||
|
||||
EvalSettings evalSettings;
|
||||
|
||||
static GlobalConfig::Register rEvalSettings(&evalSettings);
|
||||
|
||||
@@ -25,26 +25,6 @@ struct EvalSettings : Config
|
||||
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath).
|
||||
)"};
|
||||
|
||||
Setting<std::string> currentSystem{
|
||||
this, "", "eval-system",
|
||||
R"(
|
||||
This option defines
|
||||
[`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem)
|
||||
in the Nix language if it is set as a non-empty string.
|
||||
Otherwise, if it is defined as the empty string (the default), the value of the
|
||||
[`system` ](#conf-system)
|
||||
configuration setting is used instead.
|
||||
|
||||
Unlike `system`, this setting does not change what kind of derivations can be built locally.
|
||||
This is useful for evaluating Nix code on one system to produce derivations to be built on another type of system.
|
||||
)"};
|
||||
|
||||
/**
|
||||
* Implements the `eval-system` vs `system` defaulting logic
|
||||
* described for `eval-system`.
|
||||
*/
|
||||
const std::string & getCurrentSystem();
|
||||
|
||||
Setting<bool> restrictEval{
|
||||
this, false, "restrict-eval",
|
||||
R"(
|
||||
@@ -111,19 +91,6 @@ struct EvalSettings : Config
|
||||
|
||||
Setting<bool> traceVerbose{this, false, "trace-verbose",
|
||||
"Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
|
||||
|
||||
Setting<unsigned int> maxCallDepth{this, 10000, "max-call-depth",
|
||||
"The maximum function call depth to allow before erroring."};
|
||||
|
||||
Setting<bool> builtinsTraceDebugger{this, false, "debugger-on-trace",
|
||||
R"(
|
||||
If set to true and the `--debugger` flag is given,
|
||||
[`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will
|
||||
enter the debugger like
|
||||
[`builtins.break`](@docroot@/language/builtins.md#builtins-break).
|
||||
|
||||
This is useful for debugging warnings in third-party Nix code.
|
||||
)"};
|
||||
};
|
||||
|
||||
extern EvalSettings evalSettings;
|
||||
|
||||
+349
-620
File diff suppressed because it is too large
Load Diff
+103
-59
@@ -2,7 +2,6 @@
|
||||
///@file
|
||||
|
||||
#include "attr-set.hh"
|
||||
#include "eval-error.hh"
|
||||
#include "types.hh"
|
||||
#include "value.hh"
|
||||
#include "nixexpr.hh"
|
||||
@@ -11,7 +10,6 @@
|
||||
#include "experimental-features.hh"
|
||||
#include "input-accessor.hh"
|
||||
#include "search-path.hh"
|
||||
#include "repl-exit-status.hh"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -20,12 +18,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* We put a limit on primop arity because it lets us use a fixed size array on
|
||||
* the stack. 8 is already an impractical number of arguments. Use an attrset
|
||||
* argument for such overly complicated functions.
|
||||
*/
|
||||
constexpr size_t maxPrimOpArity = 8;
|
||||
|
||||
class Store;
|
||||
class EvalState;
|
||||
@@ -77,16 +69,8 @@ struct PrimOp
|
||||
* Optional experimental for this to be gated on.
|
||||
*/
|
||||
std::optional<ExperimentalFeature> experimentalFeature;
|
||||
|
||||
/**
|
||||
* Validity check to be performed by functions that introduce primops,
|
||||
* such as RegisterPrimOp() and Value::mkPrimOp().
|
||||
*/
|
||||
void check();
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp);
|
||||
|
||||
/**
|
||||
* Info about a constant
|
||||
*/
|
||||
@@ -119,6 +103,11 @@ struct Constant
|
||||
struct Env
|
||||
{
|
||||
Env * up;
|
||||
/**
|
||||
* Number of of levels up to next `with` environment
|
||||
*/
|
||||
unsigned short prevWith:14;
|
||||
enum { Plain = 0, HasWithExpr, HasWithAttrs } type:2;
|
||||
Value * values[0];
|
||||
};
|
||||
|
||||
@@ -130,7 +119,7 @@ std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const Stati
|
||||
void copyContext(const Value & v, NixStringContext & context);
|
||||
|
||||
|
||||
std::string printValue(EvalState & state, Value & v);
|
||||
std::string printValue(const EvalState & state, const Value & v);
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t);
|
||||
|
||||
|
||||
@@ -145,13 +134,52 @@ struct RegexCache;
|
||||
std::shared_ptr<RegexCache> makeRegexCache();
|
||||
|
||||
struct DebugTrace {
|
||||
std::shared_ptr<Pos> pos;
|
||||
std::shared_ptr<AbstractPos> pos;
|
||||
const Expr & expr;
|
||||
const Env & env;
|
||||
HintFmt hint;
|
||||
hintformat hint;
|
||||
bool isError;
|
||||
};
|
||||
|
||||
void debugError(Error * e, Env & env, Expr & expr);
|
||||
|
||||
class ErrorBuilder
|
||||
{
|
||||
private:
|
||||
EvalState & state;
|
||||
ErrorInfo info;
|
||||
|
||||
ErrorBuilder(EvalState & s, ErrorInfo && i): state(s), info(i) { }
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
[[nodiscard, gnu::noinline]]
|
||||
static ErrorBuilder * create(EvalState & s, const Args & ... args)
|
||||
{
|
||||
return new ErrorBuilder(s, ErrorInfo { .msg = hintfmt(args...) });
|
||||
}
|
||||
|
||||
[[nodiscard, gnu::noinline]]
|
||||
ErrorBuilder & atPos(PosIdx pos);
|
||||
|
||||
[[nodiscard, gnu::noinline]]
|
||||
ErrorBuilder & withTrace(PosIdx pos, const std::string_view text);
|
||||
|
||||
[[nodiscard, gnu::noinline]]
|
||||
ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text);
|
||||
|
||||
[[nodiscard, gnu::noinline]]
|
||||
ErrorBuilder & withSuggestions(Suggestions & s);
|
||||
|
||||
[[nodiscard, gnu::noinline]]
|
||||
ErrorBuilder & withFrame(const Env & e, const Expr & ex);
|
||||
|
||||
template<class ErrorType>
|
||||
[[gnu::noinline, gnu::noreturn]]
|
||||
void debugThrow();
|
||||
};
|
||||
|
||||
|
||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||
{
|
||||
public:
|
||||
@@ -169,8 +197,6 @@ public:
|
||||
sPrefix,
|
||||
sOutputSpecified;
|
||||
|
||||
const Expr::AstSymbols exprSymbols;
|
||||
|
||||
/**
|
||||
* If set, force copying files to the Nix store even if they
|
||||
* already exist there.
|
||||
@@ -185,11 +211,6 @@ public:
|
||||
|
||||
Bindings emptyBindings;
|
||||
|
||||
/**
|
||||
* Empty list constant.
|
||||
*/
|
||||
Value vEmptyList;
|
||||
|
||||
const SourcePath derivationInternal;
|
||||
|
||||
/**
|
||||
@@ -208,8 +229,9 @@ public:
|
||||
/**
|
||||
* Debugger
|
||||
*/
|
||||
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
bool debugStop;
|
||||
bool debugQuit;
|
||||
int trylevel;
|
||||
std::list<DebugTrace> debugTraces;
|
||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
@@ -224,11 +246,39 @@ public:
|
||||
|
||||
void runDebugRepl(const Error * error, const Env & env, const Expr & expr);
|
||||
|
||||
template<class T, typename... Args>
|
||||
template<class E>
|
||||
[[gnu::noinline, gnu::noreturn]]
|
||||
void debugThrowLastTrace(E && error)
|
||||
{
|
||||
debugThrow(error, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
[[gnu::noinline, gnu::noreturn]]
|
||||
void debugThrow(E && error, const Env * env, const Expr * expr)
|
||||
{
|
||||
if (debugRepl && ((env && expr) || !debugTraces.empty())) {
|
||||
if (!env || !expr) {
|
||||
const DebugTrace & last = debugTraces.front();
|
||||
env = &last.env;
|
||||
expr = &last.expr;
|
||||
}
|
||||
runDebugRepl(&error, *env, *expr);
|
||||
}
|
||||
|
||||
throw std::move(error);
|
||||
}
|
||||
|
||||
// This is dangerous, but gets in line with the idea that error creation and
|
||||
// throwing should not allocate on the stack of hot functions.
|
||||
// as long as errors are immediately thrown, it works.
|
||||
ErrorBuilder * errorBuilder;
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard, gnu::noinline]]
|
||||
EvalErrorBuilder<T> & error(const Args & ... args) {
|
||||
// `EvalErrorBuilder::debugThrow` performs the corresponding `delete`.
|
||||
return *new EvalErrorBuilder<T>(*this, args...);
|
||||
ErrorBuilder & error(const Args & ... args) {
|
||||
errorBuilder = ErrorBuilder::create(*this, args...);
|
||||
return *errorBuilder;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -405,7 +455,8 @@ public:
|
||||
*/
|
||||
inline void forceValue(Value & v, const PosIdx pos);
|
||||
|
||||
void tryFixupBlackHolePos(Value & v, PosIdx pos);
|
||||
template <typename Callable>
|
||||
inline void forceValue(Value & v, Callable getPos);
|
||||
|
||||
/**
|
||||
* Force a value, then recursively force list elements and
|
||||
@@ -434,12 +485,10 @@ public:
|
||||
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
|
||||
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
template<typename... Args>
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const Args & ... formatArgs) const;
|
||||
template<typename... Args>
|
||||
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
|
||||
[[gnu::noinline]]
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const Args & ... formatArgs) const;
|
||||
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -569,11 +618,6 @@ private:
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
|
||||
*/
|
||||
size_t callDepth = 0;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@@ -665,25 +709,9 @@ public:
|
||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
/**
|
||||
* Print statistics, if enabled.
|
||||
*
|
||||
* Performs a full memory GC before printing the statistics, so that the
|
||||
* GC statistics are more accurate.
|
||||
* Print statistics.
|
||||
*/
|
||||
void maybePrintStats();
|
||||
|
||||
/**
|
||||
* Print statistics, unconditionally, cheaply, without performing a GC first.
|
||||
*/
|
||||
void printStatistics();
|
||||
|
||||
/**
|
||||
* Perform a full memory garbage collection - not incremental.
|
||||
*
|
||||
* @return true if Nix was built with GC and a GC was performed, false if not.
|
||||
* The return value is currently not thread safe - just the return value.
|
||||
*/
|
||||
bool fullGC();
|
||||
void printStats();
|
||||
|
||||
/**
|
||||
* Realise the given context, and return a mapping from the placeholders
|
||||
@@ -755,6 +783,7 @@ struct DebugTraceStacker {
|
||||
DebugTraceStacker(EvalState & evalState, DebugTrace t);
|
||||
~DebugTraceStacker()
|
||||
{
|
||||
// assert(evalState.debugTraces.front() == trace);
|
||||
evalState.debugTraces.pop_front();
|
||||
}
|
||||
EvalState & evalState;
|
||||
@@ -775,8 +804,23 @@ std::string showType(const Value & v);
|
||||
*/
|
||||
SourcePath resolveExprPath(SourcePath path);
|
||||
|
||||
struct InvalidPathError : EvalError
|
||||
{
|
||||
Path path;
|
||||
InvalidPathError(const Path & path);
|
||||
#ifdef EXCEPTION_NEEDS_THROW_SPEC
|
||||
~InvalidPathError() throw () { };
|
||||
#endif
|
||||
};
|
||||
|
||||
static const std::string corepkgsPrefix{"/__corepkgs__/"};
|
||||
|
||||
template<class ErrorType>
|
||||
void ErrorBuilder::debugThrow()
|
||||
{
|
||||
// NOTE: We always use the -LastTrace version as we push the new trace in withFrame()
|
||||
state.debugThrowLastTrace(ErrorType(info));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+14
-19
@@ -140,15 +140,15 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
|
||||
break;
|
||||
default:
|
||||
state.error<TypeError>("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
state.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
state.symbols[attr.name], showType(*attr.value));
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace(
|
||||
state.positions[attr.pos],
|
||||
HintFmt("while evaluating flake attribute '%s'", state.symbols[attr.name]));
|
||||
hintfmt("while evaluating flake attribute '%s'", state.symbols[attr.name]));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
try {
|
||||
input.ref = FlakeRef::fromAttrs(attrs);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[pos], HintFmt("while evaluating flake input"));
|
||||
e.addTrace(state.positions[pos], hintfmt("while evaluating flake input"));
|
||||
throw;
|
||||
}
|
||||
else {
|
||||
@@ -205,16 +205,8 @@ static Flake getFlake(
|
||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
|
||||
// We need to guard against symlink attacks, but before we start doing
|
||||
// filesystem operations we should make sure there's a flake.nix in the
|
||||
// first place.
|
||||
auto unsafeFlakeDir = sourceInfo.actualPath + "/" + lockedRef.subdir;
|
||||
auto unsafeFlakeFile = unsafeFlakeDir + "/flake.nix";
|
||||
if (!pathExists(unsafeFlakeFile))
|
||||
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
|
||||
|
||||
// Guard against symlink attacks.
|
||||
auto flakeDir = canonPath(unsafeFlakeDir, true);
|
||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
|
||||
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
||||
@@ -227,6 +219,9 @@ static Flake getFlake(
|
||||
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
||||
};
|
||||
|
||||
if (!pathExists(flakeFile))
|
||||
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
|
||||
|
||||
Value vInfo;
|
||||
state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
|
||||
|
||||
@@ -288,15 +283,15 @@ static Flake getFlake(
|
||||
std::vector<std::string> ss;
|
||||
for (auto elem : setting.value->listItems()) {
|
||||
if (elem->type() != nString)
|
||||
state.error<TypeError>("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
state.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
state.symbols[setting.name], showType(*setting.value));
|
||||
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
|
||||
}
|
||||
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
||||
}
|
||||
else
|
||||
state.error<TypeError>("flake configuration setting '%s' is %s",
|
||||
state.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
throw TypeError("flake configuration setting '%s' is %s",
|
||||
state.symbols[setting.name], showType(*setting.value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,11 +852,11 @@ static void prim_flakeRefToString(
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
std::string(attr.value->str()));
|
||||
} else {
|
||||
state.error<EvalError>(
|
||||
state.error(
|
||||
"flake reference attribute sets may only contain integers, Booleans, "
|
||||
"and strings, but attribute '%s' is %s",
|
||||
state.symbols[attr.name],
|
||||
showType(*attr.value)).debugThrow();
|
||||
showType(*attr.value)).debugThrow<EvalError>();
|
||||
}
|
||||
}
|
||||
auto flakeRef = FlakeRef::fromAttrs(attrs);
|
||||
|
||||
@@ -89,7 +89,7 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
|
||||
std::string inputKey = i.value();
|
||||
auto k = nodeMap.find(inputKey);
|
||||
if (k == nodeMap.end()) {
|
||||
auto & nodes = json["nodes"];
|
||||
auto nodes = json["nodes"];
|
||||
auto jsonNode2 = nodes.find(inputKey);
|
||||
if (jsonNode2 == nodes.end())
|
||||
throw Error("lock file references missing node '%s'", inputKey);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
||||
#include <gc/gc.h>
|
||||
#include <gc/gc_cpp.h>
|
||||
#include <gc/gc_allocator.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct Value;
|
||||
|
||||
/**
|
||||
* A GC compatible vector that may used a reserved portion of `nItems` on the stack instead of allocating on the heap.
|
||||
*/
|
||||
#if HAVE_BOEHMGC
|
||||
template <typename T, size_t nItems>
|
||||
using SmallVector = boost::container::small_vector<T, nItems, traceable_allocator<T>>;
|
||||
#else
|
||||
template <typename T, size_t nItems>
|
||||
using SmallVector = boost::container::small_vector<T, nItems>;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A vector of value pointers. See `SmallVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
using SmallValueVector = SmallVector<Value *, nItems>;
|
||||
|
||||
/**
|
||||
* A vector of values that must not be referenced after the vector is destroyed.
|
||||
*
|
||||
* See also `SmallValueVector`.
|
||||
*/
|
||||
template <size_t nItems>
|
||||
using SmallTemporaryValueVector = SmallVector<Value, nItems>;
|
||||
|
||||
}
|
||||
+17
-20
@@ -50,7 +50,7 @@ std::string DrvInfo::queryName() const
|
||||
{
|
||||
if (name == "" && attrs) {
|
||||
auto i = attrs->find(state->sName);
|
||||
if (i == attrs->end()) state->error<TypeError>("derivation name missing").debugThrow();
|
||||
if (i == attrs->end()) throw TypeError("derivation name missing");
|
||||
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||
}
|
||||
return name;
|
||||
@@ -199,7 +199,7 @@ StringSet DrvInfo::queryMetaNames()
|
||||
|
||||
bool DrvInfo::checkMeta(Value & v)
|
||||
{
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||
if (v.type() == nList) {
|
||||
for (auto elem : v.listItems())
|
||||
if (!checkMeta(*elem)) return false;
|
||||
@@ -296,16 +296,22 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
|
||||
typedef std::set<Bindings *> Done;
|
||||
|
||||
|
||||
/* The result boolean indicates whether it makes sense
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs' (unless it's already in `done').
|
||||
The result boolean indicates whether it makes sense
|
||||
for the caller to recursively search for derivations in `v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const std::string & attrPath, DrvInfos & drvs,
|
||||
const std::string & attrPath, DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
try {
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
state.forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||
derivation {...}; y = x;}'. */
|
||||
if (!done.insert(v.attrs).second) return false;
|
||||
|
||||
DrvInfo drv(state, attrPath, v.attrs);
|
||||
|
||||
drv.queryName();
|
||||
@@ -324,8 +330,9 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
std::optional<DrvInfo> getDerivation(EvalState & state, Value & v,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, v, "", drvs, ignoreAssertionFailures);
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return {};
|
||||
return std::move(drvs.front());
|
||||
}
|
||||
@@ -340,9 +347,6 @@ static std::string addToPath(const std::string & s1, const std::string & s2)
|
||||
static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*");
|
||||
|
||||
|
||||
/* Evaluate value `v'. If it evaluates to a set of type `derivation',
|
||||
then put information about it in `drvs'. If it evaluates to a different
|
||||
kind of set recurse (unless it's already in `done'). */
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const std::string & pathPrefix, Bindings & autoArgs,
|
||||
DrvInfos & drvs, Done & done,
|
||||
@@ -352,14 +356,10 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
/* Process the expression. */
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, ignoreAssertionFailures)) ;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
|
||||
else if (v.type() == nAttrs) {
|
||||
|
||||
/* Dont consider sets we've already seen, e.g. y in
|
||||
`rec { x.d = derivation {...}; y = x; }`. */
|
||||
if (!done.insert(v.attrs).second) return;
|
||||
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
|
||||
@@ -376,7 +376,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
|
||||
if (combineChannels)
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, *i->value, pathPrefix2, drvs, ignoreAssertionFailures)) {
|
||||
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
/* If the value of this attribute is itself a set,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
@@ -390,17 +390,14 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
}
|
||||
|
||||
else if (v.type() == nList) {
|
||||
// NOTE we can't really deduplicate here because small lists don't have stable addresses
|
||||
// and can cause spurious duplicate detections due to v being on the stack.
|
||||
for (auto [n, elem] : enumerate(v.listItems())) {
|
||||
std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
|
||||
if (getDerivation(state, *elem, pathPrefix2, drvs, ignoreAssertionFailures))
|
||||
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
state.error<TypeError>("expression does not evaluate to a derivation (or a set or list of those)").debugThrow();
|
||||
else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "json-to-value.hh"
|
||||
#include "value.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
#include <variant>
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -161,7 +159,7 @@ public:
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) {
|
||||
throw JSONParseError("%s", ex.what());
|
||||
throw JSONParseError(ex.what());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "error.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
class EvalState;
|
||||
struct Value;
|
||||
|
||||
MakeError(JSONParseError, Error);
|
||||
MakeError(JSONParseError, EvalError);
|
||||
|
||||
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
|
||||
|
||||
|
||||
+23
-22
@@ -1,5 +1,4 @@
|
||||
%option reentrant bison-bridge bison-locations
|
||||
%option align
|
||||
%option noyywrap
|
||||
%option never-interactive
|
||||
%option stack
|
||||
@@ -20,12 +19,6 @@
|
||||
#pragma clang diagnostic ignored "-Wunneeded-internal-declaration"
|
||||
#endif
|
||||
|
||||
// yacc generates code that uses unannotated fallthrough.
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#endif
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "nixexpr.hh"
|
||||
@@ -35,7 +28,15 @@ using namespace nix;
|
||||
|
||||
namespace nix {
|
||||
|
||||
#define CUR_POS state->at(*yylloc)
|
||||
static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||
{
|
||||
return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
|
||||
}
|
||||
|
||||
#define CUR_POS makeCurPos(*yylloc, data)
|
||||
|
||||
// backup to recover from yyless(0)
|
||||
thread_local YYLTYPE prev_yylloc;
|
||||
|
||||
static void initLoc(YYLTYPE * loc)
|
||||
{
|
||||
@@ -45,7 +46,7 @@ static void initLoc(YYLTYPE * loc)
|
||||
|
||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
{
|
||||
loc->stash();
|
||||
prev_yylloc = *loc;
|
||||
|
||||
loc->first_line = loc->last_line;
|
||||
loc->first_column = loc->last_column;
|
||||
@@ -152,9 +153,9 @@ or { return OR_KW; }
|
||||
try {
|
||||
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
||||
} catch (const boost::bad_lexical_cast &) {
|
||||
throw ParseError(ErrorInfo{
|
||||
.msg = HintFmt("invalid integer '%1%'", yytext),
|
||||
.pos = state->positions[CUR_POS],
|
||||
throw ParseError({
|
||||
.msg = hintfmt("invalid integer '%1%'", yytext),
|
||||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
}
|
||||
return INT;
|
||||
@@ -162,9 +163,9 @@ or { return OR_KW; }
|
||||
{FLOAT} { errno = 0;
|
||||
yylval->nf = strtod(yytext, 0);
|
||||
if (errno != 0)
|
||||
throw ParseError(ErrorInfo{
|
||||
.msg = HintFmt("invalid float '%1%'", yytext),
|
||||
.pos = state->positions[CUR_POS],
|
||||
throw ParseError({
|
||||
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
return FLOAT;
|
||||
}
|
||||
@@ -187,7 +188,7 @@ or { return OR_KW; }
|
||||
/* It is impossible to match strings ending with '$' with one
|
||||
regex because trailing contexts are only valid at the end
|
||||
of a rule. (A sane but undocumented limitation.) */
|
||||
yylval->str = unescapeStr(state->symbols, yytext, yyleng);
|
||||
yylval->str = unescapeStr(data->symbols, yytext, yyleng);
|
||||
return STR;
|
||||
}
|
||||
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||
@@ -215,7 +216,7 @@ or { return OR_KW; }
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\\{ANY} {
|
||||
yylval->str = unescapeStr(state->symbols, yytext + 2, yyleng - 2);
|
||||
yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||
@@ -229,7 +230,7 @@ or { return OR_KW; }
|
||||
{HPATH_START}\$\{ {
|
||||
PUSH_STATE(PATH_START);
|
||||
yyless(0);
|
||||
yylloc->unstash();
|
||||
*yylloc = prev_yylloc;
|
||||
}
|
||||
|
||||
<PATH_START>{PATH_SEG} {
|
||||
@@ -285,15 +286,15 @@ or { return OR_KW; }
|
||||
context (it may be ')', ';', or something of that sort) */
|
||||
POP_STATE();
|
||||
yyless(0);
|
||||
yylloc->unstash();
|
||||
*yylloc = prev_yylloc;
|
||||
return PATH_END;
|
||||
}
|
||||
|
||||
<INPATH_SLASH>{ANY} |
|
||||
<INPATH_SLASH><<EOF>> {
|
||||
throw ParseError(ErrorInfo{
|
||||
.msg = HintFmt("path has a trailing slash"),
|
||||
.pos = state->positions[CUR_POS],
|
||||
throw ParseError({
|
||||
.msg = hintfmt("path has a trailing slash"),
|
||||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -36,15 +36,15 @@ $(d)/lexer-tab.cc $(d)/lexer-tab.hh: $(d)/lexer.l
|
||||
|
||||
clean-files += $(d)/parser-tab.cc $(d)/parser-tab.hh $(d)/lexer-tab.cc $(d)/lexer-tab.hh
|
||||
|
||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-expr.pc, $(libdir)/pkgconfig, 0644))
|
||||
$(eval $(call install-file-in, $(d)/nix-expr.pc, $(libdir)/pkgconfig, 0644))
|
||||
|
||||
$(foreach i, $(wildcard src/libexpr/value/*.hh), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/value, 0644)))
|
||||
$(foreach i, $(wildcard src/libexpr/flake/*.hh), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644)))
|
||||
|
||||
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh
|
||||
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
|
||||
|
||||
$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/flake/call-flake.nix.gen.hh
|
||||
$(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh
|
||||
|
||||
$(buildprefix)src/libexpr/primops/fromTOML.o: ERROR_SWITCH_ENUM =
|
||||
src/libexpr/primops/fromTOML.o: ERROR_SWITCH_ENUM =
|
||||
|
||||
+102
-105
@@ -9,7 +9,57 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
ExprBlackHole eBlackHole;
|
||||
struct PosAdapter : AbstractPos
|
||||
{
|
||||
Pos::Origin origin;
|
||||
|
||||
PosAdapter(Pos::Origin origin)
|
||||
: origin(std::move(origin))
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<std::string> getSource() const override
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[](const Pos::none_tag &) -> std::optional<std::string> {
|
||||
return std::nullopt;
|
||||
},
|
||||
[](const Pos::Stdin & s) -> std::optional<std::string> {
|
||||
// Get rid of the null terminators added by the parser.
|
||||
return std::string(s.source->c_str());
|
||||
},
|
||||
[](const Pos::String & s) -> std::optional<std::string> {
|
||||
// Get rid of the null terminators added by the parser.
|
||||
return std::string(s.source->c_str());
|
||||
},
|
||||
[](const SourcePath & path) -> std::optional<std::string> {
|
||||
try {
|
||||
return path.readFile();
|
||||
} catch (Error &) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}, origin);
|
||||
}
|
||||
|
||||
void print(std::ostream & out) const override
|
||||
{
|
||||
std::visit(overloaded {
|
||||
[&](const Pos::none_tag &) { out << "«none»"; },
|
||||
[&](const Pos::Stdin &) { out << "«stdin»"; },
|
||||
[&](const Pos::String & s) { out << "«string»"; },
|
||||
[&](const SourcePath & path) { out << path; }
|
||||
}, origin);
|
||||
}
|
||||
};
|
||||
|
||||
Pos::operator std::shared_ptr<AbstractPos>() const
|
||||
{
|
||||
auto pos = std::make_shared<PosAdapter>(origin);
|
||||
pos->line = line;
|
||||
pos->column = column;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// FIXME: remove, because *symbols* are abstract and do not have a single
|
||||
// textual representation; see printIdentifier()
|
||||
@@ -68,8 +118,10 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
|
||||
}
|
||||
|
||||
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
|
||||
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
typedef const decltype(attrs)::value_type * Attr;
|
||||
std::vector<Attr> sorted;
|
||||
for (auto & i : attrs) sorted.push_back(&i);
|
||||
@@ -77,37 +129,10 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||
std::string_view sa = symbols[a->first], sb = symbols[b->first];
|
||||
return sa < sb;
|
||||
});
|
||||
std::vector<Symbol> inherits;
|
||||
std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
|
||||
for (auto & i : sorted) {
|
||||
switch (i->second.kind) {
|
||||
case AttrDef::Kind::Plain:
|
||||
break;
|
||||
case AttrDef::Kind::Inherited:
|
||||
inherits.push_back(i->first);
|
||||
break;
|
||||
case AttrDef::Kind::InheritedFrom: {
|
||||
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
|
||||
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
|
||||
inheritsFrom[&from].push_back(i->first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!inherits.empty()) {
|
||||
str << "inherit";
|
||||
for (auto sym : inherits) str << " " << symbols[sym];
|
||||
str << "; ";
|
||||
}
|
||||
for (const auto & [from, syms] : inheritsFrom) {
|
||||
str << "inherit (";
|
||||
(*inheritFromExprs)[from->displ]->show(symbols, str);
|
||||
str << ")";
|
||||
for (auto sym : syms) str << " " << symbols[sym];
|
||||
str << "; ";
|
||||
}
|
||||
for (auto & i : sorted) {
|
||||
if (i->second.kind == AttrDef::Kind::Plain) {
|
||||
if (i->second.inherited)
|
||||
str << "inherit " << symbols[i->first] << " " << "; ";
|
||||
else {
|
||||
str << symbols[i->first] << " = ";
|
||||
i->second.e->show(symbols, str);
|
||||
str << "; ";
|
||||
@@ -120,13 +145,6 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||
i.valueExpr->show(symbols, str);
|
||||
str << "; ";
|
||||
}
|
||||
}
|
||||
|
||||
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
showBindings(symbols, str);
|
||||
str << "}";
|
||||
}
|
||||
|
||||
@@ -182,7 +200,15 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << "(let ";
|
||||
attrs->showBindings(symbols, str);
|
||||
for (auto & i : attrs->attrs)
|
||||
if (i.second.inherited) {
|
||||
str << "inherit " << symbols[i.first] << "; ";
|
||||
}
|
||||
else {
|
||||
str << symbols[i.first] << " = ";
|
||||
i.second.e->show(symbols, str);
|
||||
str << "; ";
|
||||
}
|
||||
str << "in ";
|
||||
body->show(symbols, str);
|
||||
str << ")";
|
||||
@@ -240,6 +266,17 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
}
|
||||
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||
{
|
||||
if (auto pos2 = (std::shared_ptr<AbstractPos>) pos) {
|
||||
str << *pos2;
|
||||
} else
|
||||
str << "undefined position";
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||
{
|
||||
std::ostringstream out;
|
||||
@@ -294,8 +331,6 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
fromWith = nullptr;
|
||||
|
||||
/* Check whether the variable appears in the environment. If so,
|
||||
set its level and displacement. */
|
||||
const StaticEnv * curEnv;
|
||||
@@ -307,6 +342,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
} else {
|
||||
auto i = curEnv->find(name);
|
||||
if (i != curEnv->vars.end()) {
|
||||
fromWith = false;
|
||||
this->level = level;
|
||||
displ = i->second;
|
||||
return;
|
||||
@@ -318,21 +354,14 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1)
|
||||
es.error<UndefinedVarError>(
|
||||
"undefined variable '%1%'",
|
||||
es.symbols[name]
|
||||
).atPos(pos).debugThrow();
|
||||
for (auto * e = env.get(); e && !fromWith; e = e->up)
|
||||
fromWith = e->isWith;
|
||||
throw UndefinedVarError({
|
||||
.msg = hintfmt("undefined variable '%1%'", es.symbols[name]),
|
||||
.errPos = es.positions[pos]
|
||||
});
|
||||
fromWith = true;
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
@@ -356,47 +385,22 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
|
||||
i.expr->bindVars(es, env);
|
||||
}
|
||||
|
||||
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (!inheritFromExprs)
|
||||
return nullptr;
|
||||
|
||||
// the inherit (from) source values are inserted into an env of its own, which
|
||||
// does not introduce any variable names.
|
||||
// analysis must see an empty env, or an env that contains only entries with
|
||||
// otherwise unused names to not interfere with regular names. the parser
|
||||
// has already filled all exprs that access this env with appropriate level
|
||||
// and displacement, and nothing else is allowed to access it. ideally we'd
|
||||
// not even *have* an expr that grabs anything from this env since it's fully
|
||||
// invisible, but the evaluator does not allow for this yet.
|
||||
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
|
||||
for (auto from : *inheritFromExprs)
|
||||
from->bindVars(es, env);
|
||||
|
||||
return inner;
|
||||
}
|
||||
|
||||
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
if (recursive) {
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
|
||||
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++);
|
||||
return newEnv;
|
||||
}();
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
|
||||
// No need to sort newEnv since attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
i.nameExpr->bindVars(es, newEnv);
|
||||
@@ -404,10 +408,8 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto inheritFromEnv = bindInheritSources(es, env);
|
||||
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
|
||||
i.second.e->bindVars(es, env);
|
||||
|
||||
for (auto & i : dynamicAttrs) {
|
||||
i.nameExpr->bindVars(es, env);
|
||||
@@ -431,7 +433,7 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
auto newEnv = std::make_shared<StaticEnv>(
|
||||
nullptr, env.get(),
|
||||
false, env.get(),
|
||||
(hasFormals() ? formals->formals.size() : 0) +
|
||||
(!arg ? 0 : 1));
|
||||
|
||||
@@ -464,23 +466,19 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
|
||||
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
return newEnv;
|
||||
}();
|
||||
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++);
|
||||
|
||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs->attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, newEnv));
|
||||
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
@@ -490,10 +488,6 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
parentWith = nullptr;
|
||||
for (auto * e = env.get(); e && !parentWith; e = e->up)
|
||||
parentWith = e->isWith;
|
||||
|
||||
/* 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. */
|
||||
@@ -506,8 +500,11 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
break;
|
||||
}
|
||||
|
||||
if (es.debugRepl)
|
||||
es.exprEnvs.insert(std::make_pair(this, env));
|
||||
|
||||
attrs->bindVars(es, env);
|
||||
auto newEnv = std::make_shared<StaticEnv>(this, env.get());
|
||||
auto newEnv = std::make_shared<StaticEnv>(true, env.get());
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
|
||||
+126
-81
@@ -8,18 +8,130 @@
|
||||
#include "symbol-table.hh"
|
||||
#include "error.hh"
|
||||
#include "chunked-vector.hh"
|
||||
#include "position.hh"
|
||||
#include "eval-error.hh"
|
||||
#include "pos-idx.hh"
|
||||
#include "pos-table.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
MakeError(EvalError, Error);
|
||||
MakeError(ParseError, Error);
|
||||
MakeError(AssertionError, EvalError);
|
||||
MakeError(ThrownError, AssertionError);
|
||||
MakeError(Abort, EvalError);
|
||||
MakeError(TypeError, EvalError);
|
||||
MakeError(UndefinedVarError, Error);
|
||||
MakeError(MissingArgumentError, EvalError);
|
||||
MakeError(RestrictedPathError, Error);
|
||||
|
||||
/**
|
||||
* Position objects.
|
||||
*/
|
||||
struct Pos
|
||||
{
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
|
||||
struct none_tag { };
|
||||
struct Stdin { ref<std::string> source; };
|
||||
struct String { ref<std::string> source; };
|
||||
|
||||
typedef std::variant<none_tag, Stdin, String, SourcePath> Origin;
|
||||
|
||||
Origin origin;
|
||||
|
||||
explicit operator bool() const { return line > 0; }
|
||||
|
||||
operator std::shared_ptr<AbstractPos>() const;
|
||||
};
|
||||
|
||||
class PosIdx {
|
||||
friend class PosTable;
|
||||
|
||||
private:
|
||||
uint32_t id;
|
||||
|
||||
explicit PosIdx(uint32_t id): id(id) {}
|
||||
|
||||
public:
|
||||
PosIdx() : id(0) {}
|
||||
|
||||
explicit operator bool() const { return id > 0; }
|
||||
|
||||
bool operator <(const PosIdx other) const { return id < other.id; }
|
||||
|
||||
bool operator ==(const PosIdx other) const { return id == other.id; }
|
||||
|
||||
bool operator !=(const PosIdx other) const { return id != other.id; }
|
||||
};
|
||||
|
||||
class PosTable
|
||||
{
|
||||
public:
|
||||
class Origin {
|
||||
friend PosTable;
|
||||
private:
|
||||
// must always be invalid by default, add() replaces this with the actual value.
|
||||
// subsequent add() calls use this index as a token to quickly check whether the
|
||||
// current origins.back() can be reused or not.
|
||||
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
// Used for searching in PosTable::[].
|
||||
explicit Origin(uint32_t idx): idx(idx), origin{Pos::none_tag()} {}
|
||||
|
||||
public:
|
||||
const Pos::Origin origin;
|
||||
|
||||
Origin(Pos::Origin origin): origin(origin) {}
|
||||
};
|
||||
|
||||
struct Offset {
|
||||
uint32_t line, column;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Origin> origins;
|
||||
ChunkedVector<Offset, 8192> offsets;
|
||||
|
||||
public:
|
||||
PosTable(): offsets(1024)
|
||||
{
|
||||
origins.reserve(1024);
|
||||
}
|
||||
|
||||
PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
|
||||
{
|
||||
const auto idx = offsets.add({line, column}).second;
|
||||
if (origins.empty() || origins.back().idx != origin.idx) {
|
||||
origin.idx = idx;
|
||||
origins.push_back(origin);
|
||||
}
|
||||
return PosIdx(idx + 1);
|
||||
}
|
||||
|
||||
Pos operator[](PosIdx p) const
|
||||
{
|
||||
if (p.id == 0 || p.id > offsets.size())
|
||||
return {};
|
||||
const auto idx = p.id - 1;
|
||||
/* we want the last key <= idx, so we'll take prev(first key > idx).
|
||||
this is guaranteed to never rewind origin.begin because the first
|
||||
key is always 0. */
|
||||
const auto pastOrigin = std::upper_bound(
|
||||
origins.begin(), origins.end(), Origin(idx),
|
||||
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
|
||||
const auto origin = *std::prev(pastOrigin);
|
||||
const auto offset = offsets[idx];
|
||||
return {offset.line, offset.column, origin.origin};
|
||||
}
|
||||
};
|
||||
|
||||
inline PosIdx noPos = {};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
||||
|
||||
|
||||
struct Env;
|
||||
struct Value;
|
||||
class EvalState;
|
||||
struct ExprWith;
|
||||
struct StaticEnv;
|
||||
|
||||
|
||||
@@ -43,11 +155,6 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||
|
||||
struct Expr
|
||||
{
|
||||
struct AstSymbols {
|
||||
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
|
||||
};
|
||||
|
||||
|
||||
virtual ~Expr() { };
|
||||
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||
virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
@@ -107,11 +214,8 @@ struct ExprVar : Expr
|
||||
Symbol name;
|
||||
|
||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||
or function argument) or from a "with".
|
||||
|
||||
`nullptr`: Not from a `with`.
|
||||
Valid pointer: the nearest, innermost `with` expression to query first. */
|
||||
ExprWith * fromWith;
|
||||
or function argument) or from a "with". */
|
||||
bool fromWith;
|
||||
|
||||
/* In the former case, the value is obtained by going `level`
|
||||
levels up from the current environment and getting the
|
||||
@@ -129,29 +233,12 @@ struct ExprVar : Expr
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
/**
|
||||
* A pseudo-expression for the purpose of evaluating the `from` expression in `inherit (from)` syntax.
|
||||
* Unlike normal variable references, the displacement is set during parsing, and always refers to
|
||||
* `ExprAttrs::inheritFromExprs` (by itself or in `ExprLet`), whose values are put into their own `Env`.
|
||||
*/
|
||||
struct ExprInheritFrom : ExprVar
|
||||
{
|
||||
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
|
||||
{
|
||||
this->level = 0;
|
||||
this->displ = displ;
|
||||
this->fromWith = nullptr;
|
||||
}
|
||||
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
};
|
||||
|
||||
struct ExprSelect : Expr
|
||||
{
|
||||
PosIdx pos;
|
||||
Expr * e, * def;
|
||||
AttrPath attrPath;
|
||||
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
|
||||
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath && attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(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
|
||||
@@ -161,7 +248,7 @@ struct ExprOpHasAttr : Expr
|
||||
{
|
||||
Expr * e;
|
||||
AttrPath attrPath;
|
||||
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||
ExprOpHasAttr(Expr * e, const AttrPath && attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
COMMON_METHODS
|
||||
};
|
||||
@@ -171,40 +258,16 @@ struct ExprAttrs : Expr
|
||||
bool recursive;
|
||||
PosIdx pos;
|
||||
struct AttrDef {
|
||||
enum class Kind {
|
||||
/** `attr = expr;` */
|
||||
Plain,
|
||||
/** `inherit attr1 attrn;` */
|
||||
Inherited,
|
||||
/** `inherit (expr) attr1 attrn;` */
|
||||
InheritedFrom,
|
||||
};
|
||||
|
||||
Kind kind;
|
||||
bool inherited;
|
||||
Expr * e;
|
||||
PosIdx pos;
|
||||
Displacement displ; // displacement
|
||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||
: kind(kind), e(e), pos(pos) { };
|
||||
AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
|
||||
: inherited(inherited), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
|
||||
template<typename T>
|
||||
const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
|
||||
{
|
||||
switch (kind) {
|
||||
case Kind::Plain:
|
||||
return plain;
|
||||
case Kind::Inherited:
|
||||
return inherited;
|
||||
default:
|
||||
case Kind::InheritedFrom:
|
||||
return inheritedFrom;
|
||||
}
|
||||
}
|
||||
};
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
AttrDefs attrs;
|
||||
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
|
||||
struct DynamicAttrDef {
|
||||
Expr * nameExpr, * valueExpr;
|
||||
PosIdx pos;
|
||||
@@ -217,11 +280,6 @@ struct ExprAttrs : Expr
|
||||
ExprAttrs() : recursive(false) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Env * buildInheritFromEnv(EvalState & state, Env & up);
|
||||
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
|
||||
};
|
||||
|
||||
struct ExprList : Expr
|
||||
@@ -229,7 +287,6 @@ struct ExprList : Expr
|
||||
std::vector<Expr *> elems;
|
||||
ExprList() { };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||
|
||||
PosIdx getPos() const override
|
||||
{
|
||||
@@ -316,7 +373,6 @@ struct ExprWith : Expr
|
||||
PosIdx pos;
|
||||
Expr * attrs, * body;
|
||||
size_t prevWith;
|
||||
ExprWith * parentWith;
|
||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
@@ -344,7 +400,6 @@ struct ExprOpNot : Expr
|
||||
{
|
||||
Expr * e;
|
||||
ExprOpNot(Expr * e) : e(e) { };
|
||||
PosIdx getPos() const override { return e->getPos(); }
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -394,30 +449,20 @@ struct ExprPos : Expr
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
/* only used to mark thunks as black holes. */
|
||||
struct ExprBlackHole : Expr
|
||||
{
|
||||
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 {}
|
||||
};
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
|
||||
|
||||
/* Static environments are used to map variable names onto (level,
|
||||
displacement) pairs used to obtain the value of the variable at
|
||||
runtime. */
|
||||
struct StaticEnv
|
||||
{
|
||||
ExprWith * isWith;
|
||||
bool isWith;
|
||||
const StaticEnv * up;
|
||||
|
||||
// Note: these must be in sorted order.
|
||||
typedef std::vector<std::pair<Symbol, Displacement>> Vars;
|
||||
Vars vars;
|
||||
|
||||
StaticEnv(ExprWith * isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
|
||||
StaticEnv(bool isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
|
||||
vars.reserve(expectedSize);
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user