forked from lix-project/lix
Merge branch 'path-info' into ca-drv-exotic
This commit is contained in:
commit
5abd643c6d
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
|
@ -5,7 +5,7 @@
|
||||||
# For documentation on this mechanism, see https://help.github.com/articles/about-codeowners/
|
# For documentation on this mechanism, see https://help.github.com/articles/about-codeowners/
|
||||||
|
|
||||||
# Default reviewers if nothing else matches
|
# Default reviewers if nothing else matches
|
||||||
* @edolstra @thufschmitt
|
* @edolstra
|
||||||
|
|
||||||
# This file
|
# This file
|
||||||
.github/CODEOWNERS @edolstra
|
.github/CODEOWNERS @edolstra
|
||||||
|
@ -13,3 +13,6 @@
|
||||||
# Public documentation
|
# Public documentation
|
||||||
/doc @fricklerhandwerk
|
/doc @fricklerhandwerk
|
||||||
*.md @fricklerhandwerk
|
*.md @fricklerhandwerk
|
||||||
|
|
||||||
|
# Libstore layer
|
||||||
|
/src/libstore @thufschmitt
|
||||||
|
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -21,8 +21,8 @@ Maintainers: tick if completed or explain if not relevant
|
||||||
- [ ] tests, as appropriate
|
- [ ] tests, as appropriate
|
||||||
- functional tests - `tests/**.sh`
|
- functional tests - `tests/**.sh`
|
||||||
- unit tests - `src/*/tests`
|
- unit tests - `src/*/tests`
|
||||||
- integration tests
|
- integration tests - `tests/nixos/*`
|
||||||
- [ ] documentation in the manual
|
- [ ] documentation in the manual
|
||||||
- [ ] code and comments are self-explanatory
|
- [ ] code and comments are self-explanatory
|
||||||
- [ ] commit message explains why the change was made
|
- [ ] commit message explains why the change was made
|
||||||
- [ ] new feature or bug fix: updated release notes
|
- [ ] new feature or incompatible change: updated release notes
|
||||||
|
|
4
.github/workflows/backport.yml
vendored
4
.github/workflows/backport.yml
vendored
|
@ -21,12 +21,12 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
# should be kept in sync with `version`
|
# should be kept in sync with `version`
|
||||||
uses: zeebe-io/backport-action@v1.0.1
|
uses: zeebe-io/backport-action@v1.2.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
github_workspace: ${{ github.workspace }}
|
github_workspace: ${{ github.workspace }}
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
Automatic backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
||||||
# should be kept in sync with `uses`
|
# should be kept in sync with `uses`
|
||||||
version: v0.0.5
|
version: v0.0.5
|
||||||
|
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: cachix/install-nix-action@v18
|
- uses: cachix/install-nix-action@v19
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/cachix-action@v12
|
- uses: cachix/cachix-action@v12
|
||||||
if: needs.check_secrets.outputs.cachix == 'true'
|
if: needs.check_secrets.outputs.cachix == 'true'
|
||||||
|
@ -58,7 +58,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/install-nix-action@v18
|
- uses: cachix/install-nix-action@v19
|
||||||
- uses: cachix/cachix-action@v12
|
- uses: cachix/cachix-action@v12
|
||||||
with:
|
with:
|
||||||
name: '${{ env.CACHIX_NAME }}'
|
name: '${{ env.CACHIX_NAME }}'
|
||||||
|
@ -77,7 +77,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/install-nix-action@v18
|
- uses: cachix/install-nix-action@v19
|
||||||
with:
|
with:
|
||||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||||
|
@ -102,7 +102,7 @@ jobs:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: cachix/install-nix-action@v18
|
- uses: cachix/install-nix-action@v19
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV
|
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV
|
||||||
- uses: cachix/cachix-action@v12
|
- uses: cachix/cachix-action@v12
|
||||||
|
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -37,14 +37,14 @@ perl/Makefile.config
|
||||||
/src/libexpr/parser-tab.hh
|
/src/libexpr/parser-tab.hh
|
||||||
/src/libexpr/parser-tab.output
|
/src/libexpr/parser-tab.output
|
||||||
/src/libexpr/nix.tbl
|
/src/libexpr/nix.tbl
|
||||||
/src/libexpr/tests/libexpr-tests
|
/src/libexpr/tests/libnixexpr-tests
|
||||||
|
|
||||||
# /src/libstore/
|
# /src/libstore/
|
||||||
*.gen.*
|
*.gen.*
|
||||||
/src/libstore/tests/libstore-tests
|
/src/libstore/tests/libnixstore-tests
|
||||||
|
|
||||||
# /src/libutil/
|
# /src/libutil/
|
||||||
/src/libutil/tests/libutil-tests
|
/src/libutil/tests/libnixutil-tests
|
||||||
|
|
||||||
/src/nix/nix
|
/src/nix/nix
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ perl/Makefile.config
|
||||||
|
|
||||||
# /tests/
|
# /tests/
|
||||||
/tests/test-tmp
|
/tests/test-tmp
|
||||||
/tests/common.sh
|
/tests/common/vars-and-functions.sh
|
||||||
/tests/result*
|
/tests/result*
|
||||||
/tests/restricted-innocent
|
/tests/restricted-innocent
|
||||||
/tests/shell
|
/tests/shell
|
||||||
|
@ -103,6 +103,7 @@ outputs/
|
||||||
|
|
||||||
*.a
|
*.a
|
||||||
*.o
|
*.o
|
||||||
|
*.o.tmp
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -36,4 +36,4 @@ endif
|
||||||
|
|
||||||
include mk/lib.mk
|
include mk/lib.mk
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++17 -I src
|
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++2a -I src
|
||||||
|
|
|
@ -20,8 +20,8 @@ Information on additional installation methods is available on the [Nix download
|
||||||
|
|
||||||
## Building And Developing
|
## Building And Developing
|
||||||
|
|
||||||
See our [Hacking guide](https://nixos.org/manual/nix/stable/contributing/hacking.html) in our manual for instruction on how to
|
See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to
|
||||||
build nix from source with nix-build or how to get a development environment.
|
to set up a development environment and build Nix from source.
|
||||||
|
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
|
|
77
boehmgc-coroutine-sp-fallback.diff
Normal file
77
boehmgc-coroutine-sp-fallback.diff
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
|
||||||
|
index 3dbaa3fb..36a1d1f7 100644
|
||||||
|
--- a/darwin_stop_world.c
|
||||||
|
+++ b/darwin_stop_world.c
|
||||||
|
@@ -352,6 +352,7 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
int nthreads = 0;
|
||||||
|
word total_size = 0;
|
||||||
|
mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ;
|
||||||
|
+ size_t stack_limit;
|
||||||
|
if (!EXPECT(GC_thr_initialized, TRUE))
|
||||||
|
GC_thr_init();
|
||||||
|
|
||||||
|
@@ -407,6 +408,19 @@ GC_INNER void GC_push_all_stacks(void)
|
||||||
|
GC_push_all_stack_sections(lo, hi, p->traced_stack_sect);
|
||||||
|
}
|
||||||
|
if (altstack_lo) {
|
||||||
|
+ // When a thread goes into a coroutine, we lose its original sp until
|
||||||
|
+ // control flow returns to the thread.
|
||||||
|
+ // While in the coroutine, the sp points outside the thread stack,
|
||||||
|
+ // so we can detect this and push the entire thread stack instead,
|
||||||
|
+ // as an approximation.
|
||||||
|
+ // We assume that the coroutine has similarly added its entire stack.
|
||||||
|
+ // This could be made accurate by cooperating with the application
|
||||||
|
+ // via new functions and/or callbacks.
|
||||||
|
+ stack_limit = pthread_get_stacksize_np(p->id);
|
||||||
|
+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack
|
||||||
|
+ altstack_lo = altstack_hi - stack_limit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
total_size += altstack_hi - altstack_lo;
|
||||||
|
GC_push_all_stack(altstack_lo, altstack_hi);
|
||||||
|
}
|
||||||
|
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
|
||||||
|
index b5d71e62..aed7b0bf 100644
|
||||||
|
--- a/pthread_stop_world.c
|
||||||
|
+++ b/pthread_stop_world.c
|
||||||
|
@@ -768,6 +768,8 @@ STATIC void GC_restart_handler(int sig)
|
||||||
|
/* world is stopped. Should not fail if it isn't. */
|
||||||
|
GC_INNER void GC_push_all_stacks(void)
|
||||||
|
{
|
||||||
|
+ size_t stack_limit;
|
||||||
|
+ pthread_attr_t pattr;
|
||||||
|
GC_bool found_me = FALSE;
|
||||||
|
size_t nthreads = 0;
|
||||||
|
int i;
|
||||||
|
@@ -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 {
|
||||||
|
+ if (pthread_getattr_np(p->id, &pattr)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
||||||
|
+ }
|
||||||
|
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
|
||||||
|
+ }
|
||||||
|
+ if (pthread_attr_destroy(&pattr)) {
|
||||||
|
+ ABORT("GC_push_all_stacks: pthread_attr_destroy failed!");
|
||||||
|
+ }
|
||||||
|
+ // When a thread goes into a coroutine, we lose its original sp until
|
||||||
|
+ // control flow returns to the thread.
|
||||||
|
+ // While in the coroutine, the sp points outside the thread stack,
|
||||||
|
+ // so we can detect this and push the entire thread stack instead,
|
||||||
|
+ // as an approximation.
|
||||||
|
+ // We assume that the coroutine has similarly added its entire stack.
|
||||||
|
+ // This could be made accurate by cooperating with the application
|
||||||
|
+ // via new functions and/or callbacks.
|
||||||
|
+ #ifndef STACK_GROWS_UP
|
||||||
|
+ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack
|
||||||
|
+ lo = hi - stack_limit;
|
||||||
|
+ }
|
||||||
|
+ #else
|
||||||
|
+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
|
||||||
|
+ #endif
|
||||||
|
}
|
||||||
|
GC_push_all_stack_sections(lo, hi, traced_stack_sect);
|
||||||
|
# ifdef STACK_GROWS_UP
|
|
@ -276,8 +276,11 @@ PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||||
|
|
||||||
# Look for rapidcheck.
|
# Look for rapidcheck.
|
||||||
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
|
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
|
||||||
|
AC_LANG_PUSH(C++)
|
||||||
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
|
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
|
||||||
AC_CHECK_LIB([rapidcheck], [])
|
dnl No good for C++ libs with mangled symbols
|
||||||
|
dnl AC_CHECK_LIB([rapidcheck], [])
|
||||||
|
AC_LANG_POP(C++)
|
||||||
|
|
||||||
|
|
||||||
# Look for nlohmann/json.
|
# Look for nlohmann/json.
|
||||||
|
|
|
@ -5,3 +5,7 @@ h1:not(:first-of-type) {
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hljs-meta {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
|
@ -51,13 +51,13 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
|
||||||
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
|
||||||
@rm -rf $@
|
@rm -rf $@
|
||||||
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix { toplevel = builtins.readFile $<; }'
|
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix { toplevel = builtins.readFile $<; }'
|
||||||
# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
@# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
||||||
$(trace-gen) sed -i $@.tmp/*.md -e 's^@docroot@^../..^g'
|
$(trace-gen) sed -i $@.tmp/*.md -e 's^@docroot@^../..^g'
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
|
||||||
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/generate-options.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
|
||||||
# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
@# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
||||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-options.nix (builtins.fromJSON (builtins.readFile $<))' \
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-options.nix (builtins.fromJSON (builtins.readFile $<))' \
|
||||||
| sed -e 's^@docroot@^..^g'>> $@.tmp
|
| sed -e 's^@docroot@^..^g'>> $@.tmp
|
||||||
@mv $@.tmp $@
|
@mv $@.tmp $@
|
||||||
|
@ -72,7 +72,7 @@ $(d)/conf-file.json: $(bindir)/nix
|
||||||
|
|
||||||
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
|
||||||
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
|
||||||
# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
@# @docroot@: https://nixos.org/manual/nix/unstable/contributing/hacking.html#docroot-variable
|
||||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' \
|
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' \
|
||||||
| sed -e 's^@docroot@^..^g' >> $@.tmp
|
| sed -e 's^@docroot@^..^g' >> $@.tmp
|
||||||
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
@cat doc/manual/src/language/builtins-suffix.md >> $@.tmp
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
- [CLI guideline](contributing/cli-guideline.md)
|
- [CLI guideline](contributing/cli-guideline.md)
|
||||||
- [Release Notes](release-notes/release-notes.md)
|
- [Release Notes](release-notes/release-notes.md)
|
||||||
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
||||||
|
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
|
||||||
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)
|
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)
|
||||||
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
|
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
|
||||||
- [Release 2.11 (2022-08-25)](release-notes/rl-2.11.md)
|
- [Release 2.11 (2022-08-25)](release-notes/rl-2.11.md)
|
||||||
|
|
|
@ -91,3 +91,16 @@ Most Nix commands interpret the following environment variables:
|
||||||
variable sets the initial size of the heap in bytes. It defaults to
|
variable sets the initial size of the heap in bytes. It defaults to
|
||||||
384 MiB. Setting it to a low value reduces memory consumption, but
|
384 MiB. Setting it to a low value reduces memory consumption, but
|
||||||
will increase runtime due to the overhead of garbage collection.
|
will increase runtime due to the overhead of garbage collection.
|
||||||
|
|
||||||
|
## XDG Base Directory
|
||||||
|
|
||||||
|
New Nix commands conform to the [XDG Base Directory Specification], and use the following environment variables to determine locations of various state and configuration files:
|
||||||
|
|
||||||
|
- [`XDG_CONFIG_HOME`]{#env-XDG_CONFIG_HOME} (default `~/.config`)
|
||||||
|
- [`XDG_STATE_HOME`]{#env-XDG_STATE_HOME} (default `~/.local/state`)
|
||||||
|
- [`XDG_CACHE_HOME`]{#env-XDG_CACHE_HOME} (default `~/.cache`)
|
||||||
|
|
||||||
|
Classic Nix commands can also be made to follow this standard using the [`use-xdg-base-directories`] configuration option.
|
||||||
|
|
||||||
|
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
[`use-xdg-base-directories`]: ../command-ref/conf-file.md#conf-use-xdg-base-directories
|
|
@ -82,8 +82,8 @@ paths. Realisation is a somewhat overloaded term:
|
||||||
produced through substitutes. If there are no (successful)
|
produced through substitutes. If there are no (successful)
|
||||||
substitutes, realisation fails.
|
substitutes, realisation fails.
|
||||||
|
|
||||||
[valid]: ../glossary.md#validity
|
[valid]: ../glossary.md#gloss-validity
|
||||||
[substitutes]: ../glossary.md#substitute
|
[substitutes]: ../glossary.md#gloss-substitute
|
||||||
|
|
||||||
The output path of each derivation is printed on standard output. (For
|
The output path of each derivation is printed on standard output. (For
|
||||||
non-derivations argument, the argument itself is printed.)
|
non-derivations argument, the argument itself is printed.)
|
||||||
|
@ -633,7 +633,7 @@ written to standard output.
|
||||||
|
|
||||||
A NAR archive is like a TAR or Zip archive, but it contains only the
|
A NAR archive is like a TAR or Zip archive, but it contains only the
|
||||||
information that Nix considers important. For instance, timestamps are
|
information that Nix considers important. For instance, timestamps are
|
||||||
elided because all files in the Nix store have their timestamp set to 0
|
elided because all files in the Nix store have their timestamp set to 1
|
||||||
anyway. Likewise, all permissions are left out except for the execute
|
anyway. Likewise, all permissions are left out except for the execute
|
||||||
bit, because all files in the Nix store have 444 or 555 permission.
|
bit, because all files in the Nix store have 444 or 555 permission.
|
||||||
|
|
||||||
|
|
|
@ -389,6 +389,88 @@ colors, no emojis and using ASCII instead of Unicode symbols). The same should
|
||||||
happen when TTY is not detected on STDERR. We should not display progress /
|
happen when TTY is not detected on STDERR. We should not display progress /
|
||||||
status section, but only print warnings and errors.
|
status section, but only print warnings and errors.
|
||||||
|
|
||||||
|
## Returning future proof JSON
|
||||||
|
|
||||||
|
The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this.
|
||||||
|
|
||||||
|
Two definitions are helpful here, because while JSON only defines one "key-value"
|
||||||
|
object type, we use it to cover two use cases:
|
||||||
|
|
||||||
|
- **dictionary**: a map from names to value that all have the same type. In
|
||||||
|
C++ this would be a `std::map` with string keys.
|
||||||
|
- **record**: a fixed set of attributes each with their own type. In C++, this
|
||||||
|
would be represented by a `struct`.
|
||||||
|
|
||||||
|
It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type.
|
||||||
|
|
||||||
|
This leads to the following guidelines:
|
||||||
|
|
||||||
|
- The top-level (root) value must be a record.
|
||||||
|
|
||||||
|
Otherwise, one can not change the structure of a command's output.
|
||||||
|
|
||||||
|
- The value of a dictionary item must be a record.
|
||||||
|
|
||||||
|
Otherwise, the item type can not be extended.
|
||||||
|
|
||||||
|
- List items should be records.
|
||||||
|
|
||||||
|
Otherwise, one can not change the structure of the list items.
|
||||||
|
|
||||||
|
If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records.
|
||||||
|
|
||||||
|
- Streaming JSON should return records.
|
||||||
|
|
||||||
|
An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
|
||||||
|
This is bad, because all keys must be assumed to be store implementations:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"local": { ... },
|
||||||
|
"remote": { ... },
|
||||||
|
"http": { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is good, because the it is extensible at the root, and is somewhat self-documenting:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"storeTypes": { "local": { ... }, ... },
|
||||||
|
"pluginSupport": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
While the dictionary of store types seems like a very complete response at first, a use case may arise that warrants returning additional information.
|
||||||
|
For example, the presence of plugin support may be crucial information for a client to proceed when their desired store type is missing.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The following representation is bad because it is not extensible:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "outputs": [ "out" "bin" ] }
|
||||||
|
```
|
||||||
|
|
||||||
|
However, simply converting everything to records is not enough, because the order of outputs must be preserved:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "outputs": { "bin": {}, "out": {} } }
|
||||||
|
```
|
||||||
|
|
||||||
|
The first item is the default output. Deriving this information from the outputs ordering is not great, but this is how Nix currently happens to work.
|
||||||
|
While it is possible for a JSON parser to preserve the order of fields, we can not rely on this capability to be present in all JSON libraries.
|
||||||
|
|
||||||
|
This representation is extensible and preserves the ordering:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] }
|
||||||
|
```
|
||||||
|
|
||||||
## Dialog with the user
|
## Dialog with the user
|
||||||
|
|
||||||
CLIs don't always make it clear when an action has taken place. For every
|
CLIs don't always make it clear when an action has taken place. For every
|
||||||
|
|
|
@ -8,25 +8,64 @@ $ git clone https://github.com/NixOS/nix.git
|
||||||
$ cd nix
|
$ cd nix
|
||||||
```
|
```
|
||||||
|
|
||||||
To build Nix for the current operating system/architecture use
|
The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment. If you don't have it installed, follow the [installation instructions].
|
||||||
|
|
||||||
|
[installation instructions]: ../installation/installation.md
|
||||||
|
|
||||||
|
## Nix with flakes
|
||||||
|
|
||||||
|
This section assumes you are using Nix with [flakes] enabled. See the [next section](#classic-nix) for equivalent instructions which don't require flakes.
|
||||||
|
|
||||||
|
[flakes]: ../command-ref/new-cli/nix3-flake.md#description
|
||||||
|
|
||||||
|
To build all dependencies and start a shell in which all environment
|
||||||
|
variables are set up so that those dependencies can be found:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-build
|
$ nix develop
|
||||||
```
|
```
|
||||||
|
|
||||||
or if you have a flake-enabled nix:
|
This shell also adds `./outputs/bin/nix` to your `$PATH` so you can run `nix` immediately after building it.
|
||||||
|
|
||||||
|
To get a shell with one of the other [supported compilation environments](#compilation-environments):
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix develop .#native-clang11StdenvPackages
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> Use `ccacheStdenv` to drastically improve rebuild time.
|
||||||
|
> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`.
|
||||||
|
|
||||||
|
To build Nix itself in this shell:
|
||||||
|
|
||||||
|
```console
|
||||||
|
[nix-shell]$ ./bootstrap.sh
|
||||||
|
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
|
||||||
|
[nix-shell]$ make -j $NIX_BUILD_CORES
|
||||||
|
```
|
||||||
|
|
||||||
|
To install it in `$(pwd)/outputs` and test it:
|
||||||
|
|
||||||
|
```console
|
||||||
|
[nix-shell]$ make install
|
||||||
|
[nix-shell]$ make installcheck -j $NIX_BUILD_CORES
|
||||||
|
[nix-shell]$ nix --version
|
||||||
|
nix (Nix) 2.12
|
||||||
|
```
|
||||||
|
|
||||||
|
To build a release version of Nix:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix build
|
$ nix build
|
||||||
```
|
```
|
||||||
|
|
||||||
This will build `defaultPackage` attribute defined in the `flake.nix`
|
You can also build Nix for one of the [supported target platforms](#target-platforms).
|
||||||
file. To build for other platforms add one of the following suffixes to
|
|
||||||
it: aarch64-linux, i686-linux, x86\_64-darwin, x86\_64-linux. i.e.
|
|
||||||
|
|
||||||
```console
|
## Classic Nix
|
||||||
$ nix-build -A defaultPackage.x86_64-linux
|
|
||||||
```
|
This section is for Nix without [flakes].
|
||||||
|
|
||||||
To build all dependencies and start a shell in which all environment
|
To build all dependencies and start a shell in which all environment
|
||||||
variables are set up so that those dependencies can be found:
|
variables are set up so that those dependencies can be found:
|
||||||
|
@ -35,27 +74,16 @@ variables are set up so that those dependencies can be found:
|
||||||
$ nix-shell
|
$ nix-shell
|
||||||
```
|
```
|
||||||
|
|
||||||
or if you have a flake-enabled nix:
|
To get a shell with one of the other [supported compilation environments](#compilation-environments):
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix develop
|
$ nix-shell -A devShells.x86_64-linux.native-clang11StdenvPackages
|
||||||
```
|
```
|
||||||
|
|
||||||
To get a shell with a different compilation environment (e.g. stdenv,
|
> **Note**
|
||||||
gccStdenv, clangStdenv, clang11Stdenv, ccacheStdenv):
|
>
|
||||||
|
> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time.
|
||||||
```console
|
> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`.
|
||||||
$ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages
|
|
||||||
```
|
|
||||||
|
|
||||||
or if you have a flake-enabled nix:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ nix develop .#clang11StdenvPackages
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: you can use `ccacheStdenv` to drastically improve rebuild
|
|
||||||
time. By default, ccache keeps artifacts in `~/.cache/ccache/`.
|
|
||||||
|
|
||||||
To build Nix itself in this shell:
|
To build Nix itself in this shell:
|
||||||
|
|
||||||
|
@ -71,21 +99,99 @@ To install it in `$(pwd)/outputs` and test it:
|
||||||
[nix-shell]$ make install
|
[nix-shell]$ make install
|
||||||
[nix-shell]$ make installcheck -j $NIX_BUILD_CORES
|
[nix-shell]$ make installcheck -j $NIX_BUILD_CORES
|
||||||
[nix-shell]$ ./outputs/out/bin/nix --version
|
[nix-shell]$ ./outputs/out/bin/nix --version
|
||||||
nix (Nix) 3.0
|
nix (Nix) 2.12
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have a flakes-enabled Nix you can replace:
|
To build Nix for the current operating system and CPU architecture use
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix-shell
|
$ nix-build
|
||||||
```
|
```
|
||||||
|
|
||||||
by:
|
You can also build Nix for one of the [supported target platforms](#target-platforms).
|
||||||
|
|
||||||
|
## Platforms
|
||||||
|
|
||||||
|
As specified in [`flake.nix`], Nix can be built for various platforms:
|
||||||
|
|
||||||
|
- `aarch64-linux`
|
||||||
|
- `i686-linux`
|
||||||
|
- `x86_64-darwin`
|
||||||
|
- `x86_64-linux`
|
||||||
|
|
||||||
|
[`flake.nix`]: https://github.com/nixos/nix/blob/master/flake.nix
|
||||||
|
|
||||||
|
In order to build Nix for a different platform than the one you're currently
|
||||||
|
on, you need to have some way for your system Nix to build code for that
|
||||||
|
platform. Common solutions include [remote builders] and [binfmt emulation]
|
||||||
|
(only supported on NixOS).
|
||||||
|
|
||||||
|
[remote builders]: ../advanced-topics/distributed-builds.md
|
||||||
|
[binfmt emulation]: https://nixos.org/manual/nixos/stable/options.html#opt-boot.binfmt.emulatedSystems
|
||||||
|
|
||||||
|
These solutions let Nix perform builds as if you're on the native platform, so
|
||||||
|
executing the build is as simple as
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ nix develop
|
$ nix build .#packages.aarch64-linux.default
|
||||||
```
|
```
|
||||||
|
|
||||||
|
for flake-enabled Nix, or
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-build -A packages.aarch64-linux.default
|
||||||
|
```
|
||||||
|
|
||||||
|
for classic Nix.
|
||||||
|
|
||||||
|
You can use any of the other supported platforms in place of `aarch64-linux`.
|
||||||
|
|
||||||
|
Cross-compiled builds are available for ARMv6 and ARMv7, and Nix on unsupported platforms can be bootstrapped by adding more `crossSystems` in `flake.nix`.
|
||||||
|
|
||||||
|
## Compilation environments
|
||||||
|
|
||||||
|
Nix can be compiled using multiple environments:
|
||||||
|
|
||||||
|
- `stdenv`: default;
|
||||||
|
- `gccStdenv`: force the use of `gcc` compiler;
|
||||||
|
- `clangStdenv`: force the use of `clang` compiler;
|
||||||
|
- `ccacheStdenv`: enable [ccache], a compiler cache to speed up compilation.
|
||||||
|
|
||||||
|
To build with one of those environments, you can use
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix build .#nix-ccacheStdenv
|
||||||
|
```
|
||||||
|
|
||||||
|
for flake-enabled Nix, or
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-build -A nix-ccacheStdenv
|
||||||
|
```
|
||||||
|
|
||||||
|
for classic Nix.
|
||||||
|
|
||||||
|
You can use any of the other supported environments in place of `nix-ccacheStdenv`.
|
||||||
|
|
||||||
|
## Editor integration
|
||||||
|
|
||||||
|
The `clangd` LSP server is installed by default on the `clang`-based `devShell`s.
|
||||||
|
See [supported compilation environments](#compilation-environments) and instructions how to set up a shell [with flakes](#nix-with-flakes) or in [classic Nix](#classic-nix).
|
||||||
|
|
||||||
|
To use the LSP with your editor, you first need to [set up `clangd`](https://clangd.llvm.org/installation#project-setup) by running:
|
||||||
|
|
||||||
|
```console
|
||||||
|
make clean && bear -- make -j$NIX_BUILD_CORES install
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure your editor to use the `clangd` from the shell, either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration).
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> For some editors (e.g. Visual Studio Code), you may need to install a [special extension](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd) for the editor to interact with `clangd`.
|
||||||
|
> Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim).
|
||||||
|
> Editor-specific setup is typically opinionated, so we will not cover it here in more detail.
|
||||||
|
|
||||||
## Running tests
|
## Running tests
|
||||||
|
|
||||||
### Unit-tests
|
### Unit-tests
|
||||||
|
@ -219,7 +325,7 @@ After the CI run completes, you can check the output to extract the installer UR
|
||||||
5. To generate an install command, plug this `install_url` and your GitHub username into this template:
|
5. To generate an install command, plug this `install_url` and your GitHub username into this template:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
sh <(curl -L <install_url>) --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
|
curl -L <install_url> | sh -s -- --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- #### Manually generating test installers
|
<!-- #### Manually generating test installers
|
||||||
|
|
|
@ -19,6 +19,13 @@
|
||||||
|
|
||||||
[store derivation]: #gloss-store-derivation
|
[store derivation]: #gloss-store-derivation
|
||||||
|
|
||||||
|
- [instantiate]{#gloss-instantiate}, instantiation\
|
||||||
|
Translate a [derivation] into a [store derivation].
|
||||||
|
|
||||||
|
See [`nix-instantiate`](./command-ref/nix-instantiate.md).
|
||||||
|
|
||||||
|
[instantiate]: #gloss-instantiate
|
||||||
|
|
||||||
- [realise]{#gloss-realise}, realisation\
|
- [realise]{#gloss-realise}, realisation\
|
||||||
Ensure a [store path] is [valid][validity].
|
Ensure a [store path] is [valid][validity].
|
||||||
|
|
||||||
|
@ -156,6 +163,8 @@
|
||||||
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
|
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
|
||||||
references `R` then `R` is also in the closure of `P`.
|
references `R` then `R` is also in the closure of `P`.
|
||||||
|
|
||||||
|
[closure]: #gloss-closure
|
||||||
|
|
||||||
- [output path]{#gloss-output-path}\
|
- [output path]{#gloss-output-path}\
|
||||||
A [store path] produced by a [derivation].
|
A [store path] produced by a [derivation].
|
||||||
|
|
||||||
|
@ -172,6 +181,8 @@
|
||||||
- The store path is listed in the Nix database as being valid.
|
- The store path is listed in the Nix database as being valid.
|
||||||
- All paths in the store path's [closure] are valid.
|
- All paths in the store path's [closure] are valid.
|
||||||
|
|
||||||
|
[validity]: #gloss-validity
|
||||||
|
|
||||||
- [user environment]{#gloss-user-env}\
|
- [user environment]{#gloss-user-env}\
|
||||||
An automatically generated store object that consists of a set of
|
An automatically generated store object that consists of a set of
|
||||||
symlinks to “active” applications, i.e., other store paths. These
|
symlinks to “active” applications, i.e., other store paths. These
|
||||||
|
|
|
@ -27,7 +27,7 @@ Set the environment variable and install Nix
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
|
||||||
$ sh <(curl -L https://nixos.org/nix/install)
|
$ curl -L https://nixos.org/nix/install | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
In the shell profile and rc files (for example, `/etc/bashrc`,
|
In the shell profile and rc files (for example, `/etc/bashrc`,
|
||||||
|
|
|
@ -1,2 +1,38 @@
|
||||||
This section describes how to install and configure Nix for first-time
|
# Installation
|
||||||
use.
|
|
||||||
|
This section describes how to install and configure Nix for first-time use.
|
||||||
|
|
||||||
|
The current recommended option on Linux and MacOS is [multi-user](#multi-user).
|
||||||
|
|
||||||
|
## Multi-user
|
||||||
|
|
||||||
|
This installation offers better sharing, improved isolation, and more security
|
||||||
|
over a single user installation.
|
||||||
|
|
||||||
|
This option requires either:
|
||||||
|
|
||||||
|
* Linux running systemd, with SELinux disabled
|
||||||
|
* MacOS
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ bash <(curl -L https://nixos.org/nix/install) --daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
## Single-user
|
||||||
|
|
||||||
|
> Single-user is not supported on Mac.
|
||||||
|
|
||||||
|
This installation has less requirements than the multi-user install, however it
|
||||||
|
cannot offer equivalent sharing, isolation, or security.
|
||||||
|
|
||||||
|
This option is suitable for systems without systemd.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ bash <(curl -L https://nixos.org/nix/install) --no-daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
## Distributions
|
||||||
|
|
||||||
|
The Nix community maintains installers for several distributions.
|
||||||
|
|
||||||
|
They can be found in the [`nix-community/nix-installers`](https://github.com/nix-community/nix-installers) repository.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
The easiest way to install Nix is to run the following command:
|
The easiest way to install Nix is to run the following command:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ sh <(curl -L https://nixos.org/nix/install)
|
$ curl -L https://nixos.org/nix/install | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
This will run the installer interactively (causing it to explain what
|
This will run the installer interactively (causing it to explain what
|
||||||
|
@ -27,7 +27,7 @@ you can authenticate with `sudo`.
|
||||||
To explicitly select a single-user installation on your system:
|
To explicitly select a single-user installation on your system:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ sh <(curl -L https://nixos.org/nix/install) --no-daemon
|
$ curl -L https://nixos.org/nix/install | sh -s -- --no-daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
This will perform a single-user installation of Nix, meaning that `/nix`
|
This will perform a single-user installation of Nix, meaning that `/nix`
|
||||||
|
@ -66,7 +66,7 @@ You can instruct the installer to perform a multi-user installation on
|
||||||
your system:
|
your system:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ sh <(curl -L https://nixos.org/nix/install) --daemon
|
$ curl -L https://nixos.org/nix/install | sh -s -- --daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
The multi-user installation of Nix will create build users between the
|
The multi-user installation of Nix will create build users between the
|
||||||
|
@ -287,7 +287,7 @@ These install scripts can be used the same as the main NixOS.org
|
||||||
installation script:
|
installation script:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ sh <(curl -L https://nixos.org/nix/install)
|
$ curl -L https://nixos.org/nix/install | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
In the same directory of the install script are sha256 sums, and gpg
|
In the same directory of the install script are sha256 sums, and gpg
|
||||||
|
|
|
@ -212,7 +212,7 @@ Derivations can declare some infrequently used optional attributes.
|
||||||
If this **experimental** attribute is set to true, then the derivation
|
If this **experimental** attribute is set to true, then the derivation
|
||||||
outputs will be stored in a content-addressed location rather than the
|
outputs will be stored in a content-addressed location rather than the
|
||||||
traditional input-addressed one.
|
traditional input-addressed one.
|
||||||
This only has an effect if the `ca-derivation` experimental feature is enabled.
|
This only has an effect if the `ca-derivations` experimental feature is enabled.
|
||||||
|
|
||||||
Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
|
Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
|
||||||
|
|
||||||
|
@ -255,3 +255,78 @@ Derivations can declare some infrequently used optional attributes.
|
||||||
> substituted. Thus it is usually a good idea to align `system` with
|
> substituted. Thus it is usually a good idea to align `system` with
|
||||||
> `builtins.currentSystem` when setting `allowSubstitutes` to
|
> `builtins.currentSystem` when setting `allowSubstitutes` to
|
||||||
> `false`. For most trivial derivations this should be the case.
|
> `false`. For most trivial derivations this should be the case.
|
||||||
|
|
||||||
|
- [`__structuredAttrs`]{#adv-attr-structuredAttrs}\
|
||||||
|
If the special attribute `__structuredAttrs` is set to `true`, the other derivation
|
||||||
|
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 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]}`.
|
||||||
|
|
||||||
|
- [`outputChecks`]{#adv-attr-outputChecks}\
|
||||||
|
When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks`
|
||||||
|
attribute allows defining checks per-output.
|
||||||
|
|
||||||
|
In addition to
|
||||||
|
[`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites),
|
||||||
|
[`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites),
|
||||||
|
the following attributes are available:
|
||||||
|
|
||||||
|
- `maxSize` defines the maximum size of the resulting [store object](../glossary.md#gloss-store-object).
|
||||||
|
- `maxClosureSize` defines the maximum size of the output's closure.
|
||||||
|
- `ignoreSelfRefs` controls whether self-references should be considered when
|
||||||
|
checking for allowed references/requisites.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
__structuredAttrs = true;
|
||||||
|
|
||||||
|
outputChecks.out = {
|
||||||
|
# The closure of 'out' must not be larger than 256 MiB.
|
||||||
|
maxClosureSize = 256 * 1024 * 1024;
|
||||||
|
|
||||||
|
# It must not refer to the C compiler or to the 'dev' output.
|
||||||
|
disallowedRequisites = [ stdenv.cc "dev" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
outputChecks.dev = {
|
||||||
|
# The 'dev' output must not be larger than 128 KiB.
|
||||||
|
maxSize = 128 * 1024;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
|
||||||
|
> **Warning**
|
||||||
|
> This is an experimental feature.
|
||||||
|
>
|
||||||
|
> To enable it, add the following to [nix.conf](../command-ref/conf-file.md):
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> extra-experimental-features = discard-references
|
||||||
|
> ```
|
||||||
|
|
||||||
|
When using [structured attributes](#adv-attr-structuredAttrs), the
|
||||||
|
attribute `unsafeDiscardReferences` is an attribute set with a boolean value for each output name.
|
||||||
|
If set to `true`, it disables scanning the output for runtime dependencies.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
__structuredAttrs = true;
|
||||||
|
unsafeDiscardReferences.out = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful, for example, when generating self-contained filesystem images with
|
||||||
|
their own embedded Nix store: hashes found inside such an image refer
|
||||||
|
to the embedded store and not to the host's Nix store.
|
||||||
|
|
|
@ -91,7 +91,7 @@ This is an incomplete overview of language features, by example.
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
`"hello ${ { a = "world" }.a }"`
|
`"hello ${ { a = "world"; }.a }"`
|
||||||
|
|
||||||
`"1 2 ${toString 3}"`
|
`"1 2 ${toString 3}"`
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
| [Equality] | *expr* `==` *expr* | none | 11 |
|
| [Equality] | *expr* `==` *expr* | none | 11 |
|
||||||
| Inequality | *expr* `!=` *expr* | none | 11 |
|
| Inequality | *expr* `!=` *expr* | none | 11 |
|
||||||
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
|
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
|
||||||
| Logical disjunction (`OR`) | *bool* `\|\|` *bool* | left | 13 |
|
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
|
||||||
| [Logical implication] | *bool* `->` *bool* | none | 14 |
|
| [Logical implication] | *bool* `->` *bool* | none | 14 |
|
||||||
|
|
||||||
[string]: ./values.md#type-string
|
[string]: ./values.md#type-string
|
||||||
|
@ -116,7 +116,7 @@ The result is a string.
|
||||||
[store path]: ../glossary.md#gloss-store-path
|
[store path]: ../glossary.md#gloss-store-path
|
||||||
[store]: ../glossary.md#gloss-store
|
[store]: ../glossary.md#gloss-store
|
||||||
|
|
||||||
[Path and string concatenation]: #path-and-string-concatenation
|
[String and path concatenation]: #string-and-path-concatenation
|
||||||
|
|
||||||
## Update
|
## Update
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,16 @@ This chapter is for impatient people who don't like reading
|
||||||
documentation. For more in-depth information you are kindly referred
|
documentation. For more in-depth information you are kindly referred
|
||||||
to subsequent chapters.
|
to subsequent chapters.
|
||||||
|
|
||||||
1. Install single-user Nix by running the following:
|
1. Install Nix by running the following:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ bash <(curl -L https://nixos.org/nix/install)
|
$ curl -L https://nixos.org/nix/install | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
This will install Nix in `/nix`. The install script will create
|
The install script will use `sudo`, so make sure you have sufficient rights.
|
||||||
`/nix` using `sudo`, so make sure you have sufficient rights. (For
|
On Linux, `--daemon` can be omitted for a single-user install.
|
||||||
other installation methods, see
|
|
||||||
[here](installation/installation.md).)
|
For other installation methods, see [here](installation/installation.md).
|
||||||
|
|
||||||
1. See what installable packages are currently available in the
|
1. See what installable packages are currently available in the
|
||||||
channel:
|
channel:
|
||||||
|
|
|
@ -25,11 +25,11 @@
|
||||||
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables.
|
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables.
|
||||||
For example,
|
For example,
|
||||||
```shell-session
|
```shell-session
|
||||||
# nix-build /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^dev
|
# nix build /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^dev
|
||||||
```
|
```
|
||||||
now works just as
|
now works just as
|
||||||
```shell-session
|
```shell-session
|
||||||
# nix-build glibc^dev
|
# nix build nixpkgs#glibc^dev
|
||||||
```
|
```
|
||||||
does already.
|
does already.
|
||||||
|
|
||||||
|
|
22
doc/manual/src/release-notes/rl-2.14.md
Normal file
22
doc/manual/src/release-notes/rl-2.14.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Release 2.14 (2023-02-28)
|
||||||
|
|
||||||
|
* A new function `builtins.readFileType` is available. It is similar to
|
||||||
|
`builtins.readDir` but acts on a single file or directory.
|
||||||
|
|
||||||
|
* In flakes, the `.outPath` attribute of a flake now always refers to
|
||||||
|
the directory containing the `flake.nix`. This was not the case for
|
||||||
|
when `flake.nix` was in a subdirectory of e.g. a Git repository.
|
||||||
|
The root of the source of a flake in a subdirectory is still
|
||||||
|
available in `.sourceInfo.outPath`.
|
||||||
|
|
||||||
|
* In derivations that use structured attributes, you can now use `unsafeDiscardReferences`
|
||||||
|
to disable scanning a given output for runtime dependencies:
|
||||||
|
```nix
|
||||||
|
__structuredAttrs = true;
|
||||||
|
unsafeDiscardReferences.out = true;
|
||||||
|
```
|
||||||
|
This is useful e.g. when generating self-contained filesystem images with
|
||||||
|
their own embedded Nix store: hashes found inside such an image refer
|
||||||
|
to the embedded store and not to the host's Nix store.
|
||||||
|
|
||||||
|
This requires the `discard-references` experimental feature.
|
|
@ -1,10 +1,2 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
* A new function `builtins.readFileType` is available. It is similar to
|
|
||||||
`builtins.readDir` but acts on a single file or directory.
|
|
||||||
|
|
||||||
* The `builtins.readDir` function has been optimized when encountering not-yet-known
|
|
||||||
file types from POSIX's `readdir`. In such cases the type of each file is/was
|
|
||||||
discovered by making multiple syscalls. This change makes these operations
|
|
||||||
lazy such that these lookups will only be performed if the attribute is used.
|
|
||||||
This optimization affects a minority of filesystems and operating systems.
|
|
||||||
|
|
348
flake.nix
348
flake.nix
|
@ -8,10 +8,11 @@
|
||||||
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
|
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
|
||||||
officialRelease = false;
|
officialRelease = false;
|
||||||
|
|
||||||
version = nixpkgs.lib.fileContents ./.version + versionSuffix;
|
version = lib.fileContents ./.version + versionSuffix;
|
||||||
versionSuffix =
|
versionSuffix =
|
||||||
if officialRelease
|
if officialRelease
|
||||||
then ""
|
then ""
|
||||||
|
@ -25,36 +26,42 @@
|
||||||
|
|
||||||
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
|
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
|
||||||
|
|
||||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
forAllSystems = lib.genAttrs systems;
|
||||||
forAllSystemsAndStdenvs = f: forAllSystems (system:
|
|
||||||
nixpkgs.lib.listToAttrs
|
forAllCrossSystems = lib.genAttrs crossSystems;
|
||||||
(map
|
|
||||||
(n:
|
forAllStdenvs = f:
|
||||||
nixpkgs.lib.nameValuePair "${n}Packages" (
|
lib.listToAttrs
|
||||||
f system n
|
(map
|
||||||
)) stdenvs
|
(stdenvName: {
|
||||||
)
|
name = "${stdenvName}Packages";
|
||||||
);
|
value = f stdenvName;
|
||||||
|
})
|
||||||
|
stdenvs);
|
||||||
|
|
||||||
forAllStdenvs = f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
|
|
||||||
|
|
||||||
# Memoize nixpkgs for different platforms for efficiency.
|
# Memoize nixpkgs for different platforms for efficiency.
|
||||||
nixpkgsFor =
|
nixpkgsFor = forAllSystems
|
||||||
let stdenvsPackages = forAllSystemsAndStdenvs
|
(system: let
|
||||||
(system: stdenv:
|
make-pkgs = crossSystem: stdenv: import nixpkgs {
|
||||||
import nixpkgs {
|
inherit system crossSystem;
|
||||||
inherit system;
|
|
||||||
overlays = [
|
overlays = [
|
||||||
(overlayFor (p: p.${stdenv}))
|
(overlayFor (p: p.${stdenv}))
|
||||||
];
|
];
|
||||||
}
|
};
|
||||||
);
|
stdenvs = forAllStdenvs (make-pkgs null);
|
||||||
in
|
native = stdenvs.stdenvPackages;
|
||||||
# Add the `stdenvPackages` at toplevel, both because these are the ones
|
in {
|
||||||
# we want most of the time and for backwards compatibility
|
inherit stdenvs native;
|
||||||
forAllSystems (system: stdenvsPackages.${system} // stdenvsPackages.${system}.stdenvPackages);
|
static = native.pkgsStatic;
|
||||||
|
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||||
|
});
|
||||||
|
|
||||||
commonDeps = { pkgs, isStatic ? false }: with pkgs; rec {
|
commonDeps =
|
||||||
|
{ pkgs
|
||||||
|
, isStatic ? pkgs.stdenv.hostPlatform.isStatic
|
||||||
|
}:
|
||||||
|
with pkgs; rec {
|
||||||
# Use "busybox-sandbox-shell" if present,
|
# Use "busybox-sandbox-shell" if present,
|
||||||
# if not (legacy) fallback and hope it's sufficient.
|
# if not (legacy) fallback and hope it's sufficient.
|
||||||
sh = pkgs.busybox-sandbox-shell or (busybox.override {
|
sh = pkgs.busybox-sandbox-shell or (busybox.override {
|
||||||
|
@ -131,15 +138,20 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
propagatedDeps =
|
propagatedDeps =
|
||||||
[ (boehmgc.override {
|
[ ((boehmgc.override {
|
||||||
enableLargeConfig = true;
|
enableLargeConfig = true;
|
||||||
|
}).overrideAttrs(o: {
|
||||||
|
patches = (o.patches or []) ++ [
|
||||||
|
./boehmgc-coroutine-sp-fallback.diff
|
||||||
|
];
|
||||||
})
|
})
|
||||||
|
)
|
||||||
nlohmann_json
|
nlohmann_json
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
installScriptFor = systems:
|
installScriptFor = systems:
|
||||||
with nixpkgsFor.x86_64-linux;
|
with nixpkgsFor.x86_64-linux.native;
|
||||||
runCommand "installer-script"
|
runCommand "installer-script"
|
||||||
{ buildInputs = [ nix ];
|
{ buildInputs = [ nix ];
|
||||||
}
|
}
|
||||||
|
@ -203,8 +215,9 @@
|
||||||
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
|
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
|
||||||
};
|
};
|
||||||
|
|
||||||
binaryTarball = buildPackages: nix: pkgs:
|
binaryTarball = nix: pkgs:
|
||||||
let
|
let
|
||||||
|
inherit (pkgs) buildPackages;
|
||||||
inherit (pkgs) cacert;
|
inherit (pkgs) cacert;
|
||||||
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
|
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
|
||||||
in
|
in
|
||||||
|
@ -284,7 +297,15 @@
|
||||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||||
nixUnstable = prev.nixUnstable;
|
nixUnstable = prev.nixUnstable;
|
||||||
|
|
||||||
nix = with final; with commonDeps { inherit pkgs; }; currentStdenv.mkDerivation {
|
nix =
|
||||||
|
with final;
|
||||||
|
with commonDeps {
|
||||||
|
inherit pkgs;
|
||||||
|
inherit (currentStdenv.hostPlatform) isStatic;
|
||||||
|
};
|
||||||
|
let
|
||||||
|
canRunInstalled = currentStdenv.buildPlatform.canExecute currentStdenv.hostPlatform;
|
||||||
|
in currentStdenv.mkDerivation {
|
||||||
name = "nix-${version}";
|
name = "nix-${version}";
|
||||||
inherit version;
|
inherit version;
|
||||||
|
|
||||||
|
@ -295,24 +316,26 @@
|
||||||
outputs = [ "out" "dev" "doc" ];
|
outputs = [ "out" "dev" "doc" ];
|
||||||
|
|
||||||
nativeBuildInputs = nativeBuildDeps;
|
nativeBuildInputs = nativeBuildDeps;
|
||||||
buildInputs = buildDeps ++ awsDeps;
|
buildInputs = buildDeps
|
||||||
|
# There have been issues building these dependencies
|
||||||
|
++ lib.optionals (currentStdenv.hostPlatform == currentStdenv.buildPlatform) awsDeps;
|
||||||
|
|
||||||
propagatedBuildInputs = propagatedDeps;
|
propagatedBuildInputs = propagatedDeps;
|
||||||
|
|
||||||
disallowedReferences = [ boost ];
|
disallowedReferences = [ boost ];
|
||||||
|
|
||||||
preConfigure =
|
preConfigure = lib.optionalString (! currentStdenv.hostPlatform.isStatic)
|
||||||
''
|
''
|
||||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||||
# https://github.com/NixOS/nixpkgs/issues/45462
|
# https://github.com/NixOS/nixpkgs/issues/45462
|
||||||
mkdir -p $out/lib
|
mkdir -p $out/lib
|
||||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||||
rm -f $out/lib/*.a
|
rm -f $out/lib/*.a
|
||||||
${lib.optionalString currentStdenv.isLinux ''
|
${lib.optionalString currentStdenv.hostPlatform.isLinux ''
|
||||||
chmod u+w $out/lib/*.so.*
|
chmod u+w $out/lib/*.so.*
|
||||||
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||||
''}
|
''}
|
||||||
${lib.optionalString currentStdenv.isDarwin ''
|
${lib.optionalString currentStdenv.hostPlatform.isDarwin ''
|
||||||
for LIB in $out/lib/*.dylib; do
|
for LIB in $out/lib/*.dylib; do
|
||||||
chmod u+w $LIB
|
chmod u+w $LIB
|
||||||
install_name_tool -id $LIB $LIB
|
install_name_tool -id $LIB $LIB
|
||||||
|
@ -323,7 +346,9 @@
|
||||||
'';
|
'';
|
||||||
|
|
||||||
configureFlags = configureFlags ++
|
configureFlags = configureFlags ++
|
||||||
[ "--sysconfdir=/etc" ];
|
[ "--sysconfdir=/etc" ] ++
|
||||||
|
lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell" ++
|
||||||
|
lib.optional (!canRunInstalled) "--disable-doc-gen";
|
||||||
|
|
||||||
enableParallelBuilding = true;
|
enableParallelBuilding = true;
|
||||||
|
|
||||||
|
@ -347,10 +372,12 @@
|
||||||
doInstallCheck = true;
|
doInstallCheck = true;
|
||||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||||
|
|
||||||
separateDebugInfo = true;
|
separateDebugInfo = !currentStdenv.hostPlatform.isStatic;
|
||||||
|
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
|
|
||||||
|
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||||
|
|
||||||
passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation {
|
passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation {
|
||||||
name = "nix-perl-${version}";
|
name = "nix-perl-${version}";
|
||||||
|
|
||||||
|
@ -383,7 +410,7 @@
|
||||||
postUnpack = "sourceRoot=$sourceRoot/perl";
|
postUnpack = "sourceRoot=$sourceRoot/perl";
|
||||||
});
|
});
|
||||||
|
|
||||||
meta.platforms = systems;
|
meta.platforms = lib.platforms.unix;
|
||||||
};
|
};
|
||||||
|
|
||||||
lowdown-nix = with final; currentStdenv.mkDerivation rec {
|
lowdown-nix = with final; currentStdenv.mkDerivation rec {
|
||||||
|
@ -404,7 +431,20 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
in {
|
||||||
|
inherit nixpkgsFor;
|
||||||
|
|
||||||
# A Nixpkgs overlay that overrides the 'nix' and
|
# A Nixpkgs overlay that overrides the 'nix' and
|
||||||
# 'nix.perl-bindings' packages.
|
# 'nix.perl-bindings' packages.
|
||||||
|
@ -413,32 +453,28 @@
|
||||||
hydraJobs = {
|
hydraJobs = {
|
||||||
|
|
||||||
# Binary package for various platforms.
|
# Binary package for various platforms.
|
||||||
build = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix);
|
build = forAllSystems (system: self.packages.${system}.nix);
|
||||||
|
|
||||||
buildStatic = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
|
buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
|
||||||
|
|
||||||
buildCross = nixpkgs.lib.genAttrs crossSystems (crossSystem:
|
buildCross = forAllCrossSystems (crossSystem:
|
||||||
nixpkgs.lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
|
lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
|
||||||
|
|
||||||
buildNoGc = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.overrideAttrs (a: { configureFlags = (a.configureFlags or []) ++ ["--enable-gc=no"];}));
|
buildNoGc = forAllSystems (system: self.packages.${system}.nix.overrideAttrs (a: { configureFlags = (a.configureFlags or []) ++ ["--enable-gc=no"];}));
|
||||||
|
|
||||||
# Perl bindings for various platforms.
|
# Perl bindings for various platforms.
|
||||||
perlBindings = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.perl-bindings);
|
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
|
||||||
|
|
||||||
# Binary tarball for various platforms, containing a Nix store
|
# Binary tarball for various platforms, containing a Nix store
|
||||||
# with the closure of 'nix' package, and the second half of
|
# with the closure of 'nix' package, and the second half of
|
||||||
# the installation script.
|
# the installation script.
|
||||||
binaryTarball = nixpkgs.lib.genAttrs systems (system: binaryTarball nixpkgsFor.${system} nixpkgsFor.${system}.nix nixpkgsFor.${system});
|
binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native);
|
||||||
|
|
||||||
binaryTarballCross = nixpkgs.lib.genAttrs ["x86_64-linux"] (system: builtins.listToAttrs (map (crossSystem: {
|
binaryTarballCross = lib.genAttrs ["x86_64-linux"] (system:
|
||||||
name = crossSystem;
|
forAllCrossSystems (crossSystem:
|
||||||
value = let
|
binaryTarball
|
||||||
nixpkgsCross = import nixpkgs {
|
self.packages.${system}."nix-${crossSystem}"
|
||||||
inherit system crossSystem;
|
nixpkgsFor.${system}.cross.${crossSystem}));
|
||||||
overlays = [ self.overlays.default ];
|
|
||||||
};
|
|
||||||
in binaryTarball nixpkgsFor.${system} self.packages.${system}."nix-${crossSystem}" nixpkgsCross;
|
|
||||||
}) crossSystems));
|
|
||||||
|
|
||||||
# The first half of the installation script. This is uploaded
|
# The first half of the installation script. This is uploaded
|
||||||
# to https://nixos.org/nix/install. It downloads the binary
|
# to https://nixos.org/nix/install. It downloads the binary
|
||||||
|
@ -448,11 +484,11 @@
|
||||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
||||||
|
|
||||||
# docker image with Nix inside
|
# docker image with Nix inside
|
||||||
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||||
|
|
||||||
# Line coverage analysis.
|
# Line coverage analysis.
|
||||||
coverage =
|
coverage =
|
||||||
with nixpkgsFor.x86_64-linux;
|
with nixpkgsFor.x86_64-linux.native;
|
||||||
with commonDeps { inherit pkgs; };
|
with commonDeps { inherit pkgs; };
|
||||||
|
|
||||||
releaseTools.coverageAnalysis {
|
releaseTools.coverageAnalysis {
|
||||||
|
@ -460,6 +496,10 @@
|
||||||
|
|
||||||
src = self;
|
src = self;
|
||||||
|
|
||||||
|
configureFlags = [
|
||||||
|
"CXXFLAGS=-I${lib.getDev pkgs.rapidcheck}/extras/gtest/include"
|
||||||
|
];
|
||||||
|
|
||||||
enableParallelBuilding = true;
|
enableParallelBuilding = true;
|
||||||
|
|
||||||
nativeBuildInputs = nativeBuildDeps;
|
nativeBuildInputs = nativeBuildDeps;
|
||||||
|
@ -478,54 +518,29 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
# System tests.
|
# System tests.
|
||||||
tests.remoteBuilds = import ./tests/remote-builds.nix {
|
tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix;
|
||||||
system = "x86_64-linux";
|
|
||||||
inherit nixpkgs;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
};
|
|
||||||
|
|
||||||
tests.nix-copy-closure = import ./tests/nix-copy-closure.nix {
|
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix;
|
||||||
system = "x86_64-linux";
|
|
||||||
inherit nixpkgs;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
};
|
|
||||||
|
|
||||||
tests.nssPreload = (import ./tests/nss-preload.nix rec {
|
tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix;
|
||||||
system = "x86_64-linux";
|
|
||||||
inherit nixpkgs;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.githubFlakes = (import ./tests/github-flakes.nix rec {
|
tests.nssPreload = runNixOSTestFor "x86_64-linux" ./tests/nixos/nss-preload.nix;
|
||||||
system = "x86_64-linux";
|
|
||||||
inherit nixpkgs;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.sourcehutFlakes = (import ./tests/sourcehut-flakes.nix rec {
|
tests.githubFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/github-flakes.nix;
|
||||||
system = "x86_64-linux";
|
|
||||||
inherit nixpkgs;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.containers = (import ./tests/containers.nix rec {
|
tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix;
|
||||||
system = "x86_64-linux";
|
|
||||||
inherit nixpkgs;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.setuid = nixpkgs.lib.genAttrs
|
tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix;
|
||||||
|
|
||||||
|
tests.setuid = lib.genAttrs
|
||||||
["i686-linux" "x86_64-linux"]
|
["i686-linux" "x86_64-linux"]
|
||||||
(system:
|
(system: runNixOSTestFor system ./tests/nixos/setuid.nix);
|
||||||
import ./tests/setuid.nix rec {
|
|
||||||
inherit nixpkgs system;
|
|
||||||
overlay = self.overlays.default;
|
|
||||||
});
|
|
||||||
|
|
||||||
# Make sure that nix-env still produces the exact same result
|
# Make sure that nix-env still produces the exact same result
|
||||||
# on a particular version of Nixpkgs.
|
# on a particular version of Nixpkgs.
|
||||||
tests.evalNixpkgs =
|
tests.evalNixpkgs =
|
||||||
with nixpkgsFor.x86_64-linux;
|
with nixpkgsFor.x86_64-linux.native;
|
||||||
runCommand "eval-nixos" { buildInputs = [ nix ]; }
|
runCommand "eval-nixos" { buildInputs = [ nix ]; }
|
||||||
''
|
''
|
||||||
type -p nix-env
|
type -p nix-env
|
||||||
|
@ -536,18 +551,18 @@
|
||||||
'';
|
'';
|
||||||
|
|
||||||
tests.nixpkgsLibTests =
|
tests.nixpkgsLibTests =
|
||||||
nixpkgs.lib.genAttrs systems (system:
|
forAllSystems (system:
|
||||||
import (nixpkgs + "/lib/tests/release.nix")
|
import (nixpkgs + "/lib/tests/release.nix")
|
||||||
{ pkgs = nixpkgsFor.${system}; }
|
{ pkgs = nixpkgsFor.${system}.native; }
|
||||||
);
|
);
|
||||||
|
|
||||||
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
|
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
|
||||||
pkgs = nixpkgsFor.x86_64-linux;
|
pkgs = nixpkgsFor.x86_64-linux.native;
|
||||||
nixpkgs = nixpkgs-regression;
|
nixpkgs = nixpkgs-regression;
|
||||||
};
|
};
|
||||||
|
|
||||||
installTests = forAllSystems (system:
|
installTests = forAllSystems (system:
|
||||||
let pkgs = nixpkgsFor.${system}; in
|
let pkgs = nixpkgsFor.${system}.native; in
|
||||||
pkgs.runCommand "install-tests" {
|
pkgs.runCommand "install-tests" {
|
||||||
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
|
||||||
againstCurrentUnstable =
|
againstCurrentUnstable =
|
||||||
|
@ -572,67 +587,18 @@
|
||||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||||
installTests = self.hydraJobs.installTests.${system};
|
installTests = self.hydraJobs.installTests.${system};
|
||||||
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
||||||
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
||||||
dockerImage = self.hydraJobs.dockerImage.${system};
|
dockerImage = self.hydraJobs.dockerImage.${system};
|
||||||
});
|
});
|
||||||
|
|
||||||
packages = forAllSystems (system: rec {
|
packages = forAllSystems (system: rec {
|
||||||
inherit (nixpkgsFor.${system}) nix;
|
inherit (nixpkgsFor.${system}.native) nix;
|
||||||
default = nix;
|
default = nix;
|
||||||
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
||||||
nix-static = let
|
nix-static = nixpkgsFor.${system}.static.nix;
|
||||||
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
|
|
||||||
in with commonDeps { pkgs = nixpkgs; isStatic = true; }; nixpkgs.stdenv.mkDerivation {
|
|
||||||
name = "nix-${version}";
|
|
||||||
|
|
||||||
src = self;
|
|
||||||
|
|
||||||
VERSION_SUFFIX = versionSuffix;
|
|
||||||
|
|
||||||
outputs = [ "out" "dev" "doc" ];
|
|
||||||
|
|
||||||
nativeBuildInputs = nativeBuildDeps;
|
|
||||||
buildInputs = buildDeps ++ propagatedDeps;
|
|
||||||
|
|
||||||
# Work around pkgsStatic disabling all tests.
|
|
||||||
# Remove in NixOS 22.11, see https://github.com/NixOS/nixpkgs/pull/140271.
|
|
||||||
preHook =
|
|
||||||
''
|
|
||||||
doCheck=1
|
|
||||||
doInstallCheck=1
|
|
||||||
'';
|
|
||||||
|
|
||||||
configureFlags =
|
|
||||||
configureFlags ++
|
|
||||||
[ "--sysconfdir=/etc"
|
|
||||||
"--enable-embedded-sandbox-shell"
|
|
||||||
];
|
|
||||||
|
|
||||||
enableParallelBuilding = true;
|
|
||||||
|
|
||||||
makeFlags = "profiledir=$(out)/etc/profile.d";
|
|
||||||
|
|
||||||
installFlags = "sysconfdir=$(out)/etc";
|
|
||||||
|
|
||||||
postInstall = ''
|
|
||||||
mkdir -p $doc/nix-support
|
|
||||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
|
||||||
mkdir -p $out/nix-support
|
|
||||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
|
||||||
'';
|
|
||||||
|
|
||||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
|
||||||
|
|
||||||
stripAllList = ["bin"];
|
|
||||||
|
|
||||||
strictDeps = true;
|
|
||||||
|
|
||||||
hardeningDisable = [ "pie" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
dockerImage =
|
dockerImage =
|
||||||
let
|
let
|
||||||
pkgs = nixpkgsFor.${system};
|
pkgs = nixpkgsFor.${system}.native;
|
||||||
image = import ./docker.nix { inherit pkgs; tag = version; };
|
image = import ./docker.nix { inherit pkgs; tag = version; };
|
||||||
in
|
in
|
||||||
pkgs.runCommand
|
pkgs.runCommand
|
||||||
|
@ -644,65 +610,30 @@
|
||||||
ln -s ${image} $image
|
ln -s ${image} $image
|
||||||
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
|
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
|
||||||
'';
|
'';
|
||||||
}
|
} // builtins.listToAttrs (map
|
||||||
|
(crossSystem: {
|
||||||
// builtins.listToAttrs (map (crossSystem: {
|
|
||||||
name = "nix-${crossSystem}";
|
name = "nix-${crossSystem}";
|
||||||
value = let
|
value = nixpkgsFor.${system}.cross.${crossSystem}.nix;
|
||||||
nixpkgsCross = import nixpkgs {
|
})
|
||||||
inherit system crossSystem;
|
crossSystems)
|
||||||
overlays = [ self.overlays.default ];
|
// builtins.listToAttrs (map
|
||||||
};
|
(stdenvName: {
|
||||||
in with commonDeps { pkgs = nixpkgsCross; }; nixpkgsCross.stdenv.mkDerivation {
|
name = "nix-${stdenvName}";
|
||||||
name = "nix-${version}";
|
value = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nix;
|
||||||
|
})
|
||||||
|
stdenvs)));
|
||||||
|
|
||||||
src = self;
|
devShells = let
|
||||||
|
makeShell = pkgs: stdenv:
|
||||||
VERSION_SUFFIX = versionSuffix;
|
|
||||||
|
|
||||||
outputs = [ "out" "dev" "doc" ];
|
|
||||||
|
|
||||||
nativeBuildInputs = nativeBuildDeps;
|
|
||||||
buildInputs = buildDeps ++ propagatedDeps;
|
|
||||||
|
|
||||||
configureFlags = [ "--sysconfdir=/etc" "--disable-doc-gen" ];
|
|
||||||
|
|
||||||
enableParallelBuilding = true;
|
|
||||||
|
|
||||||
makeFlags = "profiledir=$(out)/etc/profile.d";
|
|
||||||
|
|
||||||
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
|
|
||||||
mkdir -p $out/nix-support
|
|
||||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
|
||||||
'';
|
|
||||||
|
|
||||||
doInstallCheck = true;
|
|
||||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
|
||||||
};
|
|
||||||
}) (if system == "x86_64-linux" then crossSystems else [])))
|
|
||||||
|
|
||||||
// (builtins.listToAttrs (map (stdenvName:
|
|
||||||
nixpkgsFor.${system}.lib.nameValuePair
|
|
||||||
"nix-${stdenvName}"
|
|
||||||
nixpkgsFor.${system}."${stdenvName}Packages".nix
|
|
||||||
) stdenvs)));
|
|
||||||
|
|
||||||
devShells = forAllSystems (system:
|
|
||||||
forAllStdenvs (stdenv:
|
|
||||||
with nixpkgsFor.${system};
|
|
||||||
with commonDeps { inherit pkgs; };
|
with commonDeps { inherit pkgs; };
|
||||||
nixpkgsFor.${system}.${stdenv}.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "nix";
|
name = "nix";
|
||||||
|
|
||||||
outputs = [ "out" "dev" "doc" ];
|
outputs = [ "out" "dev" "doc" ];
|
||||||
|
|
||||||
nativeBuildInputs = nativeBuildDeps;
|
nativeBuildInputs = nativeBuildDeps
|
||||||
|
++ (lib.optionals stdenv.cc.isClang [ pkgs.bear pkgs.clang-tools ]);
|
||||||
|
|
||||||
buildInputs = buildDeps ++ propagatedDeps ++ awsDeps;
|
buildInputs = buildDeps ++ propagatedDeps ++ awsDeps;
|
||||||
|
|
||||||
inherit configureFlags;
|
inherit configureFlags;
|
||||||
|
@ -720,10 +651,21 @@
|
||||||
# Make bash completion work.
|
# Make bash completion work.
|
||||||
XDG_DATA_DIRS+=:$out/share
|
XDG_DATA_DIRS+=:$out/share
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
forAllSystems (system:
|
||||||
|
let
|
||||||
|
makeShells = prefix: pkgs:
|
||||||
|
lib.mapAttrs'
|
||||||
|
(k: v: lib.nameValuePair "${prefix}-${k}" v)
|
||||||
|
(forAllStdenvs (stdenvName: makeShell pkgs pkgs.${stdenvName}));
|
||||||
|
in
|
||||||
|
(makeShells "native" nixpkgsFor.${system}.native) //
|
||||||
|
(makeShells "static" nixpkgsFor.${system}.static) //
|
||||||
|
(forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) //
|
||||||
|
{
|
||||||
|
default = self.devShells.${system}.native-stdenvPackages;
|
||||||
}
|
}
|
||||||
)
|
|
||||||
// { default = self.devShells.${system}.stdenv; }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
12
maintainers/backporting.md
Normal file
12
maintainers/backporting.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
# Backporting
|
||||||
|
|
||||||
|
To [automatically backport a pull request](https://github.com/NixOS/nix/blob/master/.github/workflows/backport.yml) to a release branch once it's merged, assign it a label of the form [`backport <branch>`](https://github.com/NixOS/nix/labels?q=backport).
|
||||||
|
|
||||||
|
Since [GitHub Actions workflows will not trigger other workflows](https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow), checks on the automatic backport need to be triggered by another actor.
|
||||||
|
This is achieved by closing and reopening the backport pull request.
|
||||||
|
|
||||||
|
This specifically affects the [`installer_test`] check.
|
||||||
|
Note that it only runs after the other tests, so it may take a while to appear.
|
||||||
|
|
||||||
|
[`installer_test`]: https://github.com/NixOS/nix/blob/895dfc656a21f6252ddf48df0d1f215effa04ecb/.github/workflows/ci.yml#L70-L91
|
181
maintainers/release-process.md
Normal file
181
maintainers/release-process.md
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
# Nix release process
|
||||||
|
|
||||||
|
## Release artifacts
|
||||||
|
|
||||||
|
The release process is intended to create the following for each
|
||||||
|
release:
|
||||||
|
|
||||||
|
* A Git tag
|
||||||
|
|
||||||
|
* Binary tarballs in https://releases.nixos.org/?prefix=nix/
|
||||||
|
|
||||||
|
* Docker images
|
||||||
|
|
||||||
|
* Closures in https://cache.nixos.org
|
||||||
|
|
||||||
|
* (Optionally) Updated `fallback-paths.nix` in Nixpkgs
|
||||||
|
|
||||||
|
* An updated manual on https://nixos.org/manual/nix/stable/
|
||||||
|
|
||||||
|
## Creating a new release from the `master` branch
|
||||||
|
|
||||||
|
* Make sure that the [Hydra `master` jobset](https://hydra.nixos.org/jobset/nix/master) succeeds.
|
||||||
|
|
||||||
|
* In a checkout of the Nix repo, make sure you're on `master` and run
|
||||||
|
`git pull`.
|
||||||
|
|
||||||
|
* 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
|
||||||
|
$ git add doc/manual/src/release-notes/rl-$VERSION.md
|
||||||
|
$ git commit -a -m 'Release notes'
|
||||||
|
$ git push --set-upstream $REMOTE release-notes
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create a PR for `release-notes`.
|
||||||
|
|
||||||
|
* Wait for the PR to be merged.
|
||||||
|
|
||||||
|
* Create a branch for the release:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ git checkout master
|
||||||
|
$ git pull
|
||||||
|
$ git checkout -b $VERSION-maintenance
|
||||||
|
```
|
||||||
|
|
||||||
|
* Mark the release as stable:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ git cherry-pick f673551e71942a52b6d7ae66af8b67140904a76a
|
||||||
|
```
|
||||||
|
|
||||||
|
This removes the link to `rl-next.md` from the manual and sets
|
||||||
|
`officialRelease = true` in `flake.nix`.
|
||||||
|
|
||||||
|
* Push the release branch:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ git push --set-upstream origin $VERSION-maintenance
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create a jobset for the release branch on Hydra as follows:
|
||||||
|
|
||||||
|
* Go to the jobset of the previous release
|
||||||
|
(e.g. https://hydra.nixos.org/jobset/nix/maintenance-2.11).
|
||||||
|
|
||||||
|
* Select `Actions -> Clone this jobset`.
|
||||||
|
|
||||||
|
* Set identifier to `maintenance-$VERSION`.
|
||||||
|
|
||||||
|
* Set description to `$VERSION release branch`.
|
||||||
|
|
||||||
|
* Set flake URL to `github:NixOS/nix/$VERSION-maintenance`.
|
||||||
|
|
||||||
|
* Hit `Create jobset`.
|
||||||
|
|
||||||
|
* Wait for the new jobset to evaluate and build. If impatient, go to
|
||||||
|
the evaluation and select `Actions -> Bump builds to front of
|
||||||
|
queue`.
|
||||||
|
|
||||||
|
* When the jobset evaluation has succeeded building, take note of the
|
||||||
|
evaluation ID (e.g. `1780832` in
|
||||||
|
`https://hydra.nixos.org/eval/1780832`).
|
||||||
|
|
||||||
|
* Tag the release and upload the release artifacts to
|
||||||
|
[`releases.nixos.org`](https://releases.nixos.org/) and [Docker Hub](https://hub.docker.com/):
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ IS_LATEST=1 ./maintainers/upload-release.pl <EVAL-ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: `IS_LATEST=1` causes the `latest-release` branch to be
|
||||||
|
force-updated. This is used by the `nixos.org` website to get the
|
||||||
|
[latest Nix manual](https://nixos.org/manual/nixpkgs/unstable/).
|
||||||
|
|
||||||
|
TODO: This script requires the right AWS credentials. Document.
|
||||||
|
|
||||||
|
TODO: This script currently requires a
|
||||||
|
`/home/eelco/Dev/nix-pristine` and
|
||||||
|
`/home/eelco/Dev/nixpkgs-pristine`.
|
||||||
|
|
||||||
|
TODO: trigger nixos.org netlify: https://docs.netlify.com/configure-builds/build-hooks/
|
||||||
|
* Prepare for the next point release by editing `.version` to
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ echo 2.12.1 > .version
|
||||||
|
$ git commit -a -m 'Bump version'
|
||||||
|
$ git push
|
||||||
|
```
|
||||||
|
|
||||||
|
Commit and push this to the maintenance branch.
|
||||||
|
|
||||||
|
* Bump the version of `master`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ git checkout master
|
||||||
|
$ git pull
|
||||||
|
$ NEW_VERSION=2.13.0
|
||||||
|
$ echo -n $NEW_VERSION > .version
|
||||||
|
$ git checkout -b bump-$NEW_VERSION
|
||||||
|
$ git commit -a -m 'Bump version'
|
||||||
|
$ git push --set-upstream origin bump-$NEW_VERSION
|
||||||
|
```
|
||||||
|
|
||||||
|
Make a pull request and auto-merge it.
|
||||||
|
|
||||||
|
* Create a milestone for the next release, move all unresolved issues
|
||||||
|
from the previous milestone, and close the previous milestone. Set
|
||||||
|
the date for the next milestone 6 weeks from now.
|
||||||
|
|
||||||
|
* Create a backport label
|
||||||
|
|
||||||
|
* Post an [announcement on Discourse](https://discourse.nixos.org/c/announcements/8), including the contents of
|
||||||
|
`rl-$VERSION.md`.
|
||||||
|
|
||||||
|
## Creating a point release
|
||||||
|
|
||||||
|
* Wait for the desired evaluation of the maintenance jobset to finish
|
||||||
|
building.
|
||||||
|
|
||||||
|
* Run
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ IS_LATEST=1 ./maintainers/upload-release.pl <EVAL-ID>
|
||||||
|
```
|
||||||
|
|
||||||
|
Omit `IS_LATEST=1` when creating a point release that is not on the
|
||||||
|
most recent stable branch. This prevents `nixos.org` to going back
|
||||||
|
to an older release.
|
||||||
|
|
||||||
|
* Bump the version number of the release branch as above (e.g. to
|
||||||
|
`2.12.2`).
|
||||||
|
|
||||||
|
## Recovering from mistakes
|
||||||
|
|
||||||
|
`upload-release.pl` should be idempotent. For instance a wrong `IS_LATEST` value can be fixed that way, by running the script on the actual latest release.
|
||||||
|
|
|
@ -67,6 +67,7 @@ define build-library
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE :=
|
$(1)_LDFLAGS_USE :=
|
||||||
$(1)_LDFLAGS_USE_INSTALLED :=
|
$(1)_LDFLAGS_USE_INSTALLED :=
|
||||||
|
$(1)_LIB_CLOSURE := $(1)
|
||||||
|
|
||||||
$$(eval $$(call create-dir, $$(_d)))
|
$$(eval $$(call create-dir, $$(_d)))
|
||||||
|
|
||||||
|
@ -128,10 +129,12 @@ define build-library
|
||||||
+$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$^
|
+$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$^
|
||||||
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
|
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE))
|
||||||
|
|
||||||
$(1)_INSTALL_PATH := $$(libdir)/$$($(1)_NAME).a
|
$(1)_INSTALL_PATH := $$(libdir)/$$($(1)_NAME).a
|
||||||
|
|
||||||
|
$(1)_LIB_CLOSURE += $$($(1)_LIBS)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(1)_LDFLAGS_USE += $$($(1)_LDFLAGS_PROPAGATED)
|
$(1)_LDFLAGS_USE += $$($(1)_LDFLAGS_PROPAGATED)
|
||||||
|
|
|
@ -3,6 +3,9 @@ programs-list :=
|
||||||
# Build a program with symbolic name $(1). The program is defined by
|
# Build a program with symbolic name $(1). The program is defined by
|
||||||
# various variables prefixed by ‘$(1)_’:
|
# various variables prefixed by ‘$(1)_’:
|
||||||
#
|
#
|
||||||
|
# - $(1)_NAME: the name of the program (e.g. ‘foo’); defaults to
|
||||||
|
# $(1).
|
||||||
|
#
|
||||||
# - $(1)_DIR: the directory where the (non-installed) program will be
|
# - $(1)_DIR: the directory where the (non-installed) program will be
|
||||||
# placed.
|
# placed.
|
||||||
#
|
#
|
||||||
|
@ -23,11 +26,12 @@ programs-list :=
|
||||||
# - $(1)_INSTALL_DIR: the directory where the program will be
|
# - $(1)_INSTALL_DIR: the directory where the program will be
|
||||||
# installed; defaults to $(bindir).
|
# installed; defaults to $(bindir).
|
||||||
define build-program
|
define build-program
|
||||||
|
$(1)_NAME ?= $(1)
|
||||||
_d := $(buildprefix)$$($(1)_DIR)
|
_d := $(buildprefix)$$($(1)_DIR)
|
||||||
_srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src)))
|
_srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src)))
|
||||||
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
|
||||||
_libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH))
|
_libs := $$(foreach lib, $$($(1)_LIBS), $$(foreach lib2, $$($$(lib)_LIB_CLOSURE), $$($$(lib2)_PATH)))
|
||||||
$(1)_PATH := $$(_d)/$(1)
|
$(1)_PATH := $$(_d)/$$($(1)_NAME)
|
||||||
|
|
||||||
$$(eval $$(call create-dir, $$(_d)))
|
$$(eval $$(call create-dir, $$(_d)))
|
||||||
|
|
||||||
|
@ -38,7 +42,7 @@ define build-program
|
||||||
|
|
||||||
ifdef $(1)_INSTALL_DIR
|
ifdef $(1)_INSTALL_DIR
|
||||||
|
|
||||||
$(1)_INSTALL_PATH := $$($(1)_INSTALL_DIR)/$(1)
|
$(1)_INSTALL_PATH := $$($(1)_INSTALL_DIR)/$$($(1)_NAME)
|
||||||
|
|
||||||
$$(eval $$(call create-dir, $$($(1)_INSTALL_DIR)))
|
$$(eval $$(call create-dir, $$($(1)_INSTALL_DIR)))
|
||||||
|
|
||||||
|
@ -54,7 +58,7 @@ define build-program
|
||||||
else
|
else
|
||||||
|
|
||||||
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_PATH) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_PATH) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
|
||||||
install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$<
|
+$$(trace-install) install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$<
|
||||||
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
makefiles = local.mk
|
makefiles = local.mk
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++17 -I ../src
|
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src
|
||||||
|
|
||||||
-include Makefile.config
|
-include Makefile.config
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ static ref<Store> store()
|
||||||
static std::shared_ptr<Store> _store;
|
static std::shared_ptr<Store> _store;
|
||||||
if (!_store) {
|
if (!_store) {
|
||||||
try {
|
try {
|
||||||
|
initLibStore();
|
||||||
loadConfFile();
|
loadConfFile();
|
||||||
settings.lockCPU = false;
|
settings.lockCPU = false;
|
||||||
_store = openStore();
|
_store = openStore();
|
||||||
|
@ -295,7 +296,7 @@ SV * makeFixedOutputPath(int recursive, char * algo, char * hash, char * name)
|
||||||
auto h = Hash::parseAny(hash, parseHashType(algo));
|
auto h = Hash::parseAny(hash, parseHashType(algo));
|
||||||
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||||
auto path = store()->makeFixedOutputPath(name, FixedOutputInfo {
|
auto path = store()->makeFixedOutputPath(name, FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = method,
|
.method = method,
|
||||||
.hash = h,
|
.hash = h,
|
||||||
},
|
},
|
||||||
|
|
|
@ -136,7 +136,7 @@ EOF
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
$step. Delete the files Nix added to your system:
|
$step. Delete the files Nix added to your system:
|
||||||
|
|
||||||
sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
|
sudo rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix"
|
||||||
|
|
||||||
and that is it.
|
and that is it.
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,8 @@ fi
|
||||||
# shellcheck source=./nix-profile.sh.in
|
# shellcheck source=./nix-profile.sh.in
|
||||||
. "$nix/etc/profile.d/nix.sh"
|
. "$nix/etc/profile.d/nix.sh"
|
||||||
|
|
||||||
|
NIX_LINK="$HOME/.nix-profile"
|
||||||
|
|
||||||
if ! "$nix/bin/nix-env" -i "$nix"; then
|
if ! "$nix/bin/nix-env" -i "$nix"; then
|
||||||
echo "$0: unable to install Nix into your default profile" >&2
|
echo "$0: unable to install Nix into your default profile" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -196,7 +198,7 @@ fi
|
||||||
# Install an SSL certificate bundle.
|
# Install an SSL certificate bundle.
|
||||||
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
|
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
|
||||||
"$nix/bin/nix-env" -i "$cacert"
|
"$nix/bin/nix-env" -i "$cacert"
|
||||||
export NIX_SSL_CERT_FILE="$HOME/.nix-profile/etc/ssl/certs/ca-bundle.crt"
|
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Subscribe the user to the Nixpkgs channel and fetch it.
|
# Subscribe the user to the Nixpkgs channel and fetch it.
|
||||||
|
@ -214,8 +216,8 @@ fi
|
||||||
|
|
||||||
added=
|
added=
|
||||||
p=
|
p=
|
||||||
p_sh=$HOME/.nix-profile/etc/profile.d/nix.sh
|
p_sh=$NIX_LINK/etc/profile.d/nix.sh
|
||||||
p_fish=$HOME/.nix-profile/etc/profile.d/nix.fish
|
p_fish=$NIX_LINK/etc/profile.d/nix.fish
|
||||||
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
|
||||||
# Make the shell source nix.sh during login.
|
# Make the shell source nix.sh during login.
|
||||||
for i in .bash_profile .bash_login .profile; do
|
for i in .bash_profile .bash_login .profile; do
|
||||||
|
|
|
@ -2,7 +2,33 @@
|
||||||
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
|
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
|
||||||
__ETC_PROFILE_NIX_SOURCED=1
|
__ETC_PROFILE_NIX_SOURCED=1
|
||||||
|
|
||||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
NIX_LINK=$HOME/.nix-profile
|
||||||
|
if [ -n "$XDG_STATE_HOME" ]; then
|
||||||
|
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
|
||||||
|
else
|
||||||
|
NIX_LINK_NEW=$HOME/.local/state/nix/profile
|
||||||
|
fi
|
||||||
|
if ! [ -e "$NIX_LINK" ]; then
|
||||||
|
NIX_LINK="$NIX_LINK_NEW"
|
||||||
|
else
|
||||||
|
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
|
||||||
|
warning="\033[1;35mwarning:\033[0m"
|
||||||
|
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
|
||||||
|
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
|
||||||
|
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
|
||||||
|
else
|
||||||
|
# This should be an exceptionally rare occasion: the only way to get it would be to
|
||||||
|
# 1. Update to newer Nix;
|
||||||
|
# 2. Remove .nix-profile;
|
||||||
|
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
|
||||||
|
# 4. Roll back to older Nix.
|
||||||
|
# If someone did all that, they can probably figure out how to migrate the profile.
|
||||||
|
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||||
|
|
||||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||||
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||||
|
@ -34,4 +60,5 @@ else
|
||||||
unset -f check_nix_profiles
|
unset -f check_nix_profiles
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PATH="$HOME/.nix-profile/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
|
||||||
|
unset NIX_LINK
|
||||||
|
|
|
@ -2,11 +2,35 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||||
|
|
||||||
# Set up the per-user profile.
|
# Set up the per-user profile.
|
||||||
|
|
||||||
NIX_LINK=$HOME/.nix-profile
|
NIX_LINK="$HOME/.nix-profile"
|
||||||
|
if [ -n "$XDG_STATE_HOME" ]; then
|
||||||
|
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
|
||||||
|
else
|
||||||
|
NIX_LINK_NEW="$HOME/.local/state/nix/profile"
|
||||||
|
fi
|
||||||
|
if ! [ -e "$NIX_LINK" ]; then
|
||||||
|
NIX_LINK="$NIX_LINK_NEW"
|
||||||
|
else
|
||||||
|
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
|
||||||
|
warning="\033[1;35mwarning:\033[0m"
|
||||||
|
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
|
||||||
|
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
|
||||||
|
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
|
||||||
|
else
|
||||||
|
# This should be an exceptionally rare occasion: the only way to get it would be to
|
||||||
|
# 1. Update to newer Nix;
|
||||||
|
# 2. Remove .nix-profile;
|
||||||
|
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
|
||||||
|
# 4. Roll back to older Nix.
|
||||||
|
# If someone did all that, they can probably figure out how to migrate the profile.
|
||||||
|
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Set up environment.
|
# Set up environment.
|
||||||
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
|
||||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
|
||||||
|
|
||||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
# 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
|
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||||
|
@ -31,5 +55,5 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PATH="$NIX_LINK/bin:$PATH"
|
export PATH="$NIX_LINK/bin:$PATH"
|
||||||
unset NIX_LINK
|
unset NIX_LINK NIX_LINK_NEW
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -72,6 +72,7 @@ static int main_build_remote(int argc, char * * argv)
|
||||||
settings.set(name, value);
|
settings.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto maxBuildJobs = settings.maxBuildJobs;
|
||||||
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
||||||
|
|
||||||
initPlugins();
|
initPlugins();
|
||||||
|
@ -112,10 +113,14 @@ static int main_build_remote(int argc, char * * argv)
|
||||||
drvPath = store->parseStorePath(readString(source));
|
drvPath = store->parseStorePath(readString(source));
|
||||||
auto requiredFeatures = readStrings<std::set<std::string>>(source);
|
auto requiredFeatures = readStrings<std::set<std::string>>(source);
|
||||||
|
|
||||||
auto canBuildLocally = amWilling
|
/* It would be possible to build locally after some builds clear out,
|
||||||
|
so don't show the warning now: */
|
||||||
|
bool couldBuildLocally = maxBuildJobs > 0
|
||||||
&& ( neededSystem == settings.thisSystem
|
&& ( neededSystem == settings.thisSystem
|
||||||
|| settings.extraPlatforms.get().count(neededSystem) > 0)
|
|| settings.extraPlatforms.get().count(neededSystem) > 0)
|
||||||
&& allSupportedLocally(*store, requiredFeatures);
|
&& allSupportedLocally(*store, requiredFeatures);
|
||||||
|
/* It's possible to build this locally right now: */
|
||||||
|
bool canBuildLocally = amWilling && couldBuildLocally;
|
||||||
|
|
||||||
/* Error ignored here, will be caught later */
|
/* Error ignored here, will be caught later */
|
||||||
mkdir(currentLoad.c_str(), 0777);
|
mkdir(currentLoad.c_str(), 0777);
|
||||||
|
@ -214,7 +219,7 @@ static int main_build_remote(int argc, char * * argv)
|
||||||
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
|
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
|
||||||
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
|
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
|
||||||
|
|
||||||
printMsg(canBuildLocally ? lvlChatty : lvlWarn, error);
|
printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error);
|
||||||
|
|
||||||
std::cerr << "# decline\n";
|
std::cerr << "# decline\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
|
#include "repl.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -121,12 +122,22 @@ ref<EvalState> EvalCommand::getEvalState()
|
||||||
;
|
;
|
||||||
|
|
||||||
if (startReplOnEvalErrors) {
|
if (startReplOnEvalErrors) {
|
||||||
evalState->debugRepl = &runRepl;
|
evalState->debugRepl = &AbstractNixRepl::runSimple;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return ref<EvalState>(evalState);
|
return ref<EvalState>(evalState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MixOperateOnOptions::MixOperateOnOptions()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "derivation",
|
||||||
|
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
||||||
|
.category = installablesCategory,
|
||||||
|
.handler = {&operateOn, OperateOn::Derivation},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
||||||
: recursive(recursive)
|
: recursive(recursive)
|
||||||
{
|
{
|
||||||
|
@ -208,20 +219,6 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
|
||||||
run(store, *storePaths.begin());
|
run(store, *storePaths.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings editorFor(const Path & file, uint32_t line)
|
|
||||||
{
|
|
||||||
auto editor = getEnv("EDITOR").value_or("cat");
|
|
||||||
auto args = tokenizeString<Strings>(editor);
|
|
||||||
if (line > 0 && (
|
|
||||||
editor.find("emacs") != std::string::npos ||
|
|
||||||
editor.find("nano") != std::string::npos ||
|
|
||||||
editor.find("vim") != std::string::npos ||
|
|
||||||
editor.find("kak") != std::string::npos))
|
|
||||||
args.push_back(fmt("+%d", line));
|
|
||||||
args.push_back(file);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
MixProfile::MixProfile()
|
MixProfile::MixProfile()
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
|
|
|
@ -94,12 +94,8 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||||
{
|
{
|
||||||
std::optional<Path> file;
|
std::optional<Path> file;
|
||||||
std::optional<std::string> expr;
|
std::optional<std::string> expr;
|
||||||
bool readOnlyMode = false;
|
|
||||||
|
|
||||||
// FIXME: move this; not all commands (e.g. 'nix run') use it.
|
SourceExprCommand();
|
||||||
OperateOn operateOn = OperateOn::Output;
|
|
||||||
|
|
||||||
SourceExprCommand(bool supportReadOnlyMode = false);
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
||||||
ref<Store> store, std::vector<std::string> ss);
|
ref<Store> store, std::vector<std::string> ss);
|
||||||
|
@ -114,6 +110,11 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||||
void completeInstallable(std::string_view prefix);
|
void completeInstallable(std::string_view prefix);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MixReadOnlyOption : virtual Args
|
||||||
|
{
|
||||||
|
MixReadOnlyOption();
|
||||||
|
};
|
||||||
|
|
||||||
/* A command that operates on a list of "installables", which can be
|
/* A command that operates on a list of "installables", which can be
|
||||||
store paths, attribute paths, Nix expressions, etc. */
|
store paths, attribute paths, Nix expressions, etc. */
|
||||||
struct InstallablesCommand : virtual Args, SourceExprCommand
|
struct InstallablesCommand : virtual Args, SourceExprCommand
|
||||||
|
@ -139,7 +140,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||||
{
|
{
|
||||||
std::shared_ptr<Installable> installable;
|
std::shared_ptr<Installable> installable;
|
||||||
|
|
||||||
InstallableCommand(bool supportReadOnlyMode = false);
|
InstallableCommand();
|
||||||
|
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
||||||
|
@ -153,8 +154,15 @@ private:
|
||||||
std::string _installable{"."};
|
std::string _installable{"."};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MixOperateOnOptions : virtual Args
|
||||||
|
{
|
||||||
|
OperateOn operateOn = OperateOn::Output;
|
||||||
|
|
||||||
|
MixOperateOnOptions();
|
||||||
|
};
|
||||||
|
|
||||||
/* A command that operates on zero or more store paths. */
|
/* A command that operates on zero or more store paths. */
|
||||||
struct BuiltPathsCommand : public InstallablesCommand
|
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -227,10 +235,6 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||||
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
|
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper function to generate args that invoke $EDITOR on
|
|
||||||
filename:lineno. */
|
|
||||||
Strings editorFor(const Path & file, uint32_t line);
|
|
||||||
|
|
||||||
struct MixProfile : virtual StoreCommand
|
struct MixProfile : virtual StoreCommand
|
||||||
{
|
{
|
||||||
std::optional<Path> profile;
|
std::optional<Path> profile;
|
||||||
|
@ -280,8 +284,4 @@ void printClosureDiff(
|
||||||
const StorePath & afterPath,
|
const StorePath & afterPath,
|
||||||
std::string_view indent);
|
std::string_view indent);
|
||||||
|
|
||||||
|
|
||||||
void runRepl(
|
|
||||||
ref<EvalState> evalState,
|
|
||||||
const ValMap & extraEnv);
|
|
||||||
}
|
}
|
||||||
|
|
20
src/libcmd/editor-for.cc
Normal file
20
src/libcmd/editor-for.cc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "util.hh"
|
||||||
|
#include "editor-for.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
Strings editorFor(const Path & file, uint32_t line)
|
||||||
|
{
|
||||||
|
auto editor = getEnv("EDITOR").value_or("cat");
|
||||||
|
auto args = tokenizeString<Strings>(editor);
|
||||||
|
if (line > 0 && (
|
||||||
|
editor.find("emacs") != std::string::npos ||
|
||||||
|
editor.find("nano") != std::string::npos ||
|
||||||
|
editor.find("vim") != std::string::npos ||
|
||||||
|
editor.find("kak") != std::string::npos))
|
||||||
|
args.push_back(fmt("+%d", line));
|
||||||
|
args.push_back(file);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/libcmd/editor-for.hh
Normal file
11
src/libcmd/editor-for.hh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Helper function to generate args that invoke $EDITOR on
|
||||||
|
filename:lineno. */
|
||||||
|
Strings editorFor(const Path & file, uint32_t line);
|
||||||
|
|
||||||
|
}
|
109
src/libcmd/installable-attr-path.cc
Normal file
109
src/libcmd/installable-attr-path.cc
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "installable-attr-path.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "command.hh"
|
||||||
|
#include "attr-path.hh"
|
||||||
|
#include "common-eval-args.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
|
#include "eval.hh"
|
||||||
|
#include "get-drvs.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "shared.hh"
|
||||||
|
#include "flake/flake.hh"
|
||||||
|
#include "eval-cache.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
#include "registry.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
InstallableAttrPath::InstallableAttrPath(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
const std::string & attrPath,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
|
: InstallableValue(state)
|
||||||
|
, cmd(cmd)
|
||||||
|
, v(allocRootValue(v))
|
||||||
|
, attrPath(attrPath)
|
||||||
|
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
|
||||||
|
{
|
||||||
|
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
||||||
|
state.forceValue(*vRes, pos);
|
||||||
|
return {vRes, pos};
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
|
||||||
|
{
|
||||||
|
auto v = toValue(*state).first;
|
||||||
|
|
||||||
|
Bindings & autoArgs = *cmd.getAutoArgs(*state);
|
||||||
|
|
||||||
|
DrvInfos drvInfos;
|
||||||
|
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
|
||||||
|
|
||||||
|
// Backward compatibility hack: group results by drvPath. This
|
||||||
|
// helps keep .all output together.
|
||||||
|
std::map<StorePath, OutputsSpec> byDrvPath;
|
||||||
|
|
||||||
|
for (auto & drvInfo : drvInfos) {
|
||||||
|
auto drvPath = drvInfo.queryDrvPath();
|
||||||
|
if (!drvPath)
|
||||||
|
throw Error("'%s' is not a derivation", what());
|
||||||
|
|
||||||
|
auto newOutputs = std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||||
|
std::set<std::string> outputsToInstall;
|
||||||
|
for (auto & output : drvInfo.queryOutputs(false, true))
|
||||||
|
outputsToInstall.insert(output.first);
|
||||||
|
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw());
|
||||||
|
|
||||||
|
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
|
||||||
|
|
||||||
|
if (!didInsert)
|
||||||
|
iter->second = iter->second.union_(newOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo res;
|
||||||
|
for (auto & [drvPath, outputs] : byDrvPath)
|
||||||
|
res.push_back({
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = outputs,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallableAttrPath InstallableAttrPath::parse(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
state, cmd, v,
|
||||||
|
prefix == "." ? "" : std::string { prefix },
|
||||||
|
extendedOutputsSpec
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
src/libcmd/installable-attr-path.hh
Normal file
56
src/libcmd/installable-attr-path.hh
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "installable-value.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "command.hh"
|
||||||
|
#include "attr-path.hh"
|
||||||
|
#include "common-eval-args.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
|
#include "eval.hh"
|
||||||
|
#include "get-drvs.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "shared.hh"
|
||||||
|
#include "eval-cache.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
#include "registry.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class InstallableAttrPath : public InstallableValue
|
||||||
|
{
|
||||||
|
SourceExprCommand & cmd;
|
||||||
|
RootValue v;
|
||||||
|
std::string attrPath;
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec;
|
||||||
|
|
||||||
|
InstallableAttrPath(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
const std::string & attrPath,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec);
|
||||||
|
|
||||||
|
std::string what() const override { return attrPath; };
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
|
DerivedPathsWithInfo toDerivedPaths() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static InstallableAttrPath parse(
|
||||||
|
ref<EvalState> state,
|
||||||
|
SourceExprCommand & cmd,
|
||||||
|
Value * v,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
70
src/libcmd/installable-derived-path.cc
Normal file
70
src/libcmd/installable-derived-path.cc
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string InstallableDerivedPath::what() const
|
||||||
|
{
|
||||||
|
return derivedPath.to_string(*store);
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths()
|
||||||
|
{
|
||||||
|
return {{.path = derivedPath, .info = {} }};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<StorePath> InstallableDerivedPath::getStorePath()
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const DerivedPath::Built & bfd) {
|
||||||
|
return bfd.drvPath;
|
||||||
|
},
|
||||||
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
|
return bo.path;
|
||||||
|
},
|
||||||
|
}, derivedPath.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallableDerivedPath InstallableDerivedPath::parse(
|
||||||
|
ref<Store> store,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
|
{
|
||||||
|
auto derivedPath = std::visit(overloaded {
|
||||||
|
// If the user did not use ^, we treat the output more liberally.
|
||||||
|
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||||
|
// First, we accept a symlink chain or an actual store path.
|
||||||
|
auto storePath = store->followLinksToStorePath(prefix);
|
||||||
|
// Second, we see if the store path ends in `.drv` to decide what sort
|
||||||
|
// of derived path they want.
|
||||||
|
//
|
||||||
|
// This handling predates the `^` syntax. The `^*` in
|
||||||
|
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
||||||
|
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
||||||
|
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
||||||
|
//
|
||||||
|
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
||||||
|
return storePath.isDerivation()
|
||||||
|
? (DerivedPath) DerivedPath::Built {
|
||||||
|
.drvPath = std::move(storePath),
|
||||||
|
.outputs = OutputsSpec::All {},
|
||||||
|
}
|
||||||
|
: (DerivedPath) DerivedPath::Opaque {
|
||||||
|
.path = std::move(storePath),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// If the user did use ^, we just do exactly what is written.
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||||
|
return DerivedPath::Built {
|
||||||
|
.drvPath = store->parseStorePath(prefix),
|
||||||
|
.outputs = outputSpec,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw());
|
||||||
|
return InstallableDerivedPath {
|
||||||
|
store,
|
||||||
|
std::move(derivedPath),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/libcmd/installable-derived-path.hh
Normal file
28
src/libcmd/installable-derived-path.hh
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "installables.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct InstallableDerivedPath : Installable
|
||||||
|
{
|
||||||
|
ref<Store> store;
|
||||||
|
DerivedPath derivedPath;
|
||||||
|
|
||||||
|
InstallableDerivedPath(ref<Store> store, DerivedPath && derivedPath)
|
||||||
|
: store(store), derivedPath(std::move(derivedPath))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::string what() const override;
|
||||||
|
|
||||||
|
DerivedPathsWithInfo toDerivedPaths() override;
|
||||||
|
|
||||||
|
std::optional<StorePath> getStorePath() override;
|
||||||
|
|
||||||
|
static InstallableDerivedPath parse(
|
||||||
|
ref<Store> store,
|
||||||
|
std::string_view prefix,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
236
src/libcmd/installable-flake.cc
Normal file
236
src/libcmd/installable-flake.cc
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
#include "globals.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "command.hh"
|
||||||
|
#include "attr-path.hh"
|
||||||
|
#include "common-eval-args.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
#include "eval-inline.hh"
|
||||||
|
#include "eval.hh"
|
||||||
|
#include "get-drvs.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "shared.hh"
|
||||||
|
#include "flake/flake.hh"
|
||||||
|
#include "eval-cache.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
#include "registry.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
||||||
|
{
|
||||||
|
std::vector<std::string> res;
|
||||||
|
|
||||||
|
for (auto & prefix : prefixes)
|
||||||
|
res.push_back(prefix + *attrPaths.begin());
|
||||||
|
|
||||||
|
for (auto & s : attrPaths)
|
||||||
|
res.push_back(s);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
|
||||||
|
{
|
||||||
|
auto vFlake = state.allocValue();
|
||||||
|
|
||||||
|
callFlake(state, lockedFlake, *vFlake);
|
||||||
|
|
||||||
|
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||||
|
assert(aOutputs);
|
||||||
|
|
||||||
|
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
|
||||||
|
|
||||||
|
return aOutputs->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string showAttrPaths(const std::vector<std::string> & paths)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
for (const auto & [n, i] : enumerate(paths)) {
|
||||||
|
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
|
||||||
|
s += '\''; s += i; s += '\'';
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallableFlake::InstallableFlake(
|
||||||
|
SourceExprCommand * cmd,
|
||||||
|
ref<EvalState> state,
|
||||||
|
FlakeRef && flakeRef,
|
||||||
|
std::string_view fragment,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec,
|
||||||
|
Strings attrPaths,
|
||||||
|
Strings prefixes,
|
||||||
|
const flake::LockFlags & lockFlags)
|
||||||
|
: InstallableValue(state),
|
||||||
|
flakeRef(flakeRef),
|
||||||
|
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
||||||
|
prefixes(fragment == "" ? Strings{} : prefixes),
|
||||||
|
extendedOutputsSpec(std::move(extendedOutputsSpec)),
|
||||||
|
lockFlags(lockFlags)
|
||||||
|
{
|
||||||
|
if (cmd && cmd->getAutoArgs(*state)->size())
|
||||||
|
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
||||||
|
{
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
|
||||||
|
|
||||||
|
auto attr = getCursor(*state);
|
||||||
|
|
||||||
|
auto attrPath = attr->getAttrPathStr();
|
||||||
|
|
||||||
|
if (!attr->isDerivation()) {
|
||||||
|
|
||||||
|
// FIXME: use eval cache?
|
||||||
|
auto v = attr->forceValue();
|
||||||
|
|
||||||
|
if (v.type() == nPath) {
|
||||||
|
PathSet context;
|
||||||
|
auto storePath = state->copyPathToStore(context, Path(v.path));
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Opaque {
|
||||||
|
.path = std::move(storePath),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (v.type() == nString) {
|
||||||
|
PathSet context;
|
||||||
|
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
|
||||||
|
auto storePath = state->store->maybeParseStorePath(s);
|
||||||
|
if (storePath && context.count(std::string(s))) {
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Opaque {
|
||||||
|
.path = std::move(*storePath),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
} else
|
||||||
|
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto drvPath = attr->forceDerivation();
|
||||||
|
|
||||||
|
std::optional<NixInt> priority;
|
||||||
|
|
||||||
|
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||||
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||||
|
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
||||||
|
priority = aPriority->getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = std::move(drvPath),
|
||||||
|
.outputs = std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||||
|
std::set<std::string> outputsToInstall;
|
||||||
|
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||||
|
if (aOutputSpecified->getBool()) {
|
||||||
|
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
||||||
|
outputsToInstall = { aOutputName->getString() };
|
||||||
|
}
|
||||||
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||||
|
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
||||||
|
for (auto & s : aOutputsToInstall->getListOfStrings())
|
||||||
|
outputsToInstall.insert(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputsToInstall.empty())
|
||||||
|
outputsToInstall.insert("out");
|
||||||
|
|
||||||
|
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw()),
|
||||||
|
},
|
||||||
|
.info = {
|
||||||
|
.priority = priority,
|
||||||
|
.originalRef = flakeRef,
|
||||||
|
.resolvedRef = getLockedFlake()->flake.lockedRef,
|
||||||
|
.attrPath = attrPath,
|
||||||
|
.extendedOutputsSpec = extendedOutputsSpec,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||||
|
{
|
||||||
|
return {&getCursor(state)->forceValue(), noPos};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
|
InstallableFlake::getCursors(EvalState & state)
|
||||||
|
{
|
||||||
|
auto evalCache = openEvalCache(state,
|
||||||
|
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
|
||||||
|
|
||||||
|
auto root = evalCache->getRoot();
|
||||||
|
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>> res;
|
||||||
|
|
||||||
|
Suggestions suggestions;
|
||||||
|
auto attrPaths = getActualAttrPaths();
|
||||||
|
|
||||||
|
for (auto & attrPath : attrPaths) {
|
||||||
|
debug("trying flake output attribute '%s'", attrPath);
|
||||||
|
|
||||||
|
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||||
|
if (attr) {
|
||||||
|
res.push_back(ref(*attr));
|
||||||
|
} else {
|
||||||
|
suggestions += attr.getSuggestions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.size() == 0)
|
||||||
|
throw Error(
|
||||||
|
suggestions,
|
||||||
|
"flake '%s' does not provide attribute %s",
|
||||||
|
flakeRef,
|
||||||
|
showAttrPaths(attrPaths));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||||
|
{
|
||||||
|
if (!_lockedFlake) {
|
||||||
|
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||||
|
lockFlagsApplyConfig.applyNixConfig = true;
|
||||||
|
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||||
|
}
|
||||||
|
return _lockedFlake;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
|
||||||
|
{
|
||||||
|
auto lockedFlake = getLockedFlake();
|
||||||
|
|
||||||
|
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
|
||||||
|
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
|
||||||
|
debug("using nixpkgs flake '%s'", lockedNode->lockedRef);
|
||||||
|
return std::move(lockedNode->lockedRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Installable::nixpkgsFlakeRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
src/libcmd/installable-flake.hh
Normal file
50
src/libcmd/installable-flake.hh
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "installable-value.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct InstallableFlake : InstallableValue
|
||||||
|
{
|
||||||
|
FlakeRef flakeRef;
|
||||||
|
Strings attrPaths;
|
||||||
|
Strings prefixes;
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec;
|
||||||
|
const flake::LockFlags & lockFlags;
|
||||||
|
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
|
||||||
|
|
||||||
|
InstallableFlake(
|
||||||
|
SourceExprCommand * cmd,
|
||||||
|
ref<EvalState> state,
|
||||||
|
FlakeRef && flakeRef,
|
||||||
|
std::string_view fragment,
|
||||||
|
ExtendedOutputsSpec extendedOutputsSpec,
|
||||||
|
Strings attrPaths,
|
||||||
|
Strings prefixes,
|
||||||
|
const flake::LockFlags & lockFlags);
|
||||||
|
|
||||||
|
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||||
|
|
||||||
|
std::vector<std::string> getActualAttrPaths();
|
||||||
|
|
||||||
|
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
|
||||||
|
|
||||||
|
DerivedPathsWithInfo toDerivedPaths() override;
|
||||||
|
|
||||||
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
|
/* Get a cursor to every attrpath in getActualAttrPaths()
|
||||||
|
that exists. However if none exists, throw an exception. */
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
|
getCursors(EvalState & state) override;
|
||||||
|
|
||||||
|
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
||||||
|
|
||||||
|
FlakeRef nixpkgsFlakeRef() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
ref<eval_cache::EvalCache> openEvalCache(
|
||||||
|
EvalState & state,
|
||||||
|
std::shared_ptr<flake::LockedFlake> lockedFlake);
|
||||||
|
|
||||||
|
}
|
14
src/libcmd/installable-value.hh
Normal file
14
src/libcmd/installable-value.hh
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "installables.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct InstallableValue : Installable
|
||||||
|
{
|
||||||
|
ref<EvalState> state;
|
||||||
|
|
||||||
|
InstallableValue(ref<EvalState> state) : state(state) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "installables.hh"
|
#include "installables.hh"
|
||||||
|
#include "installable-derived-path.hh"
|
||||||
|
#include "installable-attr-path.hh"
|
||||||
|
#include "installable-flake.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
|
@ -144,7 +147,7 @@ void MixFlakeOptions::completionHook()
|
||||||
completeFlakeInput(*prefix);
|
completeFlakeInput(*prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
SourceExprCommand::SourceExprCommand()
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "file",
|
.longName = "file",
|
||||||
|
@ -166,24 +169,18 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
||||||
.labels = {"expr"},
|
.labels = {"expr"},
|
||||||
.handler = {&expr}
|
.handler = {&expr}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addFlag({
|
MixReadOnlyOption::MixReadOnlyOption()
|
||||||
.longName = "derivation",
|
{
|
||||||
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
|
|
||||||
.category = installablesCategory,
|
|
||||||
.handler = {&operateOn, OperateOn::Derivation},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (supportReadOnlyMode) {
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "read-only",
|
.longName = "read-only",
|
||||||
.description =
|
.description =
|
||||||
"Do not instantiate each evaluated derivation. "
|
"Do not instantiate each evaluated derivation. "
|
||||||
"This improves performance, but can cause errors when accessing "
|
"This improves performance, but can cause errors when accessing "
|
||||||
"store paths of derivations during evaluation.",
|
"store paths of derivations during evaluation.",
|
||||||
.handler = {&readOnlyMode, true},
|
.handler = {&settings.readOnlyMode, true},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
|
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
|
||||||
|
@ -379,10 +376,9 @@ Installable::getCursors(EvalState & state)
|
||||||
ref<eval_cache::AttrCursor>
|
ref<eval_cache::AttrCursor>
|
||||||
Installable::getCursor(EvalState & state)
|
Installable::getCursor(EvalState & state)
|
||||||
{
|
{
|
||||||
auto cursors = getCursors(state);
|
/* Although getCursors should return at least one element, in case it doesn't,
|
||||||
if (cursors.empty())
|
bound check to avoid an undefined behavior for vector[0] */
|
||||||
throw Error("cannot find flake attribute '%s'", what());
|
return getCursors(state).at(0);
|
||||||
return cursors[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static StorePath getDeriver(
|
static StorePath getDeriver(
|
||||||
|
@ -397,143 +393,6 @@ static StorePath getDeriver(
|
||||||
return *derivers.begin();
|
return *derivers.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InstallableStorePath : Installable
|
|
||||||
{
|
|
||||||
ref<Store> store;
|
|
||||||
DerivedPath req;
|
|
||||||
|
|
||||||
InstallableStorePath(ref<Store> store, DerivedPath && req)
|
|
||||||
: store(store), req(std::move(req))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
std::string what() const override
|
|
||||||
{
|
|
||||||
return req.to_string(*store);
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override
|
|
||||||
{
|
|
||||||
return {{.path = req, .info = {} }};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<StorePath> getStorePath() override
|
|
||||||
{
|
|
||||||
return std::visit(overloaded {
|
|
||||||
[&](const DerivedPath::Built & bfd) {
|
|
||||||
return bfd.drvPath;
|
|
||||||
},
|
|
||||||
[&](const DerivedPath::Opaque & bo) {
|
|
||||||
return bo.path;
|
|
||||||
},
|
|
||||||
}, req.raw());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstallableAttrPath : InstallableValue
|
|
||||||
{
|
|
||||||
SourceExprCommand & cmd;
|
|
||||||
RootValue v;
|
|
||||||
std::string attrPath;
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec;
|
|
||||||
|
|
||||||
InstallableAttrPath(
|
|
||||||
ref<EvalState> state,
|
|
||||||
SourceExprCommand & cmd,
|
|
||||||
Value * v,
|
|
||||||
const std::string & attrPath,
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec)
|
|
||||||
: InstallableValue(state)
|
|
||||||
, cmd(cmd)
|
|
||||||
, v(allocRootValue(v))
|
|
||||||
, attrPath(attrPath)
|
|
||||||
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
std::string what() const override { return attrPath; }
|
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override
|
|
||||||
{
|
|
||||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
|
||||||
state.forceValue(*vRes, pos);
|
|
||||||
return {vRes, pos};
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override
|
|
||||||
{
|
|
||||||
auto v = toValue(*state).first;
|
|
||||||
|
|
||||||
Bindings & autoArgs = *cmd.getAutoArgs(*state);
|
|
||||||
|
|
||||||
DrvInfos drvInfos;
|
|
||||||
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
|
|
||||||
|
|
||||||
// Backward compatibility hack: group results by drvPath. This
|
|
||||||
// helps keep .all output together.
|
|
||||||
std::map<StorePath, OutputsSpec> byDrvPath;
|
|
||||||
|
|
||||||
for (auto & drvInfo : drvInfos) {
|
|
||||||
auto drvPath = drvInfo.queryDrvPath();
|
|
||||||
if (!drvPath)
|
|
||||||
throw Error("'%s' is not a derivation", what());
|
|
||||||
|
|
||||||
auto newOutputs = std::visit(overloaded {
|
|
||||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
|
||||||
std::set<std::string> outputsToInstall;
|
|
||||||
for (auto & output : drvInfo.queryOutputs(false, true))
|
|
||||||
outputsToInstall.insert(output.first);
|
|
||||||
return OutputsSpec::Names { std::move(outputsToInstall) };
|
|
||||||
},
|
|
||||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
|
||||||
return e;
|
|
||||||
},
|
|
||||||
}, extendedOutputsSpec.raw());
|
|
||||||
|
|
||||||
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
|
|
||||||
|
|
||||||
if (!didInsert)
|
|
||||||
iter->second = iter->second.union_(newOutputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo res;
|
|
||||||
for (auto & [drvPath, outputs] : byDrvPath)
|
|
||||||
res.push_back({
|
|
||||||
.path = DerivedPath::Built {
|
|
||||||
.drvPath = drvPath,
|
|
||||||
.outputs = outputs,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
|
||||||
{
|
|
||||||
std::vector<std::string> res;
|
|
||||||
|
|
||||||
for (auto & prefix : prefixes)
|
|
||||||
res.push_back(prefix + *attrPaths.begin());
|
|
||||||
|
|
||||||
for (auto & s : attrPaths)
|
|
||||||
res.push_back(s);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
|
|
||||||
{
|
|
||||||
auto vFlake = state.allocValue();
|
|
||||||
|
|
||||||
callFlake(state, lockedFlake, *vFlake);
|
|
||||||
|
|
||||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
|
||||||
assert(aOutputs);
|
|
||||||
|
|
||||||
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
|
|
||||||
|
|
||||||
return aOutputs->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref<eval_cache::EvalCache> openEvalCache(
|
ref<eval_cache::EvalCache> openEvalCache(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
||||||
|
@ -563,214 +422,11 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string showAttrPaths(const std::vector<std::string> & paths)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
for (const auto & [n, i] : enumerate(paths)) {
|
|
||||||
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
|
|
||||||
s += '\''; s += i; s += '\'';
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstallableFlake::InstallableFlake(
|
|
||||||
SourceExprCommand * cmd,
|
|
||||||
ref<EvalState> state,
|
|
||||||
FlakeRef && flakeRef,
|
|
||||||
std::string_view fragment,
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec,
|
|
||||||
Strings attrPaths,
|
|
||||||
Strings prefixes,
|
|
||||||
const flake::LockFlags & lockFlags)
|
|
||||||
: InstallableValue(state),
|
|
||||||
flakeRef(flakeRef),
|
|
||||||
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
|
||||||
prefixes(fragment == "" ? Strings{} : prefixes),
|
|
||||||
extendedOutputsSpec(std::move(extendedOutputsSpec)),
|
|
||||||
lockFlags(lockFlags)
|
|
||||||
{
|
|
||||||
if (cmd && cmd->getAutoArgs(*state)->size())
|
|
||||||
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
|
|
||||||
}
|
|
||||||
|
|
||||||
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
|
||||||
{
|
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
|
|
||||||
|
|
||||||
auto attr = getCursor(*state);
|
|
||||||
|
|
||||||
auto attrPath = attr->getAttrPathStr();
|
|
||||||
|
|
||||||
if (!attr->isDerivation()) {
|
|
||||||
|
|
||||||
// FIXME: use eval cache?
|
|
||||||
auto v = attr->forceValue();
|
|
||||||
|
|
||||||
if (v.type() == nPath) {
|
|
||||||
PathSet context;
|
|
||||||
auto storePath = state->copyPathToStore(context, Path(v.path));
|
|
||||||
return {{
|
|
||||||
.path = DerivedPath::Opaque {
|
|
||||||
.path = std::move(storePath),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (v.type() == nString) {
|
|
||||||
PathSet context;
|
|
||||||
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
|
|
||||||
auto storePath = state->store->maybeParseStorePath(s);
|
|
||||||
if (storePath && context.count(std::string(s))) {
|
|
||||||
return {{
|
|
||||||
.path = DerivedPath::Opaque {
|
|
||||||
.path = std::move(*storePath),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
} else
|
|
||||||
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto drvPath = attr->forceDerivation();
|
|
||||||
|
|
||||||
std::optional<NixInt> priority;
|
|
||||||
|
|
||||||
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
|
||||||
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
|
||||||
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
|
||||||
priority = aPriority->getInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {{
|
|
||||||
.path = DerivedPath::Built {
|
|
||||||
.drvPath = std::move(drvPath),
|
|
||||||
.outputs = std::visit(overloaded {
|
|
||||||
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
|
||||||
std::set<std::string> outputsToInstall;
|
|
||||||
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
|
||||||
if (aOutputSpecified->getBool()) {
|
|
||||||
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
|
||||||
outputsToInstall = { aOutputName->getString() };
|
|
||||||
}
|
|
||||||
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
|
||||||
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
|
||||||
for (auto & s : aOutputsToInstall->getListOfStrings())
|
|
||||||
outputsToInstall.insert(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputsToInstall.empty())
|
|
||||||
outputsToInstall.insert("out");
|
|
||||||
|
|
||||||
return OutputsSpec::Names { std::move(outputsToInstall) };
|
|
||||||
},
|
|
||||||
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
|
||||||
return e;
|
|
||||||
},
|
|
||||||
}, extendedOutputsSpec.raw()),
|
|
||||||
},
|
|
||||||
.info = {
|
|
||||||
.priority = priority,
|
|
||||||
.originalRef = flakeRef,
|
|
||||||
.resolvedRef = getLockedFlake()->flake.lockedRef,
|
|
||||||
.attrPath = attrPath,
|
|
||||||
.extendedOutputsSpec = extendedOutputsSpec,
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
|
||||||
{
|
|
||||||
return {&getCursor(state)->forceValue(), noPos};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>>
|
|
||||||
InstallableFlake::getCursors(EvalState & state)
|
|
||||||
{
|
|
||||||
auto evalCache = openEvalCache(state,
|
|
||||||
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
|
|
||||||
|
|
||||||
auto root = evalCache->getRoot();
|
|
||||||
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>> res;
|
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
|
||||||
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
|
||||||
if (attr) res.push_back(ref(*attr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
|
|
||||||
{
|
|
||||||
auto lockedFlake = getLockedFlake();
|
|
||||||
|
|
||||||
auto cache = openEvalCache(state, lockedFlake);
|
|
||||||
auto root = cache->getRoot();
|
|
||||||
|
|
||||||
Suggestions suggestions;
|
|
||||||
|
|
||||||
auto attrPaths = getActualAttrPaths();
|
|
||||||
|
|
||||||
for (auto & attrPath : attrPaths) {
|
|
||||||
debug("trying flake output attribute '%s'", attrPath);
|
|
||||||
|
|
||||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
|
||||||
parseAttrPath(state, attrPath),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!attrOrSuggestions) {
|
|
||||||
suggestions += attrOrSuggestions.getSuggestions();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *attrOrSuggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error(
|
|
||||||
suggestions,
|
|
||||||
"flake '%s' does not provide attribute %s",
|
|
||||||
flakeRef,
|
|
||||||
showAttrPaths(attrPaths));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
|
||||||
{
|
|
||||||
if (!_lockedFlake) {
|
|
||||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
|
||||||
lockFlagsApplyConfig.applyNixConfig = true;
|
|
||||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
|
||||||
}
|
|
||||||
return _lockedFlake;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
|
|
||||||
{
|
|
||||||
auto lockedFlake = getLockedFlake();
|
|
||||||
|
|
||||||
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
|
|
||||||
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
|
|
||||||
debug("using nixpkgs flake '%s'", lockedNode->lockedRef);
|
|
||||||
return std::move(lockedNode->lockedRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Installable::nixpkgsFlakeRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
ref<Store> store, std::vector<std::string> ss)
|
ref<Store> store, std::vector<std::string> ss)
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<Installable>> result;
|
std::vector<std::shared_ptr<Installable>> result;
|
||||||
|
|
||||||
if (readOnlyMode) {
|
|
||||||
settings.readOnlyMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file || expr) {
|
if (file || expr) {
|
||||||
if (file && expr)
|
if (file && expr)
|
||||||
throw UsageError("'--file' and '--expr' are exclusive");
|
throw UsageError("'--file' and '--expr' are exclusive");
|
||||||
|
@ -796,9 +452,8 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||||
result.push_back(
|
result.push_back(
|
||||||
std::make_shared<InstallableAttrPath>(
|
std::make_shared<InstallableAttrPath>(
|
||||||
state, *this, vFile,
|
InstallableAttrPath::parse(
|
||||||
prefix == "." ? "" : std::string { prefix },
|
state, *this, vFile, prefix, extendedOutputsSpec)));
|
||||||
extendedOutputsSpec));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -811,41 +466,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
auto prefix = std::move(prefix_);
|
auto prefix = std::move(prefix_);
|
||||||
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
|
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
|
||||||
|
|
||||||
auto found = prefix.find('/');
|
if (prefix.find('/') != std::string::npos) {
|
||||||
if (found != std::string::npos) {
|
|
||||||
try {
|
try {
|
||||||
auto derivedPath = std::visit(overloaded {
|
result.push_back(std::make_shared<InstallableDerivedPath>(
|
||||||
// If the user did not use ^, we treat the output more liberally.
|
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec)));
|
||||||
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
|
||||||
// First, we accept a symlink chain or an actual store path.
|
|
||||||
auto storePath = store->followLinksToStorePath(prefix);
|
|
||||||
// Second, we see if the store path ends in `.drv` to decide what sort
|
|
||||||
// of derived path they want.
|
|
||||||
//
|
|
||||||
// This handling predates the `^` syntax. The `^*` in
|
|
||||||
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
|
||||||
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
|
||||||
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
|
||||||
//
|
|
||||||
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
|
||||||
return storePath.isDerivation()
|
|
||||||
? (DerivedPath) DerivedPath::Built {
|
|
||||||
.drvPath = std::move(storePath),
|
|
||||||
.outputs = OutputsSpec::All {},
|
|
||||||
}
|
|
||||||
: (DerivedPath) DerivedPath::Opaque {
|
|
||||||
.path = std::move(storePath),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
// If the user did use ^, we just do exactly what is written.
|
|
||||||
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
|
||||||
return DerivedPath::Built {
|
|
||||||
.drvPath = store->parseStorePath(prefix),
|
|
||||||
.outputs = outputSpec,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}, extendedOutputsSpec.raw());
|
|
||||||
result.push_back(std::make_shared<InstallableStorePath>(store, std::move(derivedPath)));
|
|
||||||
continue;
|
continue;
|
||||||
} catch (BadStorePath &) {
|
} catch (BadStorePath &) {
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -1081,8 +705,8 @@ void InstallablesCommand::prepare()
|
||||||
installables = load();
|
installables = load();
|
||||||
}
|
}
|
||||||
|
|
||||||
Installables InstallablesCommand::load() {
|
Installables InstallablesCommand::load()
|
||||||
Installables installables;
|
{
|
||||||
if (_installables.empty() && useDefaultInstallables())
|
if (_installables.empty() && useDefaultInstallables())
|
||||||
// FIXME: commands like "nix profile install" should not have a
|
// FIXME: commands like "nix profile install" should not have a
|
||||||
// default, probably.
|
// default, probably.
|
||||||
|
@ -1092,16 +716,13 @@ Installables InstallablesCommand::load() {
|
||||||
|
|
||||||
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
|
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
|
||||||
{
|
{
|
||||||
if (_installables.empty()) {
|
if (_installables.empty() && useDefaultInstallables())
|
||||||
if (useDefaultInstallables())
|
|
||||||
return {"."};
|
return {"."};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return _installables;
|
return _installables;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
InstallableCommand::InstallableCommand()
|
||||||
: SourceExprCommand(supportReadOnlyMode)
|
: SourceExprCommand()
|
||||||
{
|
{
|
||||||
expectArgs({
|
expectArgs({
|
||||||
.label = "installable",
|
.label = "installable",
|
||||||
|
|
|
@ -103,9 +103,13 @@ struct Installable
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a cursor to each value this Installable could refer to. However
|
||||||
|
if none exists, throw exception instead of returning empty vector. */
|
||||||
virtual std::vector<ref<eval_cache::AttrCursor>>
|
virtual std::vector<ref<eval_cache::AttrCursor>>
|
||||||
getCursors(EvalState & state);
|
getCursors(EvalState & state);
|
||||||
|
|
||||||
|
/* Get the first and most preferred cursor this Installable could refer
|
||||||
|
to, or throw an exception if none exists. */
|
||||||
virtual ref<eval_cache::AttrCursor>
|
virtual ref<eval_cache::AttrCursor>
|
||||||
getCursor(EvalState & state);
|
getCursor(EvalState & state);
|
||||||
|
|
||||||
|
@ -157,58 +161,4 @@ struct Installable
|
||||||
|
|
||||||
typedef std::vector<std::shared_ptr<Installable>> Installables;
|
typedef std::vector<std::shared_ptr<Installable>> Installables;
|
||||||
|
|
||||||
struct InstallableValue : Installable
|
|
||||||
{
|
|
||||||
ref<EvalState> state;
|
|
||||||
|
|
||||||
InstallableValue(ref<EvalState> state) : state(state) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstallableFlake : InstallableValue
|
|
||||||
{
|
|
||||||
FlakeRef flakeRef;
|
|
||||||
Strings attrPaths;
|
|
||||||
Strings prefixes;
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec;
|
|
||||||
const flake::LockFlags & lockFlags;
|
|
||||||
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
|
|
||||||
|
|
||||||
InstallableFlake(
|
|
||||||
SourceExprCommand * cmd,
|
|
||||||
ref<EvalState> state,
|
|
||||||
FlakeRef && flakeRef,
|
|
||||||
std::string_view fragment,
|
|
||||||
ExtendedOutputsSpec extendedOutputsSpec,
|
|
||||||
Strings attrPaths,
|
|
||||||
Strings prefixes,
|
|
||||||
const flake::LockFlags & lockFlags);
|
|
||||||
|
|
||||||
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
|
||||||
|
|
||||||
std::vector<std::string> getActualAttrPaths();
|
|
||||||
|
|
||||||
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
|
|
||||||
|
|
||||||
DerivedPathsWithInfo toDerivedPaths() override;
|
|
||||||
|
|
||||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
|
||||||
|
|
||||||
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
|
||||||
exists. */
|
|
||||||
std::vector<ref<eval_cache::AttrCursor>>
|
|
||||||
getCursors(EvalState & state) override;
|
|
||||||
|
|
||||||
/* Get a cursor to the first attrpath in getActualAttrPaths() that
|
|
||||||
exists, or throw an exception with suggestions if none exists. */
|
|
||||||
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
|
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
|
||||||
|
|
||||||
FlakeRef nixpkgsFlakeRef() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
ref<eval_cache::EvalCache> openEvalCache(
|
|
||||||
EvalState & state,
|
|
||||||
std::shared_ptr<flake::LockedFlake> lockedFlake);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ libcmd_DIR := $(d)
|
||||||
|
|
||||||
libcmd_SOURCES := $(wildcard $(d)/*.cc)
|
libcmd_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers -I src/nix
|
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
|
||||||
|
|
||||||
libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread
|
libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixcmd
|
Libs: -L${libdir} -lnixcmd
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -19,6 +19,8 @@ extern "C" {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "repl.hh"
|
||||||
|
|
||||||
#include "ansicolor.hh"
|
#include "ansicolor.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
@ -31,7 +33,9 @@ extern "C" {
|
||||||
#include "get-drvs.hh"
|
#include "get-drvs.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "command.hh"
|
#include "flake/flake.hh"
|
||||||
|
#include "flake/lockfile.hh"
|
||||||
|
#include "editor-for.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "markdown.hh"
|
#include "markdown.hh"
|
||||||
#include "local-fs-store.hh"
|
#include "local-fs-store.hh"
|
||||||
|
@ -45,18 +49,16 @@ extern "C" {
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct NixRepl
|
struct NixRepl
|
||||||
|
: AbstractNixRepl
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
: gc
|
, gc
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
std::string curDir;
|
std::string curDir;
|
||||||
ref<EvalState> state;
|
|
||||||
Bindings * autoArgs;
|
|
||||||
|
|
||||||
size_t debugTraceIndex;
|
size_t debugTraceIndex;
|
||||||
|
|
||||||
Strings loadedFiles;
|
Strings loadedFiles;
|
||||||
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
|
||||||
std::function<AnnotatedValues()> getValues;
|
std::function<AnnotatedValues()> getValues;
|
||||||
|
|
||||||
const static int envSize = 32768;
|
const static int envSize = 32768;
|
||||||
|
@ -69,8 +71,11 @@ struct NixRepl
|
||||||
|
|
||||||
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
||||||
std::function<AnnotatedValues()> getValues);
|
std::function<AnnotatedValues()> getValues);
|
||||||
~NixRepl();
|
virtual ~NixRepl();
|
||||||
void mainLoop();
|
|
||||||
|
void mainLoop() override;
|
||||||
|
void initEnv() override;
|
||||||
|
|
||||||
StringSet completePrefix(const std::string & prefix);
|
StringSet completePrefix(const std::string & prefix);
|
||||||
bool getLine(std::string & input, const std::string & prompt);
|
bool getLine(std::string & input, const std::string & prompt);
|
||||||
StorePath getDerivationPath(Value & v);
|
StorePath getDerivationPath(Value & v);
|
||||||
|
@ -78,7 +83,6 @@ struct NixRepl
|
||||||
|
|
||||||
void loadFile(const Path & path);
|
void loadFile(const Path & path);
|
||||||
void loadFlake(const std::string & flakeRef);
|
void loadFlake(const std::string & flakeRef);
|
||||||
void initEnv();
|
|
||||||
void loadFiles();
|
void loadFiles();
|
||||||
void reloadFiles();
|
void reloadFiles();
|
||||||
void addAttrsToScope(Value & attrs);
|
void addAttrsToScope(Value & attrs);
|
||||||
|
@ -92,7 +96,6 @@ struct NixRepl
|
||||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
std::string removeWhitespace(std::string s)
|
std::string removeWhitespace(std::string s)
|
||||||
{
|
{
|
||||||
s = chomp(s);
|
s = chomp(s);
|
||||||
|
@ -104,7 +107,7 @@ std::string removeWhitespace(std::string s)
|
||||||
|
|
||||||
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
std::function<NixRepl::AnnotatedValues()> getValues)
|
std::function<NixRepl::AnnotatedValues()> getValues)
|
||||||
: state(state)
|
: AbstractNixRepl(state)
|
||||||
, debugTraceIndex(0)
|
, debugTraceIndex(0)
|
||||||
, getValues(getValues)
|
, getValues(getValues)
|
||||||
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
|
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
|
||||||
|
@ -1029,8 +1032,22 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void runRepl(
|
|
||||||
ref<EvalState>evalState,
|
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||||
|
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
|
std::function<AnnotatedValues()> getValues)
|
||||||
|
{
|
||||||
|
return std::make_unique<NixRepl>(
|
||||||
|
searchPath,
|
||||||
|
openStore(),
|
||||||
|
state,
|
||||||
|
getValues
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AbstractNixRepl::runSimple(
|
||||||
|
ref<EvalState> evalState,
|
||||||
const ValMap & extraEnv)
|
const ValMap & extraEnv)
|
||||||
{
|
{
|
||||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
||||||
|
@ -1054,91 +1071,4 @@ void runRepl(
|
||||||
repl->mainLoop();
|
repl->mainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CmdRepl : InstallablesCommand
|
|
||||||
{
|
|
||||||
CmdRepl() {
|
|
||||||
evalSettings.pureEval = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void prepare() override
|
|
||||||
{
|
|
||||||
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
|
|
||||||
warn("future versions of Nix will require using `--file` to load a file");
|
|
||||||
if (this->_installables.size() > 1)
|
|
||||||
warn("more than one input file is not currently supported");
|
|
||||||
auto filePath = this->_installables[0].data();
|
|
||||||
file = std::optional(filePath);
|
|
||||||
_installables.front() = _installables.back();
|
|
||||||
_installables.pop_back();
|
|
||||||
}
|
|
||||||
installables = InstallablesCommand::load();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> files;
|
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPaths() override
|
|
||||||
{
|
|
||||||
return {""};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool useDefaultInstallables() override
|
|
||||||
{
|
|
||||||
return file.has_value() or expr.has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool forceImpureByDefault() override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string description() override
|
|
||||||
{
|
|
||||||
return "start an interactive environment for evaluating Nix expressions";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string doc() override
|
|
||||||
{
|
|
||||||
return
|
|
||||||
#include "repl.md"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
|
||||||
{
|
|
||||||
auto state = getEvalState();
|
|
||||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
|
||||||
auto installables = load();
|
|
||||||
NixRepl::AnnotatedValues values;
|
|
||||||
for (auto & installable: installables){
|
|
||||||
auto what = installable->what();
|
|
||||||
if (file){
|
|
||||||
auto [val, pos] = installable->toValue(*state);
|
|
||||||
auto what = installable->what();
|
|
||||||
state->forceValue(*val, pos);
|
|
||||||
auto autoArgs = getAutoArgs(*state);
|
|
||||||
auto valPost = state->allocValue();
|
|
||||||
state->autoCallFunction(*autoArgs, *val, *valPost);
|
|
||||||
state->forceValue(*valPost, pos);
|
|
||||||
values.push_back( {valPost, what });
|
|
||||||
} else {
|
|
||||||
auto [val, pos] = installable->toValue(*state);
|
|
||||||
values.push_back( {val, what} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
};
|
|
||||||
auto repl = std::make_unique<NixRepl>(
|
|
||||||
searchPath,
|
|
||||||
openStore(),
|
|
||||||
state,
|
|
||||||
getValues
|
|
||||||
);
|
|
||||||
repl->autoArgs = getAutoArgs(*repl->state);
|
|
||||||
repl->initEnv();
|
|
||||||
repl->mainLoop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto rCmdRepl = registerCommand<CmdRepl>("repl");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
39
src/libcmd/repl.hh
Normal file
39
src/libcmd/repl.hh
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "eval.hh"
|
||||||
|
|
||||||
|
#if HAVE_BOEHMGC
|
||||||
|
#define GC_INCLUDE_NEW
|
||||||
|
#include <gc/gc_cpp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct AbstractNixRepl
|
||||||
|
{
|
||||||
|
ref<EvalState> state;
|
||||||
|
Bindings * autoArgs;
|
||||||
|
|
||||||
|
AbstractNixRepl(ref<EvalState> state)
|
||||||
|
: state(state)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual ~AbstractNixRepl()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
||||||
|
|
||||||
|
static std::unique_ptr<AbstractNixRepl> create(
|
||||||
|
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||||
|
std::function<AnnotatedValues()> getValues);
|
||||||
|
|
||||||
|
static void runSimple(
|
||||||
|
ref<EvalState> evalState,
|
||||||
|
const ValMap & extraEnv);
|
||||||
|
|
||||||
|
virtual void initEnv() = 0;
|
||||||
|
|
||||||
|
virtual void mainLoop() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -2490,7 +2490,7 @@ Strings EvalSettings::getDefaultNixPath()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!evalSettings.restrictEval && !evalSettings.pureEval) {
|
if (!evalSettings.restrictEval && !evalSettings.pureEval) {
|
||||||
add(getHome() + "/.nix-defexpr/channels");
|
add(settings.useXDGBaseDirectories ? getStateDir() + "/nix/defexpr/channels" : getHome() + "/.nix-defexpr/channels");
|
||||||
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
|
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
|
||||||
add(settings.nixStateDir + "/profiles/per-user/root/channels");
|
add(settings.nixStateDir + "/profiles/per-user/root/channels");
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,9 @@ let
|
||||||
|
|
||||||
subdir = if key == lockFile.root then rootSubdir else node.locked.dir or "";
|
subdir = if key == lockFile.root then rootSubdir else node.locked.dir or "";
|
||||||
|
|
||||||
flake = import (sourceInfo + (if subdir != "" then "/" else "") + subdir + "/flake.nix");
|
outPath = sourceInfo + ((if subdir == "" then "" else "/") + subdir);
|
||||||
|
|
||||||
|
flake = import (outPath + "/flake.nix");
|
||||||
|
|
||||||
inputs = builtins.mapAttrs
|
inputs = builtins.mapAttrs
|
||||||
(inputName: inputSpec: allNodes.${resolveInput inputSpec})
|
(inputName: inputSpec: allNodes.${resolveInput inputSpec})
|
||||||
|
@ -43,7 +45,21 @@ let
|
||||||
|
|
||||||
outputs = flake.outputs (inputs // { self = result; });
|
outputs = flake.outputs (inputs // { self = result; });
|
||||||
|
|
||||||
result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake"; };
|
result =
|
||||||
|
outputs
|
||||||
|
# We add the sourceInfo attribute for its metadata, as they are
|
||||||
|
# relevant metadata for the flake. However, the outPath of the
|
||||||
|
# sourceInfo does not necessarily match the outPath of the flake,
|
||||||
|
# as the flake may be in a subdirectory of a source.
|
||||||
|
# This is shadowed in the next //
|
||||||
|
// sourceInfo
|
||||||
|
// {
|
||||||
|
# This shadows the sourceInfo.outPath
|
||||||
|
inherit outPath;
|
||||||
|
|
||||||
|
inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake";
|
||||||
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
if node.flake or true then
|
if node.flake or true then
|
||||||
assert builtins.isFunction flake.outputs;
|
assert builtins.isFunction flake.outputs;
|
||||||
|
|
|
@ -219,7 +219,7 @@ std::optional<FlakeRef> LockFile::isUnlocked() const
|
||||||
visit(root);
|
visit(root);
|
||||||
|
|
||||||
for (auto & i : nodes) {
|
for (auto & i : nodes) {
|
||||||
if (i == root) continue;
|
if (i == ref<const Node>(root)) continue;
|
||||||
auto node = i.dynamic_pointer_cast<const LockedNode>();
|
auto node = i.dynamic_pointer_cast<const LockedNode>();
|
||||||
if (node && !node->lockedRef.input.isLocked())
|
if (node && !node->lockedRef.input.isLocked())
|
||||||
return node->lockedRef;
|
return node->lockedRef;
|
||||||
|
|
|
@ -7,4 +7,4 @@ Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Requires: nix-store bdw-gc
|
Requires: nix-store bdw-gc
|
||||||
Libs: -L${libdir} -lnixexpr
|
Libs: -L${libdir} -lnixexpr
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -186,7 +186,7 @@ struct ExprString : Expr
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
Value v;
|
Value v;
|
||||||
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -233,7 +233,7 @@ struct ExprSelect : Expr
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Expr * e, * def;
|
Expr * e, * def;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
ExprSelect(const PosIdx & pos, Expr * e, 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)); };
|
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; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
|
@ -243,7 +243,7 @@ struct ExprOpHasAttr : Expr
|
||||||
{
|
{
|
||||||
Expr * e;
|
Expr * e;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
ExprOpHasAttr(Expr * e, const AttrPath && attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
||||||
PosIdx getPos() const override { return e->getPos(); }
|
PosIdx getPos() const override { return e->getPos(); }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,7 +90,7 @@ static void dupAttr(const EvalState & state, Symbol attr, const PosIdx pos, cons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
static void addAttr(ExprAttrs * attrs, AttrPath && attrPath,
|
||||||
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
||||||
{
|
{
|
||||||
AttrPath::iterator i;
|
AttrPath::iterator i;
|
||||||
|
@ -188,7 +188,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
|
|
||||||
|
|
||||||
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> & es)
|
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString("");
|
if (es.empty()) return new ExprString("");
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||||
s2 = std::string(s2, 0, p + 1);
|
s2 = std::string(s2, 0, p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
es2->emplace_back(i->first, new ExprString(s2));
|
es2->emplace_back(i->first, new ExprString(std::move(s2)));
|
||||||
};
|
};
|
||||||
for (; i != es.end(); ++i, --n) {
|
for (; i != es.end(); ++i, --n) {
|
||||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||||
|
@ -408,7 +408,7 @@ expr_op
|
||||||
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||||
|
@ -431,14 +431,14 @@ expr_app
|
||||||
|
|
||||||
expr_select
|
expr_select
|
||||||
: expr_simple '.' attrpath
|
: expr_simple '.' attrpath
|
||||||
{ $$ = new ExprSelect(CUR_POS, $1, *$3, 0); }
|
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), nullptr); delete $3; }
|
||||||
| expr_simple '.' attrpath OR_KW expr_select
|
| expr_simple '.' attrpath OR_KW expr_select
|
||||||
{ $$ = new ExprSelect(CUR_POS, $1, *$3, $5); }
|
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), $5); delete $3; }
|
||||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||||
expr_simple OR_KW
|
expr_simple OR_KW
|
||||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
|
||||||
| expr_simple { $$ = $1; }
|
| expr_simple
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_simple
|
expr_simple
|
||||||
|
@ -453,9 +453,10 @@ expr_simple
|
||||||
| FLOAT { $$ = new ExprFloat($1); }
|
| FLOAT { $$ = new ExprFloat($1); }
|
||||||
| '"' string_parts '"' { $$ = $2; }
|
| '"' string_parts '"' { $$ = $2; }
|
||||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||||
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
|
$$ = stripIndentation(CUR_POS, data->symbols, std::move(*$2));
|
||||||
|
delete $2;
|
||||||
}
|
}
|
||||||
| path_start PATH_END { $$ = $1; }
|
| path_start PATH_END
|
||||||
| path_start string_parts_interpolated PATH_END {
|
| path_start string_parts_interpolated PATH_END {
|
||||||
$2->insert($2->begin(), {makeCurPos(@1, data), $1});
|
$2->insert($2->begin(), {makeCurPos(@1, data), $1});
|
||||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
||||||
|
@ -465,7 +466,7 @@ expr_simple
|
||||||
$$ = new ExprCall(CUR_POS,
|
$$ = new ExprCall(CUR_POS,
|
||||||
new ExprVar(data->symbols.create("__findFile")),
|
new ExprVar(data->symbols.create("__findFile")),
|
||||||
{new ExprVar(data->symbols.create("__nixPath")),
|
{new ExprVar(data->symbols.create("__nixPath")),
|
||||||
new ExprString(path)});
|
new ExprString(std::move(path))});
|
||||||
}
|
}
|
||||||
| URI {
|
| URI {
|
||||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
||||||
|
@ -533,7 +534,7 @@ ind_string_parts
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, std::move(*$2), $4, makeCurPos(@2, data), data->state); delete $2; }
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
for (auto & i : *$3) {
|
for (auto & i : *$3) {
|
||||||
|
@ -542,6 +543,7 @@ binds
|
||||||
auto pos = makeCurPos(@3, data);
|
auto pos = makeCurPos(@3, data);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||||
}
|
}
|
||||||
|
delete $3;
|
||||||
}
|
}
|
||||||
| binds INHERIT '(' expr ')' attrs ';'
|
| binds INHERIT '(' expr ')' attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
|
@ -551,6 +553,7 @@ binds
|
||||||
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
||||||
}
|
}
|
||||||
|
delete $6;
|
||||||
}
|
}
|
||||||
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
|
||||||
;
|
;
|
||||||
|
@ -596,7 +599,7 @@ attrpath
|
||||||
;
|
;
|
||||||
|
|
||||||
attr
|
attr
|
||||||
: ID { $$ = $1; }
|
: ID
|
||||||
| OR_KW { $$ = {"or", 2}; }
|
| OR_KW { $$ = {"or", 2}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -612,9 +615,9 @@ expr_list
|
||||||
|
|
||||||
formals
|
formals
|
||||||
: formal ',' formals
|
: formal ',' formals
|
||||||
{ $$ = $3; $$->formals.push_back(*$1); }
|
{ $$ = $3; $$->formals.emplace_back(*$1); delete $1; }
|
||||||
| formal
|
| formal
|
||||||
{ $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->formals.emplace_back(*$1); $$->ellipsis = false; delete $1; }
|
||||||
|
|
|
|
||||||
{ $$ = new ParserFormals; $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->ellipsis = false; }
|
||||||
| ELLIPSIS
|
| ELLIPSIS
|
||||||
|
|
|
@ -2093,7 +2093,7 @@ static void addPath(
|
||||||
std::optional<StorePath> expectedStorePath;
|
std::optional<StorePath> expectedStorePath;
|
||||||
if (expectedHash)
|
if (expectedHash)
|
||||||
expectedStorePath = state.store->makeFixedOutputPath(name, FixedOutputInfo {
|
expectedStorePath = state.store->makeFixedOutputPath(name, FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = method,
|
.method = method,
|
||||||
.hash = *expectedHash,
|
.hash = *expectedHash,
|
||||||
},
|
},
|
||||||
|
@ -3038,9 +3038,9 @@ static RegisterPrimOp primop_foldlStrict({
|
||||||
.doc = R"(
|
.doc = R"(
|
||||||
Reduce a list by applying a binary operator, from left to right,
|
Reduce a list by applying a binary operator, from left to right,
|
||||||
e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2)
|
e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2)
|
||||||
...`. The operator is applied strictly, i.e., its arguments are
|
...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.
|
||||||
evaluated first. For example, `foldl' (x: y: x + y) 0 [1 2 3]`
|
The return value of each application of `op` is evaluated immediately,
|
||||||
evaluates to 6.
|
even for intermediate values.
|
||||||
)",
|
)",
|
||||||
.fun = prim_foldlStrict,
|
.fun = prim_foldlStrict,
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "fetchers.hh"
|
#include "fetchers.hh"
|
||||||
#include "filetransfer.hh"
|
#include "filetransfer.hh"
|
||||||
#include "registry.hh"
|
#include "registry.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
@ -68,7 +69,16 @@ void emitTreeAttrs(
|
||||||
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
|
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
|
||||||
{
|
{
|
||||||
state.checkURI(uri);
|
state.checkURI(uri);
|
||||||
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
|
if (uri.find("://") == std::string::npos) {
|
||||||
|
const auto p = ParsedURL {
|
||||||
|
.scheme = defaultScheme,
|
||||||
|
.authority = "",
|
||||||
|
.path = uri
|
||||||
|
};
|
||||||
|
return p.to_string();
|
||||||
|
} else {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixURIForGit(std::string uri, EvalState & state)
|
std::string fixURIForGit(std::string uri, EvalState & state)
|
||||||
|
@ -236,7 +246,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||||
auto expectedPath = state.store->makeFixedOutputPath(
|
auto expectedPath = state.store->makeFixedOutputPath(
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat,
|
.method = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat,
|
||||||
.hash = *expectedHash,
|
.hash = *expectedHash,
|
||||||
},
|
},
|
||||||
|
@ -461,6 +471,17 @@ static RegisterPrimOp primop_fetchGit({
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> This behavior is disabled in *Pure evaluation mode*.
|
> This behavior is disabled in *Pure evaluation mode*.
|
||||||
|
|
||||||
|
- To fetch the content of a checked-out work directory:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
builtins.fetchGit ./work-dir
|
||||||
|
```
|
||||||
|
|
||||||
|
If the URL points to a local directory, and no `ref` or `rev` is
|
||||||
|
given, `fetchGit` will use the current content of the checked-out
|
||||||
|
files, even if they are not committed or added to Git's index. It will
|
||||||
|
only consider files added to the Git repository, as listed by `git ls-files`.
|
||||||
)",
|
)",
|
||||||
.fun = prim_fetchGit,
|
.fun = prim_fetchGit,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "libexprtests.hh"
|
#include "tests/libexpr.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "libexprtests.hh"
|
#include "tests/libexpr.hh"
|
||||||
#include "value-to-json.hh"
|
#include "value-to-json.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
|
@ -7,18 +7,19 @@
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
|
||||||
|
#include "tests/libstore.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
class LibExprTest : public ::testing::Test {
|
class LibExprTest : public LibStoreTest {
|
||||||
public:
|
public:
|
||||||
static void SetUpTestSuite() {
|
static void SetUpTestSuite() {
|
||||||
initLibStore();
|
LibStoreTest::SetUpTestSuite();
|
||||||
initGC();
|
initGC();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LibExprTest()
|
LibExprTest()
|
||||||
: store(openStore("dummy://"))
|
: LibStoreTest()
|
||||||
, state({}, store)
|
, state({}, store)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -36,7 +37,6 @@ namespace nix {
|
||||||
return state.symbols.create(value);
|
return state.symbols.create(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<Store> store;
|
|
||||||
EvalState state;
|
EvalState state;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,8 @@ check: libexpr-tests_RUN
|
||||||
|
|
||||||
programs += libexpr-tests
|
programs += libexpr-tests
|
||||||
|
|
||||||
|
libexpr-tests_NAME := libnixexpr-tests
|
||||||
|
|
||||||
libexpr-tests_DIR := $(d)
|
libexpr-tests_DIR := $(d)
|
||||||
|
|
||||||
libexpr-tests_INSTALL_DIR :=
|
libexpr-tests_INSTALL_DIR :=
|
||||||
|
@ -12,6 +14,6 @@ libexpr-tests_SOURCES := \
|
||||||
|
|
||||||
libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/libexpr/tests
|
libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/libexpr/tests
|
||||||
|
|
||||||
libexpr-tests_LIBS = libexpr libutil libstore libfetchers
|
libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers
|
||||||
|
|
||||||
libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock
|
libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "libexprtests.hh"
|
#include "tests/libexpr.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
class CaptureLogger : public Logger
|
class CaptureLogger : public Logger
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "libexprtests.hh"
|
#include "tests/libexpr.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
// Testing of trivial expressions
|
// Testing of trivial expressions
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#include "value/context.hh"
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <rapidcheck/gtest.h>
|
||||||
|
|
||||||
#include "libexprtests.hh"
|
#include "tests/path.hh"
|
||||||
|
#include "tests/libexpr.hh"
|
||||||
|
#include "tests/value/context.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -70,3 +74,54 @@ TEST_F(NixStringContextElemTest, built) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace rc {
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
Gen<NixStringContextElem::Opaque> Arbitrary<NixStringContextElem::Opaque>::arbitrary()
|
||||||
|
{
|
||||||
|
return gen::just(NixStringContextElem::Opaque {
|
||||||
|
.path = *gen::arbitrary<StorePath>(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arbitrary()
|
||||||
|
{
|
||||||
|
return gen::just(NixStringContextElem::DrvDeep {
|
||||||
|
.drvPath = *gen::arbitrary<StorePath>(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Gen<NixStringContextElem::Built> Arbitrary<NixStringContextElem::Built>::arbitrary()
|
||||||
|
{
|
||||||
|
return gen::just(NixStringContextElem::Built {
|
||||||
|
.drvPath = *gen::arbitrary<StorePath>(),
|
||||||
|
.output = (*gen::arbitrary<StorePathName>()).name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
|
||||||
|
{
|
||||||
|
switch (*gen::inRange<uint8_t>(0, 2)) {
|
||||||
|
case 0:
|
||||||
|
return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Opaque>());
|
||||||
|
case 1:
|
||||||
|
return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::DrvDeep>());
|
||||||
|
default:
|
||||||
|
return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Built>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
RC_GTEST_FIXTURE_PROP(
|
||||||
|
NixStringContextElemTest,
|
||||||
|
prop_round_rip,
|
||||||
|
(const NixStringContextElem & o))
|
||||||
|
{
|
||||||
|
RC_ASSERT(o == NixStringContextElem::parse(store(), o.to_string(store())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
30
src/libexpr/tests/value/context.hh
Normal file
30
src/libexpr/tests/value/context.hh
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <rapidcheck/gen/Arbitrary.h>
|
||||||
|
|
||||||
|
#include <value/context.hh>
|
||||||
|
|
||||||
|
namespace rc {
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Arbitrary<NixStringContextElem::Opaque> {
|
||||||
|
static Gen<NixStringContextElem::Opaque> arbitrary();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Arbitrary<NixStringContextElem::Built> {
|
||||||
|
static Gen<NixStringContextElem::Built> arbitrary();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Arbitrary<NixStringContextElem::DrvDeep> {
|
||||||
|
static Gen<NixStringContextElem::DrvDeep> arbitrary();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Arbitrary<NixStringContextElem> {
|
||||||
|
static Gen<NixStringContextElem> arbitrary();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "comparator.hh"
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
|
|
||||||
#include <optional>
|
#include <variant>
|
||||||
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
|
@ -32,6 +33,8 @@ class Store;
|
||||||
*/
|
*/
|
||||||
struct NixStringContextElem_Opaque {
|
struct NixStringContextElem_Opaque {
|
||||||
StorePath path;
|
StorePath path;
|
||||||
|
|
||||||
|
GENERATE_CMP(NixStringContextElem_Opaque, me->path);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Path to a derivation and its entire build closure.
|
/* Path to a derivation and its entire build closure.
|
||||||
|
@ -44,6 +47,8 @@ struct NixStringContextElem_Opaque {
|
||||||
*/
|
*/
|
||||||
struct NixStringContextElem_DrvDeep {
|
struct NixStringContextElem_DrvDeep {
|
||||||
StorePath drvPath;
|
StorePath drvPath;
|
||||||
|
|
||||||
|
GENERATE_CMP(NixStringContextElem_DrvDeep, me->drvPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Derivation output.
|
/* Derivation output.
|
||||||
|
@ -53,6 +58,8 @@ struct NixStringContextElem_DrvDeep {
|
||||||
struct NixStringContextElem_Built {
|
struct NixStringContextElem_Built {
|
||||||
StorePath drvPath;
|
StorePath drvPath;
|
||||||
std::string output;
|
std::string output;
|
||||||
|
|
||||||
|
GENERATE_CMP(NixStringContextElem_Built, me->drvPath, me->output);
|
||||||
};
|
};
|
||||||
|
|
||||||
using _NixStringContextElem_Raw = std::variant<
|
using _NixStringContextElem_Raw = std::variant<
|
||||||
|
|
|
@ -57,7 +57,7 @@ struct FetchSettings : public Config
|
||||||
```
|
```
|
||||||
|
|
||||||
This example specifies three tokens, one each for accessing
|
This example specifies three tokens, one each for accessing
|
||||||
github.com, gitlab.mycompany.com, and sourceforge.net.
|
github.com, gitlab.mycompany.com, and gitlab.com.
|
||||||
|
|
||||||
The `input.foo` uses the "gitlab" fetcher, which might
|
The `input.foo` uses the "gitlab" fetcher, which might
|
||||||
requires specifying the token type along with the token
|
requires specifying the token type along with the token
|
||||||
|
|
|
@ -211,7 +211,7 @@ StorePath Input::computeStorePath(Store & store) const
|
||||||
if (!narHash)
|
if (!narHash)
|
||||||
throw Error("cannot compute store path for unlocked input '%s'", to_string());
|
throw Error("cannot compute store path for unlocked input '%s'", to_string());
|
||||||
return store.makeFixedOutputPath(getName(), FixedOutputInfo {
|
return store.makeFixedOutputPath(getName(), FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = FileIngestionMethod::Recursive,
|
.method = FileIngestionMethod::Recursive,
|
||||||
.hash = *narHash,
|
.hash = *narHash,
|
||||||
},
|
},
|
||||||
|
|
|
@ -615,15 +615,42 @@ struct GitInputScheme : InputScheme
|
||||||
AutoDelete delTmpGitDir(tmpGitDir, true);
|
AutoDelete delTmpGitDir(tmpGitDir, true);
|
||||||
|
|
||||||
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
|
||||||
|
|
||||||
|
{
|
||||||
// TODO: repoDir might lack the ref (it only checks if rev
|
// TODO: repoDir might lack the ref (it only checks if rev
|
||||||
// exists, see FIXME above) so use a big hammer and fetch
|
// exists, see FIXME above) so use a big hammer and fetch
|
||||||
// everything to ensure we get the rev.
|
// everything to ensure we get the rev.
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("making temporary clone of '%s'", repoDir));
|
||||||
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
|
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
|
||||||
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
|
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
|
||||||
|
}
|
||||||
|
|
||||||
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", input.getRev()->gitRev() });
|
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", input.getRev()->gitRev() });
|
||||||
runProgram("git", true, { "-C", tmpDir, "remote", "add", "origin", actualUrl });
|
|
||||||
|
/* Ensure that we use the correct origin for fetching
|
||||||
|
submodules. This matters for submodules with relative
|
||||||
|
URLs. */
|
||||||
|
if (isLocal) {
|
||||||
|
writeFile(tmpGitDir + "/config", readFile(repoDir + "/" + gitDir + "/config"));
|
||||||
|
|
||||||
|
/* Restore the config.bare setting we may have just
|
||||||
|
copied erroneously from the user's repo. */
|
||||||
|
runProgram("git", true, { "-C", tmpDir, "config", "core.bare", "false" });
|
||||||
|
} else
|
||||||
|
runProgram("git", true, { "-C", tmpDir, "config", "remote.origin.url", actualUrl });
|
||||||
|
|
||||||
|
/* As an optimisation, copy the modules directory of the
|
||||||
|
source repo if it exists. */
|
||||||
|
auto modulesPath = repoDir + "/" + gitDir + "/modules";
|
||||||
|
if (pathExists(modulesPath)) {
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying submodules of '%s'", actualUrl));
|
||||||
|
runProgram("cp", true, { "-R", "--", modulesPath, tmpGitDir + "/modules" });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching submodules of '%s'", actualUrl));
|
||||||
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
|
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
|
||||||
|
}
|
||||||
|
|
||||||
filter = isNotDotGitDirectory;
|
filter = isNotDotGitDirectory;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -74,7 +74,7 @@ DownloadFileResult downloadFile(
|
||||||
*store,
|
*store,
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = FileIngestionMethod::Flat,
|
.method = FileIngestionMethod::Flat,
|
||||||
.hash = hash,
|
.hash = hash,
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixmain
|
Libs: -L${libdir} -lnixmain
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -309,7 +309,7 @@ StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, std::string_view n
|
||||||
*this,
|
*this,
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = method,
|
.method = method,
|
||||||
.hash = nar.first,
|
.hash = nar.first,
|
||||||
},
|
},
|
||||||
|
@ -380,7 +380,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
getFile(narInfoFile,
|
getFile(narInfoFile,
|
||||||
{[=](std::future<std::optional<std::string>> fut) {
|
{[=,this](std::future<std::optional<std::string>> fut) {
|
||||||
try {
|
try {
|
||||||
auto data = fut.get();
|
auto data = fut.get();
|
||||||
|
|
||||||
|
@ -427,7 +427,7 @@ StorePath BinaryCacheStore::addToStore(
|
||||||
*this,
|
*this,
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = method,
|
.method = method,
|
||||||
.hash = h,
|
.hash = h,
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "json-utils.hh"
|
#include "json-utils.hh"
|
||||||
#include "cgroup.hh"
|
#include "cgroup.hh"
|
||||||
#include "personality.hh"
|
#include "personality.hh"
|
||||||
|
#include "namespaces.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -167,7 +168,8 @@ void LocalDerivationGoal::killSandbox(bool getStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalDerivationGoal::tryLocalBuild() {
|
void LocalDerivationGoal::tryLocalBuild()
|
||||||
|
{
|
||||||
unsigned int curBuilds = worker.getNrLocalBuilds();
|
unsigned int curBuilds = worker.getNrLocalBuilds();
|
||||||
if (curBuilds >= settings.maxBuildJobs) {
|
if (curBuilds >= settings.maxBuildJobs) {
|
||||||
state = &DerivationGoal::tryToBuild;
|
state = &DerivationGoal::tryToBuild;
|
||||||
|
@ -205,6 +207,17 @@ void LocalDerivationGoal::tryLocalBuild() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if __linux__
|
||||||
|
if (useChroot) {
|
||||||
|
if (!mountAndPidNamespacesSupported()) {
|
||||||
|
if (!settings.sandboxFallback)
|
||||||
|
throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
|
||||||
|
debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
|
||||||
|
useChroot = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (useBuildUsers()) {
|
if (useBuildUsers()) {
|
||||||
if (!buildUser)
|
if (!buildUser)
|
||||||
buildUser = acquireUserLock(parsedDrv->useUidRange() ? 65536 : 1, useChroot);
|
buildUser = acquireUserLock(parsedDrv->useUidRange() ? 65536 : 1, useChroot);
|
||||||
|
@ -372,12 +385,6 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int childEntry(void * arg)
|
|
||||||
{
|
|
||||||
((LocalDerivationGoal *) arg)->runChild();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
static void linkOrCopy(const Path & from, const Path & to)
|
static void linkOrCopy(const Path & from, const Path & to)
|
||||||
{
|
{
|
||||||
|
@ -663,6 +670,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
nobody account. The latter is kind of a hack to support
|
nobody account. The latter is kind of a hack to support
|
||||||
Samba-in-QEMU. */
|
Samba-in-QEMU. */
|
||||||
createDirs(chrootRootDir + "/etc");
|
createDirs(chrootRootDir + "/etc");
|
||||||
|
if (parsedDrv->useUidRange())
|
||||||
chownToBuilder(chrootRootDir + "/etc");
|
chownToBuilder(chrootRootDir + "/etc");
|
||||||
|
|
||||||
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
|
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
|
||||||
|
@ -888,12 +896,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
userNamespaceSync.create();
|
userNamespaceSync.create();
|
||||||
|
|
||||||
Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
|
usingUserNamespace = userNamespacesSupported();
|
||||||
static bool userNamespacesEnabled =
|
|
||||||
pathExists(maxUserNamespaces)
|
|
||||||
&& trim(readFile(maxUserNamespaces)) != "0";
|
|
||||||
|
|
||||||
usingUserNamespace = userNamespacesEnabled;
|
|
||||||
|
|
||||||
Pid helper = startProcess([&]() {
|
Pid helper = startProcess([&]() {
|
||||||
|
|
||||||
|
@ -908,76 +911,21 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (getuid() == 0 && setgroups(0, 0) == -1)
|
if (getuid() == 0 && setgroups(0, 0) == -1)
|
||||||
throw SysError("setgroups failed");
|
throw SysError("setgroups failed");
|
||||||
|
|
||||||
size_t stackSize = 1 * 1024 * 1024;
|
ProcessOptions options;
|
||||||
char * stack = (char *) mmap(0, stackSize,
|
options.cloneFlags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
||||||
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
|
||||||
if (stack == MAP_FAILED) throw SysError("allocating stack");
|
|
||||||
|
|
||||||
int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
|
|
||||||
if (privateNetwork)
|
if (privateNetwork)
|
||||||
flags |= CLONE_NEWNET;
|
options.cloneFlags |= CLONE_NEWNET;
|
||||||
if (usingUserNamespace)
|
if (usingUserNamespace)
|
||||||
flags |= CLONE_NEWUSER;
|
options.cloneFlags |= CLONE_NEWUSER;
|
||||||
|
|
||||||
|
pid_t child = startProcess([&]() { runChild(); }, options);
|
||||||
|
|
||||||
pid_t child = clone(childEntry, stack + stackSize, flags, this);
|
|
||||||
if (child == -1 && errno == EINVAL) {
|
|
||||||
/* Fallback for Linux < 2.13 where CLONE_NEWPID and
|
|
||||||
CLONE_PARENT are not allowed together. */
|
|
||||||
flags &= ~CLONE_NEWPID;
|
|
||||||
child = clone(childEntry, stack + stackSize, flags, this);
|
|
||||||
}
|
|
||||||
if (usingUserNamespace && child == -1 && (errno == EPERM || errno == EINVAL)) {
|
|
||||||
/* Some distros patch Linux to not allow unprivileged
|
|
||||||
* user namespaces. If we get EPERM or EINVAL, try
|
|
||||||
* without CLONE_NEWUSER and see if that works.
|
|
||||||
* Details: https://salsa.debian.org/kernel-team/linux/-/commit/d98e00eda6bea437e39b9e80444eee84a32438a6
|
|
||||||
*/
|
|
||||||
usingUserNamespace = false;
|
|
||||||
flags &= ~CLONE_NEWUSER;
|
|
||||||
child = clone(childEntry, stack + stackSize, flags, this);
|
|
||||||
}
|
|
||||||
if (child == -1) {
|
|
||||||
switch(errno) {
|
|
||||||
case EPERM:
|
|
||||||
case EINVAL: {
|
|
||||||
int errno_ = errno;
|
|
||||||
if (!userNamespacesEnabled && errno==EPERM)
|
|
||||||
notice("user namespaces appear to be disabled; they are required for sandboxing; check /proc/sys/user/max_user_namespaces");
|
|
||||||
if (userNamespacesEnabled) {
|
|
||||||
Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
|
|
||||||
if (pathExists(procSysKernelUnprivilegedUsernsClone)
|
|
||||||
&& trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") {
|
|
||||||
notice("user namespaces appear to be disabled; they are required for sandboxing; check /proc/sys/kernel/unprivileged_userns_clone");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Path procSelfNsUser = "/proc/self/ns/user";
|
|
||||||
if (!pathExists(procSelfNsUser))
|
|
||||||
notice("/proc/self/ns/user does not exist; your kernel was likely built without CONFIG_USER_NS=y, which is required for sandboxing");
|
|
||||||
/* Otherwise exit with EPERM so we can handle this in the
|
|
||||||
parent. This is only done when sandbox-fallback is set
|
|
||||||
to true (the default). */
|
|
||||||
if (settings.sandboxFallback)
|
|
||||||
_exit(1);
|
|
||||||
/* Mention sandbox-fallback in the error message so the user
|
|
||||||
knows that having it disabled contributed to the
|
|
||||||
unrecoverability of this failure */
|
|
||||||
throw SysError(errno_, "creating sandboxed builder process using clone(), without sandbox-fallback");
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw SysError("creating sandboxed builder process using clone()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeFull(builderOut.writeSide.get(),
|
writeFull(builderOut.writeSide.get(),
|
||||||
fmt("%d %d\n", usingUserNamespace, child));
|
fmt("%d %d\n", usingUserNamespace, child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
int res = helper.wait();
|
if (helper.wait() != 0)
|
||||||
if (res != 0 && settings.sandboxFallback) {
|
|
||||||
useChroot = false;
|
|
||||||
initTmpDir();
|
|
||||||
goto fallback;
|
|
||||||
} else if (res != 0)
|
|
||||||
throw Error("unable to start build process");
|
throw Error("unable to start build process");
|
||||||
|
|
||||||
userNamespaceSync.readSide = -1;
|
userNamespaceSync.readSide = -1;
|
||||||
|
@ -1045,9 +993,6 @@ void LocalDerivationGoal::startBuilder()
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
#if __linux__
|
|
||||||
fallback:
|
|
||||||
#endif
|
|
||||||
pid = startProcess([&]() {
|
pid = startProcess([&]() {
|
||||||
runChild();
|
runChild();
|
||||||
});
|
});
|
||||||
|
@ -1516,8 +1461,7 @@ void LocalDerivationGoal::startDaemon()
|
||||||
FdSink to(remote.get());
|
FdSink to(remote.get());
|
||||||
try {
|
try {
|
||||||
daemon::processConnection(store, from, to,
|
daemon::processConnection(store, from, to,
|
||||||
daemon::NotTrusted, daemon::Recursive,
|
daemon::NotTrusted, daemon::Recursive);
|
||||||
[&](Store & store) { store.createUser("nobody", 65535); });
|
|
||||||
debug("terminated daemon connection");
|
debug("terminated daemon connection");
|
||||||
} catch (SysError &) {
|
} catch (SysError &) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
|
@ -1907,6 +1851,10 @@ void LocalDerivationGoal::runChild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make /etc unwritable */
|
||||||
|
if (!parsedDrv->useUidRange())
|
||||||
|
chmod_(chrootRootDir + "/etc", 0555);
|
||||||
|
|
||||||
/* Unshare this mount namespace. This is necessary because
|
/* Unshare this mount namespace. This is necessary because
|
||||||
pivot_root() below changes the root of the mount
|
pivot_root() below changes the root of the mount
|
||||||
namespace. This means that the call to setns() in
|
namespace. This means that the call to setns() in
|
||||||
|
@ -2323,11 +2271,28 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
buildUser ? std::optional(buildUser->getUIDRange()) : std::nullopt,
|
buildUser ? std::optional(buildUser->getUIDRange()) : std::nullopt,
|
||||||
inodesSeen);
|
inodesSeen);
|
||||||
|
|
||||||
|
bool discardReferences = false;
|
||||||
|
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
||||||
|
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
||||||
|
settings.requireExperimentalFeature(Xp::DiscardReferences);
|
||||||
|
if (auto output = get(*udr, outputName)) {
|
||||||
|
if (!output->is_boolean())
|
||||||
|
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
|
||||||
|
discardReferences = output->get<bool>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePathSet references;
|
||||||
|
if (discardReferences)
|
||||||
|
debug("discarding references of output '%s'", outputName);
|
||||||
|
else {
|
||||||
debug("scanning for references for output '%s' in temp location '%s'", outputName, actualPath);
|
debug("scanning for references for output '%s' in temp location '%s'", outputName, actualPath);
|
||||||
|
|
||||||
/* Pass blank Sink as we are not ready to hash data at this stage. */
|
/* Pass blank Sink as we are not ready to hash data at this stage. */
|
||||||
NullSink blank;
|
NullSink blank;
|
||||||
auto references = scanForReferences(blank, actualPath, referenceablePaths);
|
references = scanForReferences(blank, actualPath, referenceablePaths);
|
||||||
|
}
|
||||||
|
|
||||||
outputReferencesIfUnregistered.insert_or_assign(
|
outputReferencesIfUnregistered.insert_or_assign(
|
||||||
outputName,
|
outputName,
|
||||||
|
|
|
@ -276,7 +276,7 @@ void Worker::run(const Goals & _topGoals)
|
||||||
if (!children.empty() || !waitingForAWhile.empty())
|
if (!children.empty() || !waitingForAWhile.empty())
|
||||||
waitForInput();
|
waitForInput();
|
||||||
else {
|
else {
|
||||||
if (awake.empty() && 0 == settings.maxBuildJobs)
|
if (awake.empty() && 0U == settings.maxBuildJobs)
|
||||||
{
|
{
|
||||||
if (getMachines().empty())
|
if (getMachines().empty())
|
||||||
throw Error("unable to start any build; either increase '--max-jobs' "
|
throw Error("unable to start any build; either increase '--max-jobs' "
|
||||||
|
|
|
@ -17,8 +17,9 @@ std::string makeFileIngestionPrefix(FileIngestionMethod m)
|
||||||
return "";
|
return "";
|
||||||
case FileIngestionMethod::Recursive:
|
case FileIngestionMethod::Recursive:
|
||||||
return "r:";
|
return "r:";
|
||||||
|
default:
|
||||||
|
throw Error("impossible, caught both cases");
|
||||||
}
|
}
|
||||||
assert(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string makeContentAddressingPrefix(ContentAddressMethod m) {
|
std::string makeContentAddressingPrefix(ContentAddressMethod m) {
|
||||||
|
@ -168,13 +169,13 @@ ContentAddressWithReferences contentAddressFromMethodHashAndRefs(
|
||||||
if (refs.self)
|
if (refs.self)
|
||||||
throw UsageError("Cannot have a self reference with text hashing scheme");
|
throw UsageError("Cannot have a self reference with text hashing scheme");
|
||||||
return TextInfo {
|
return TextInfo {
|
||||||
{ .hash = std::move(hash) },
|
.hash = { .hash = std::move(hash) },
|
||||||
.references = std::move(refs.others),
|
.references = std::move(refs.others),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[&](FileIngestionMethod m2) -> ContentAddressWithReferences {
|
[&](FileIngestionMethod m2) -> ContentAddressWithReferences {
|
||||||
return FixedOutputInfo {
|
return FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = m2,
|
.method = m2,
|
||||||
.hash = std::move(hash),
|
.hash = std::move(hash),
|
||||||
},
|
},
|
||||||
|
@ -191,7 +192,7 @@ ContentAddressMethod getContentAddressMethod(const ContentAddressWithReferences
|
||||||
return TextHashMethod {};
|
return TextHashMethod {};
|
||||||
},
|
},
|
||||||
[](const FixedOutputInfo & fsh) -> ContentAddressMethod {
|
[](const FixedOutputInfo & fsh) -> ContentAddressMethod {
|
||||||
return fsh.method;
|
return fsh.hash.method;
|
||||||
},
|
},
|
||||||
}, ca);
|
}, ca);
|
||||||
}
|
}
|
||||||
|
@ -222,13 +223,13 @@ ContentAddressWithReferences caWithoutRefs(const ContentAddress & ca) {
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](const TextHash & h) -> ContentAddressWithReferences {
|
[&](const TextHash & h) -> ContentAddressWithReferences {
|
||||||
return TextInfo {
|
return TextInfo {
|
||||||
h,
|
.hash = h,
|
||||||
.references = {},
|
.references = {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[&](const FixedOutputHash & h) -> ContentAddressWithReferences {
|
[&](const FixedOutputHash & h) -> ContentAddressWithReferences {
|
||||||
return FixedOutputInfo {
|
return FixedOutputInfo {
|
||||||
h,
|
.hash = h,
|
||||||
.references = {},
|
.references = {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -239,10 +240,10 @@ Hash getContentAddressHash(const ContentAddressWithReferences & ca)
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[](const TextInfo & th) {
|
[](const TextInfo & th) {
|
||||||
return th.hash;
|
return th.hash.hash;
|
||||||
},
|
},
|
||||||
[](const FixedOutputInfo & fsh) {
|
[](const FixedOutputInfo & fsh) {
|
||||||
return fsh.hash;
|
return fsh.hash.hash;
|
||||||
},
|
},
|
||||||
}, ca);
|
}, ca);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,18 +118,20 @@ struct StoreReferences {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This matches the additional info that we need for makeTextPath
|
// This matches the additional info that we need for makeTextPath
|
||||||
struct TextInfo : TextHash {
|
struct TextInfo {
|
||||||
|
TextHash hash;
|
||||||
// References for the paths, self references disallowed
|
// References for the paths, self references disallowed
|
||||||
StorePathSet references;
|
StorePathSet references;
|
||||||
|
|
||||||
GENERATE_CMP(TextInfo, *(const TextHash *)me, me->references);
|
GENERATE_CMP(TextInfo, me->hash, me->references);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FixedOutputInfo : FixedOutputHash {
|
struct FixedOutputInfo {
|
||||||
|
FixedOutputHash hash;
|
||||||
// References for the paths
|
// References for the paths
|
||||||
StoreReferences references;
|
StoreReferences references;
|
||||||
|
|
||||||
GENERATE_CMP(FixedOutputInfo, *(const FixedOutputHash *)me, me->references);
|
GENERATE_CMP(FixedOutputInfo, me->hash, me->references);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::variant<
|
typedef std::variant<
|
||||||
|
|
|
@ -222,7 +222,8 @@ struct ClientSettings
|
||||||
else if (!hasSuffix(s, "/") && trusted.count(s + "/"))
|
else if (!hasSuffix(s, "/") && trusted.count(s + "/"))
|
||||||
subs.push_back(s + "/");
|
subs.push_back(s + "/");
|
||||||
else
|
else
|
||||||
warn("ignoring untrusted substituter '%s'", s);
|
warn("ignoring untrusted substituter '%s', you are not a trusted user.\n"
|
||||||
|
"Run `man nix.conf` for more information on the `substituters` configuration option.", s);
|
||||||
res = subs;
|
res = subs;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -235,6 +236,10 @@ struct ClientSettings
|
||||||
// the daemon, as that could cause some pretty weird stuff
|
// the daemon, as that could cause some pretty weird stuff
|
||||||
if (parseFeatures(tokenizeString<StringSet>(value)) != settings.experimentalFeatures.get())
|
if (parseFeatures(tokenizeString<StringSet>(value)) != settings.experimentalFeatures.get())
|
||||||
debug("Ignoring the client-specified experimental features");
|
debug("Ignoring the client-specified experimental features");
|
||||||
|
} else if (name == settings.pluginFiles.name) {
|
||||||
|
if (tokenizeString<Paths>(value) != settings.pluginFiles.get())
|
||||||
|
warn("Ignoring the client-specified plugin-files.\n"
|
||||||
|
"The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14.");
|
||||||
}
|
}
|
||||||
else if (trusted
|
else if (trusted
|
||||||
|| name == settings.buildTimeout.name
|
|| name == settings.buildTimeout.name
|
||||||
|
@ -528,7 +533,14 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
mode = (BuildMode) readInt(from);
|
mode = (BuildMode) readInt(from);
|
||||||
|
|
||||||
/* Repairing is not atomic, so disallowed for "untrusted"
|
/* Repairing is not atomic, so disallowed for "untrusted"
|
||||||
clients. */
|
clients.
|
||||||
|
|
||||||
|
FIXME: layer violation in this message: the daemon code (i.e.
|
||||||
|
this file) knows whether a client/connection is trusted, but it
|
||||||
|
does not how how the client was authenticated. The mechanism
|
||||||
|
need not be getting the UID of the other end of a Unix Domain
|
||||||
|
Socket.
|
||||||
|
*/
|
||||||
if (mode == bmRepair && !trusted)
|
if (mode == bmRepair && !trusted)
|
||||||
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
||||||
}
|
}
|
||||||
|
@ -545,7 +557,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
mode = (BuildMode) readInt(from);
|
mode = (BuildMode) readInt(from);
|
||||||
|
|
||||||
/* Repairing is not atomic, so disallowed for "untrusted"
|
/* Repairing is not atomic, so disallowed for "untrusted"
|
||||||
clients. */
|
clients.
|
||||||
|
|
||||||
|
FIXME: layer violation; see above. */
|
||||||
if (mode == bmRepair && !trusted)
|
if (mode == bmRepair && !trusted)
|
||||||
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
||||||
|
|
||||||
|
@ -984,8 +998,7 @@ void processConnection(
|
||||||
FdSource & from,
|
FdSource & from,
|
||||||
FdSink & to,
|
FdSink & to,
|
||||||
TrustedFlag trusted,
|
TrustedFlag trusted,
|
||||||
RecursiveFlag recursive,
|
RecursiveFlag recursive)
|
||||||
std::function<void(Store &)> authHook)
|
|
||||||
{
|
{
|
||||||
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
||||||
|
|
||||||
|
@ -1028,10 +1041,6 @@ void processConnection(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
/* If we can't accept clientVersion, then throw an error
|
|
||||||
*here* (not above). */
|
|
||||||
authHook(*store);
|
|
||||||
|
|
||||||
tunnelLogger->stopWork();
|
tunnelLogger->stopWork();
|
||||||
to.flush();
|
to.flush();
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,6 @@ void processConnection(
|
||||||
FdSource & from,
|
FdSource & from,
|
||||||
FdSink & to,
|
FdSink & to,
|
||||||
TrustedFlag trusted,
|
TrustedFlag trusted,
|
||||||
RecursiveFlag recursive,
|
RecursiveFlag recursive);
|
||||||
/* Arbitrary hook to check authorization / initialize user data / whatever
|
|
||||||
after the protocol has been negotiated. The idea is that this function
|
|
||||||
and everything it calls doesn't know about this stuff, and the
|
|
||||||
`nix-daemon` handles that instead. */
|
|
||||||
std::function<void(Store &)> authHook);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
#include "fs-accessor.hh"
|
#include "fs-accessor.hh"
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -887,4 +888,64 @@ std::optional<BasicDerivation> Derivation::tryResolve(
|
||||||
|
|
||||||
const Hash impureOutputHash = hashString(htSHA256, "impure");
|
const Hash impureOutputHash = hashString(htSHA256, "impure");
|
||||||
|
|
||||||
|
nlohmann::json DerivationOutput::toJSON(
|
||||||
|
const Store & store, std::string_view drvName, std::string_view outputName) const
|
||||||
|
{
|
||||||
|
nlohmann::json res = nlohmann::json::object();
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const DerivationOutput::InputAddressed & doi) {
|
||||||
|
res["path"] = store.printStorePath(doi.path);
|
||||||
|
},
|
||||||
|
[&](const DerivationOutput::CAFixed & dof) {
|
||||||
|
res["path"] = store.printStorePath(dof.path(store, drvName, outputName));
|
||||||
|
res["hashAlgo"] = printMethodAlgo(dof.ca);
|
||||||
|
res["hash"] = getContentAddressHash(dof.ca).to_string(Base16, false);
|
||||||
|
// FIXME print refs?
|
||||||
|
},
|
||||||
|
[&](const DerivationOutput::CAFloating & dof) {
|
||||||
|
res["hashAlgo"] = makeContentAddressingPrefix(dof.method) + printHashType(dof.hashType);
|
||||||
|
},
|
||||||
|
[&](const DerivationOutput::Deferred &) {},
|
||||||
|
[&](const DerivationOutput::Impure & doi) {
|
||||||
|
res["hashAlgo"] = makeContentAddressingPrefix(doi.method) + printHashType(doi.hashType);
|
||||||
|
res["impure"] = true;
|
||||||
|
},
|
||||||
|
}, raw());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json Derivation::toJSON(const Store & store) const
|
||||||
|
{
|
||||||
|
nlohmann::json res = nlohmann::json::object();
|
||||||
|
|
||||||
|
{
|
||||||
|
nlohmann::json & outputsObj = res["outputs"];
|
||||||
|
outputsObj = nlohmann::json::object();
|
||||||
|
for (auto & [outputName, output] : outputs) {
|
||||||
|
outputsObj[outputName] = output.toJSON(store, name, outputName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& inputsList = res["inputSrcs"];
|
||||||
|
inputsList = nlohmann::json ::array();
|
||||||
|
for (auto & input : inputSrcs)
|
||||||
|
inputsList.emplace_back(store.printStorePath(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& inputDrvsObj = res["inputDrvs"];
|
||||||
|
inputDrvsObj = nlohmann::json ::object();
|
||||||
|
for (auto & input : inputDrvs)
|
||||||
|
inputDrvsObj[store.printStorePath(input.first)] = input.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
res["system"] = platform;
|
||||||
|
res["builder"] = builder;
|
||||||
|
res["args"] = args;
|
||||||
|
res["env"] = env;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,11 @@ struct DerivationOutput : _DerivationOutputRaw
|
||||||
inline const Raw & raw() const {
|
inline const Raw & raw() const {
|
||||||
return static_cast<const Raw &>(*this);
|
return static_cast<const Raw &>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json toJSON(
|
||||||
|
const Store & store,
|
||||||
|
std::string_view drvName,
|
||||||
|
std::string_view outputName) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
|
typedef std::map<std::string, DerivationOutput> DerivationOutputs;
|
||||||
|
@ -210,6 +215,8 @@ struct Derivation : BasicDerivation
|
||||||
Derivation() = default;
|
Derivation() = default;
|
||||||
Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
|
Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
|
||||||
Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { }
|
Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { }
|
||||||
|
|
||||||
|
nlohmann::json toJSON(const Store & store) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
|
#include "comparator.hh"
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
@ -28,8 +28,7 @@ struct DerivedPathOpaque {
|
||||||
std::string to_string(const Store & store) const;
|
std::string to_string(const Store & store) const;
|
||||||
static DerivedPathOpaque parse(const Store & store, std::string_view);
|
static DerivedPathOpaque parse(const Store & store, std::string_view);
|
||||||
|
|
||||||
bool operator < (const DerivedPathOpaque & b) const
|
GENERATE_CMP(DerivedPathOpaque, me->path);
|
||||||
{ return path < b.path; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,8 +51,7 @@ struct DerivedPathBuilt {
|
||||||
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
||||||
nlohmann::json toJSON(ref<Store> store) const;
|
nlohmann::json toJSON(ref<Store> store) const;
|
||||||
|
|
||||||
bool operator < (const DerivedPathBuilt & b) const
|
GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs);
|
||||||
{ return std::make_pair(drvPath, outputs) < std::make_pair(b.drvPath, b.outputs); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using _DerivedPathRaw = std::variant<
|
using _DerivedPathRaw = std::variant<
|
||||||
|
@ -97,6 +95,8 @@ struct BuiltPathBuilt {
|
||||||
|
|
||||||
nlohmann::json toJSON(ref<Store> store) const;
|
nlohmann::json toJSON(ref<Store> store) const;
|
||||||
static BuiltPathBuilt parse(const Store & store, std::string_view);
|
static BuiltPathBuilt parse(const Store & store, std::string_view);
|
||||||
|
|
||||||
|
GENERATE_CMP(BuiltPathBuilt, me->drvPath, me->outputs);
|
||||||
};
|
};
|
||||||
|
|
||||||
using _BuiltPathRaw = std::variant<
|
using _BuiltPathRaw = std::variant<
|
||||||
|
|
|
@ -101,6 +101,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
this->result.data.append(data);
|
this->result.data.append(data);
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
requestHeaders = curl_slist_append(requestHeaders, "Accept-Encoding: zstd, br, gzip, deflate, bzip2, xz");
|
||||||
if (!request.expectedETag.empty())
|
if (!request.expectedETag.empty())
|
||||||
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
|
||||||
if (!request.mimeType.empty())
|
if (!request.mimeType.empty())
|
||||||
|
@ -828,7 +829,7 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink)
|
||||||
{
|
{
|
||||||
auto state(_state->lock());
|
auto state(_state->lock());
|
||||||
|
|
||||||
while (state->data.empty()) {
|
if (state->data.empty()) {
|
||||||
|
|
||||||
if (state->quit) {
|
if (state->quit) {
|
||||||
if (state->exc) std::rethrow_exception(state->exc);
|
if (state->exc) std::rethrow_exception(state->exc);
|
||||||
|
@ -836,6 +837,8 @@ void FileTransfer::download(FileTransferRequest && request, Sink & sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.wait(state->avail);
|
state.wait(state->avail);
|
||||||
|
|
||||||
|
if (state->data.empty()) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = std::move(state->data);
|
chunk = std::move(state->data);
|
||||||
|
|
|
@ -222,19 +222,19 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
|
||||||
.longName = name,
|
.longName = name,
|
||||||
.description = "Enable sandboxing.",
|
.description = "Enable sandboxing.",
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(smEnabled); }}
|
.handler = {[this]() { override(smEnabled); }}
|
||||||
});
|
});
|
||||||
args.addFlag({
|
args.addFlag({
|
||||||
.longName = "no-" + name,
|
.longName = "no-" + name,
|
||||||
.description = "Disable sandboxing.",
|
.description = "Disable sandboxing.",
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(smDisabled); }}
|
.handler = {[this]() { override(smDisabled); }}
|
||||||
});
|
});
|
||||||
args.addFlag({
|
args.addFlag({
|
||||||
.longName = "relaxed-" + name,
|
.longName = "relaxed-" + name,
|
||||||
.description = "Enable sandboxing, but allow builds to disable it.",
|
.description = "Enable sandboxing, but allow builds to disable it.",
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[=]() { override(smRelaxed); }}
|
.handler = {[this]() { override(smRelaxed); }}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,16 @@ public:
|
||||||
{"build-timeout"}};
|
{"build-timeout"}};
|
||||||
|
|
||||||
PathSetting buildHook{this, true, "", "build-hook",
|
PathSetting buildHook{this, true, "", "build-hook",
|
||||||
"The path of the helper program that executes builds to remote machines."};
|
R"(
|
||||||
|
The path to the helper program that executes remote builds.
|
||||||
|
|
||||||
|
Nix communicates with the build hook over `stdio` using a custom protocol to request builds that cannot be performed directly by the Nix daemon.
|
||||||
|
The default value is the internal Nix binary that implements remote building.
|
||||||
|
|
||||||
|
> **Important**
|
||||||
|
>
|
||||||
|
> Change this setting only if you really know what you’re doing.
|
||||||
|
)"};
|
||||||
|
|
||||||
Setting<std::string> builders{
|
Setting<std::string> builders{
|
||||||
this, "@" + nixConfDir + "/machines", "builders",
|
this, "@" + nixConfDir + "/machines", "builders",
|
||||||
|
@ -279,8 +288,8 @@ public:
|
||||||
If the build users group is empty, builds will be performed under
|
If the build users group is empty, builds will be performed under
|
||||||
the uid of the Nix process (that is, the uid of the caller if
|
the uid of the Nix process (that is, the uid of the caller if
|
||||||
`NIX_REMOTE` is empty, the uid under which the Nix daemon runs if
|
`NIX_REMOTE` is empty, the uid under which the Nix daemon runs if
|
||||||
`NIX_REMOTE` is `daemon`). Obviously, this should not be used in
|
`NIX_REMOTE` is `daemon`). Obviously, this should not be used
|
||||||
multi-user settings with untrusted users.
|
with a nix daemon accessible to untrusted clients.
|
||||||
|
|
||||||
Defaults to `nixbld` when running as root, *empty* otherwise.
|
Defaults to `nixbld` when running as root, *empty* otherwise.
|
||||||
)",
|
)",
|
||||||
|
@ -570,11 +579,15 @@ public:
|
||||||
{"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="},
|
{"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="},
|
||||||
"trusted-public-keys",
|
"trusted-public-keys",
|
||||||
R"(
|
R"(
|
||||||
A whitespace-separated list of public keys. When paths are copied
|
A whitespace-separated list of public keys.
|
||||||
from another Nix store (such as a binary cache), they must be
|
|
||||||
signed with one of these keys. For example:
|
At least one of the following condition must be met
|
||||||
`cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
|
for Nix to accept copying a store object from another
|
||||||
hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=`.
|
Nix store (such as a substituter):
|
||||||
|
|
||||||
|
- the store object has been signed using a key in the trusted keys list
|
||||||
|
- the [`require-sigs`](#conf-require-sigs) option has been set to `false`
|
||||||
|
- the store object is [output-addressed](@docroot@/glossary.md#gloss-output-addressed-store-object)
|
||||||
)",
|
)",
|
||||||
{"binary-cache-public-keys"}};
|
{"binary-cache-public-keys"}};
|
||||||
|
|
||||||
|
@ -670,13 +683,14 @@ public:
|
||||||
independently. Lower value means higher priority.
|
independently. Lower value means higher priority.
|
||||||
The default is `https://cache.nixos.org`, with a Priority of 40.
|
The default is `https://cache.nixos.org`, with a Priority of 40.
|
||||||
|
|
||||||
Nix will copy a store path from a remote store only if one
|
At least one of the following conditions must be met for Nix to use
|
||||||
of the following is true:
|
a substituter:
|
||||||
|
|
||||||
- the store object is signed by one of the [`trusted-public-keys`](#conf-trusted-public-keys)
|
|
||||||
- the substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list
|
- the substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list
|
||||||
- the [`require-sigs`](#conf-require-sigs) option has been set to `false`
|
- the user calling Nix is in the [`trusted-users`](#conf-trusted-users) list
|
||||||
- the store object is [output-addressed](@docroot@/glossary.md#gloss-output-addressed-store-object)
|
|
||||||
|
In addition, each store path should be trusted as described
|
||||||
|
in [`trusted-public-keys`](#conf-trusted-public-keys)
|
||||||
)",
|
)",
|
||||||
{"binary-caches"}};
|
{"binary-caches"}};
|
||||||
|
|
||||||
|
@ -691,24 +705,6 @@ public:
|
||||||
)",
|
)",
|
||||||
{"trusted-binary-caches"}};
|
{"trusted-binary-caches"}};
|
||||||
|
|
||||||
Setting<Strings> trustedUsers{
|
|
||||||
this, {"root"}, "trusted-users",
|
|
||||||
R"(
|
|
||||||
A list of names of users (separated by whitespace) that have
|
|
||||||
additional rights when connecting to the Nix daemon, such as the
|
|
||||||
ability to specify additional binary caches, or to import unsigned
|
|
||||||
NARs. You can also specify groups by prefixing them with `@`; for
|
|
||||||
instance, `@wheel` means all users in the `wheel` group. The default
|
|
||||||
is `root`.
|
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
>
|
|
||||||
> Adding a user to `trusted-users` is essentially equivalent to
|
|
||||||
> giving that user root access to the system. For example, the user
|
|
||||||
> can set `sandbox-paths` and thereby obtain read access to
|
|
||||||
> directories that are otherwise inacessible to them.
|
|
||||||
)"};
|
|
||||||
|
|
||||||
Setting<unsigned int> ttlNegativeNarInfoCache{
|
Setting<unsigned int> ttlNegativeNarInfoCache{
|
||||||
this, 3600, "narinfo-cache-negative-ttl",
|
this, 3600, "narinfo-cache-negative-ttl",
|
||||||
R"(
|
R"(
|
||||||
|
@ -731,18 +727,6 @@ public:
|
||||||
mismatch if the build isn't reproducible.
|
mismatch if the build isn't reproducible.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
/* ?Who we trust to use the daemon in safe ways */
|
|
||||||
Setting<Strings> allowedUsers{
|
|
||||||
this, {"*"}, "allowed-users",
|
|
||||||
R"(
|
|
||||||
A list of names of users (separated by whitespace) that are allowed
|
|
||||||
to connect to the Nix daemon. As with the `trusted-users` option,
|
|
||||||
you can specify groups by prefixing them with `@`. Also, you can
|
|
||||||
allow all users by specifying `*`. The default is `*`.
|
|
||||||
|
|
||||||
Note that trusted users are always allowed to connect.
|
|
||||||
)"};
|
|
||||||
|
|
||||||
Setting<bool> printMissing{this, true, "print-missing",
|
Setting<bool> printMissing{this, true, "print-missing",
|
||||||
"Whether to print what paths need to be built or downloaded."};
|
"Whether to print what paths need to be built or downloaded."};
|
||||||
|
|
||||||
|
@ -970,6 +954,27 @@ public:
|
||||||
resolves to a different location from that of the build machine. You
|
resolves to a different location from that of the build machine. You
|
||||||
can enable this setting if you are sure you're not going to do that.
|
can enable this setting if you are sure you're not going to do that.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
Setting<bool> useXDGBaseDirectories{
|
||||||
|
this, false, "use-xdg-base-directories",
|
||||||
|
R"(
|
||||||
|
If set to `true`, Nix will conform to the [XDG Base Directory Specification] for files in `$HOME`.
|
||||||
|
The environment variables used to implement this are documented in the [Environment Variables section](@docroot@/installation/env-variables.md).
|
||||||
|
|
||||||
|
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> This changes the location of some well-known symlinks that Nix creates, which might break tools that rely on the old, non-XDG-conformant locations.
|
||||||
|
|
||||||
|
In particular, the following locations change:
|
||||||
|
|
||||||
|
| Old | New |
|
||||||
|
|-------------------|--------------------------------|
|
||||||
|
| `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` |
|
||||||
|
| `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` |
|
||||||
|
| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` |
|
||||||
|
)"
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
void init() override
|
void init() override
|
||||||
{
|
{
|
||||||
// FIXME: do this lazily?
|
// FIXME: do this lazily?
|
||||||
if (auto cacheInfo = diskCache->cacheExists(cacheUri)) {
|
if (auto cacheInfo = diskCache->upToDateCacheExists(cacheUri)) {
|
||||||
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
wantMassQuery.setDefault(cacheInfo->wantMassQuery);
|
||||||
priority.setDefault(cacheInfo->priority);
|
priority.setDefault(cacheInfo->priority);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -201,8 +201,6 @@ LocalStore::LocalStore(const Params & params)
|
||||||
throw SysError("could not set permissions on '%s' to 755", perUserDir);
|
throw SysError("could not set permissions on '%s' to 755", perUserDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
createUser(getUserName(), getuid());
|
|
||||||
|
|
||||||
/* Optionally, create directories and set permissions for a
|
/* Optionally, create directories and set permissions for a
|
||||||
multi-user install. */
|
multi-user install. */
|
||||||
if (getuid() == 0 && settings.buildUsersGroup != "") {
|
if (getuid() == 0 && settings.buildUsersGroup != "") {
|
||||||
|
@ -1417,7 +1415,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name
|
||||||
auto [hash, size] = hashSink->finish();
|
auto [hash, size] = hashSink->finish();
|
||||||
|
|
||||||
ContentAddressWithReferences desc = FixedOutputInfo {
|
ContentAddressWithReferences desc = FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = method,
|
.method = method,
|
||||||
.hash = hash,
|
.hash = hash,
|
||||||
},
|
},
|
||||||
|
@ -1844,20 +1842,6 @@ void LocalStore::signPathInfo(ValidPathInfo & info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::createUser(const std::string & userName, uid_t userId)
|
|
||||||
{
|
|
||||||
for (auto & dir : {
|
|
||||||
fmt("%s/profiles/per-user/%s", stateDir, userName),
|
|
||||||
fmt("%s/gcroots/per-user/%s", stateDir, userName)
|
|
||||||
}) {
|
|
||||||
createDirs(dir);
|
|
||||||
if (chmod(dir.c_str(), 0755) == -1)
|
|
||||||
throw SysError("changing permissions of directory '%s'", dir);
|
|
||||||
if (chown(dir.c_str(), userId, getgid()) == -1)
|
|
||||||
throw SysError("changing owner of directory '%s'", dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
|
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
|
||||||
LocalStore::State & state,
|
LocalStore::State & state,
|
||||||
const DrvOutput & id)
|
const DrvOutput & id)
|
||||||
|
|
|
@ -281,8 +281,6 @@ private:
|
||||||
void signPathInfo(ValidPathInfo & info);
|
void signPathInfo(ValidPathInfo & info);
|
||||||
void signRealisation(Realisation &);
|
void signRealisation(Realisation &);
|
||||||
|
|
||||||
void createUser(const std::string & userName, uid_t userId) override;
|
|
||||||
|
|
||||||
// XXX: Make a generic `Store` method
|
// XXX: Make a generic `Store` method
|
||||||
FixedOutputHash hashCAPath(
|
FixedOutputHash hashCAPath(
|
||||||
const FileIngestionMethod & method,
|
const FileIngestionMethod & method,
|
||||||
|
|
|
@ -31,10 +31,11 @@ std::map<StorePath, StorePath> makeContentAddressed(
|
||||||
for (auto & ref : oldInfo->references) {
|
for (auto & ref : oldInfo->references) {
|
||||||
if (ref == path)
|
if (ref == path)
|
||||||
refs.self = true;
|
refs.self = true;
|
||||||
|
else {
|
||||||
auto i = remappings.find(ref);
|
auto i = remappings.find(ref);
|
||||||
auto replacement = i != remappings.end() ? i->second : ref;
|
auto replacement = i != remappings.end() ? i->second : ref;
|
||||||
// FIXME: warn about unremapped paths?
|
// FIXME: warn about unremapped paths?
|
||||||
if (replacement != ref) {
|
if (replacement != ref)
|
||||||
rewrites.insert_or_assign(srcStore.printStorePath(ref), srcStore.printStorePath(replacement));
|
rewrites.insert_or_assign(srcStore.printStorePath(ref), srcStore.printStorePath(replacement));
|
||||||
refs.others.insert(std::move(replacement));
|
refs.others.insert(std::move(replacement));
|
||||||
}
|
}
|
||||||
|
@ -51,7 +52,7 @@ std::map<StorePath, StorePath> makeContentAddressed(
|
||||||
dstStore,
|
dstStore,
|
||||||
path.name(),
|
path.name(),
|
||||||
FixedOutputInfo {
|
FixedOutputInfo {
|
||||||
{
|
.hash = {
|
||||||
.method = FileIngestionMethod::Recursive,
|
.method = FileIngestionMethod::Recursive,
|
||||||
.hash = narModuloHash,
|
.hash = narModuloHash,
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,16 +21,16 @@ void Store::computeFSClosure(const StorePathSet & startPaths,
|
||||||
StorePathSet res;
|
StorePathSet res;
|
||||||
StorePathSet referrers;
|
StorePathSet referrers;
|
||||||
queryReferrers(path, referrers);
|
queryReferrers(path, referrers);
|
||||||
for (auto & ref : referrers)
|
for (auto& ref : referrers)
|
||||||
if (ref != path)
|
if (ref != path)
|
||||||
res.insert(ref);
|
res.insert(ref);
|
||||||
|
|
||||||
if (includeOutputs)
|
if (includeOutputs)
|
||||||
for (auto & i : queryValidDerivers(path))
|
for (auto& i : queryValidDerivers(path))
|
||||||
res.insert(i);
|
res.insert(i);
|
||||||
|
|
||||||
if (includeDerivers && path.isDerivation())
|
if (includeDerivers && path.isDerivation())
|
||||||
for (auto & [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
||||||
if (maybeOutPath && isValidPath(*maybeOutPath))
|
if (maybeOutPath && isValidPath(*maybeOutPath))
|
||||||
res.insert(*maybeOutPath);
|
res.insert(*maybeOutPath);
|
||||||
return res;
|
return res;
|
||||||
|
@ -40,12 +40,12 @@ void Store::computeFSClosure(const StorePathSet & startPaths,
|
||||||
std::future<ref<const ValidPathInfo>> & fut) {
|
std::future<ref<const ValidPathInfo>> & fut) {
|
||||||
StorePathSet res;
|
StorePathSet res;
|
||||||
auto info = fut.get();
|
auto info = fut.get();
|
||||||
for (auto & ref : info->references)
|
for (auto& ref : info->references)
|
||||||
if (ref != path)
|
if (ref != path)
|
||||||
res.insert(ref);
|
res.insert(ref);
|
||||||
|
|
||||||
if (includeOutputs && path.isDerivation())
|
if (includeOutputs && path.isDerivation())
|
||||||
for (auto & [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
for (auto& [_, maybeOutPath] : queryPartialDerivationOutputMap(path))
|
||||||
if (maybeOutPath && isValidPath(*maybeOutPath))
|
if (maybeOutPath && isValidPath(*maybeOutPath))
|
||||||
res.insert(*maybeOutPath);
|
res.insert(*maybeOutPath);
|
||||||
|
|
||||||
|
@ -93,12 +93,12 @@ std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv)
|
||||||
[&](const TextInfo & ti) -> std::optional<ContentAddress> {
|
[&](const TextInfo & ti) -> std::optional<ContentAddress> {
|
||||||
if (!ti.references.empty())
|
if (!ti.references.empty())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return static_cast<TextHash>(ti);
|
return ti.hash;
|
||||||
},
|
},
|
||||||
[&](const FixedOutputInfo & fi) -> std::optional<ContentAddress> {
|
[&](const FixedOutputInfo & fi) -> std::optional<ContentAddress> {
|
||||||
if (!fi.references.empty())
|
if (!fi.references.empty())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return static_cast<FixedOutputHash>(fi);
|
return fi.hash;
|
||||||
},
|
},
|
||||||
}, dof->ca);
|
}, dof->ca);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,11 +84,10 @@ public:
|
||||||
|
|
||||||
Sync<State> _state;
|
Sync<State> _state;
|
||||||
|
|
||||||
NarInfoDiskCacheImpl()
|
NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite")
|
||||||
{
|
{
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
|
||||||
Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite";
|
|
||||||
createDirs(dirOf(dbPath));
|
createDirs(dirOf(dbPath));
|
||||||
|
|
||||||
state->db = SQLite(dbPath);
|
state->db = SQLite(dbPath);
|
||||||
|
@ -98,7 +97,7 @@ public:
|
||||||
state->db.exec(schema);
|
state->db.exec(schema);
|
||||||
|
|
||||||
state->insertCache.create(state->db,
|
state->insertCache.create(state->db,
|
||||||
"insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)");
|
"insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?1, ?2, ?3, ?4, ?5) on conflict (url) do update set timestamp = ?2, storeDir = ?3, wantMassQuery = ?4, priority = ?5 returning id;");
|
||||||
|
|
||||||
state->queryCache.create(state->db,
|
state->queryCache.create(state->db,
|
||||||
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
|
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
|
||||||
|
@ -166,6 +165,8 @@ public:
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
std::optional<Cache> queryCacheRaw(State & state, const std::string & uri)
|
std::optional<Cache> queryCacheRaw(State & state, const std::string & uri)
|
||||||
{
|
{
|
||||||
auto i = state.caches.find(uri);
|
auto i = state.caches.find(uri);
|
||||||
|
@ -173,15 +174,21 @@ public:
|
||||||
auto queryCache(state.queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
auto queryCache(state.queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
||||||
if (!queryCache.next())
|
if (!queryCache.next())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
state.caches.emplace(uri,
|
auto cache = Cache {
|
||||||
Cache{(int) queryCache.getInt(0), queryCache.getStr(1), queryCache.getInt(2) != 0, (int) queryCache.getInt(3)});
|
.id = (int) queryCache.getInt(0),
|
||||||
|
.storeDir = queryCache.getStr(1),
|
||||||
|
.wantMassQuery = queryCache.getInt(2) != 0,
|
||||||
|
.priority = (int) queryCache.getInt(3),
|
||||||
|
};
|
||||||
|
state.caches.emplace(uri, cache);
|
||||||
}
|
}
|
||||||
return getCache(state, uri);
|
return getCache(state, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
public:
|
||||||
|
int createCache(const std::string & uri, const Path & storeDir, bool wantMassQuery, int priority) override
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
return retrySQLite<int>([&]() {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
SQLiteTxn txn(state->db);
|
SQLiteTxn txn(state->db);
|
||||||
|
|
||||||
|
@ -190,17 +197,29 @@ public:
|
||||||
auto cache(queryCacheRaw(*state, uri));
|
auto cache(queryCacheRaw(*state, uri));
|
||||||
|
|
||||||
if (cache)
|
if (cache)
|
||||||
return;
|
return cache->id;
|
||||||
|
|
||||||
state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority).exec();
|
Cache ret {
|
||||||
assert(sqlite3_changes(state->db) == 1);
|
.id = -1, // set below
|
||||||
state->caches[uri] = Cache{(int) sqlite3_last_insert_rowid(state->db), storeDir, wantMassQuery, priority};
|
.storeDir = storeDir,
|
||||||
|
.wantMassQuery = wantMassQuery,
|
||||||
|
.priority = priority,
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto r(state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority));
|
||||||
|
assert(r.next());
|
||||||
|
ret.id = (int) r.getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
state->caches[uri] = ret;
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
return ret.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CacheInfo> cacheExists(const std::string & uri) override
|
std::optional<CacheInfo> upToDateCacheExists(const std::string & uri) override
|
||||||
{
|
{
|
||||||
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
return retrySQLite<std::optional<CacheInfo>>([&]() -> std::optional<CacheInfo> {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
@ -208,6 +227,7 @@ public:
|
||||||
if (!cache)
|
if (!cache)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return CacheInfo {
|
return CacheInfo {
|
||||||
|
.id = cache->id,
|
||||||
.wantMassQuery = cache->wantMassQuery,
|
.wantMassQuery = cache->wantMassQuery,
|
||||||
.priority = cache->priority
|
.priority = cache->priority
|
||||||
};
|
};
|
||||||
|
@ -371,4 +391,9 @@ ref<NarInfoDiskCache> getNarInfoDiskCache()
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<NarInfoDiskCache> getTestNarInfoDiskCache(Path dbPath)
|
||||||
|
{
|
||||||
|
return make_ref<NarInfoDiskCacheImpl>(dbPath);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,16 +13,17 @@ public:
|
||||||
|
|
||||||
virtual ~NarInfoDiskCache() { }
|
virtual ~NarInfoDiskCache() { }
|
||||||
|
|
||||||
virtual void createCache(const std::string & uri, const Path & storeDir,
|
virtual int createCache(const std::string & uri, const Path & storeDir,
|
||||||
bool wantMassQuery, int priority) = 0;
|
bool wantMassQuery, int priority) = 0;
|
||||||
|
|
||||||
struct CacheInfo
|
struct CacheInfo
|
||||||
{
|
{
|
||||||
|
int id;
|
||||||
bool wantMassQuery;
|
bool wantMassQuery;
|
||||||
int priority;
|
int priority;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual std::optional<CacheInfo> cacheExists(const std::string & uri) = 0;
|
virtual std::optional<CacheInfo> upToDateCacheExists(const std::string & uri) = 0;
|
||||||
|
|
||||||
virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
virtual std::pair<Outcome, std::shared_ptr<NarInfo>> lookupNarInfo(
|
||||||
const std::string & uri, const std::string & hashPart) = 0;
|
const std::string & uri, const std::string & hashPart) = 0;
|
||||||
|
@ -45,4 +46,6 @@ public:
|
||||||
multiple threads. */
|
multiple threads. */
|
||||||
ref<NarInfoDiskCache> getNarInfoDiskCache();
|
ref<NarInfoDiskCache> getNarInfoDiskCache();
|
||||||
|
|
||||||
|
ref<NarInfoDiskCache> getTestNarInfoDiskCache(Path dbPath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,4 @@ Name: Nix
|
||||||
Description: Nix Package Manager
|
Description: Nix Package Manager
|
||||||
Version: @PACKAGE_VERSION@
|
Version: @PACKAGE_VERSION@
|
||||||
Libs: -L${libdir} -lnixstore -lnixutil
|
Libs: -L${libdir} -lnixstore -lnixutil
|
||||||
Cflags: -I${includedir}/nix -std=c++17
|
Cflags: -I${includedir}/nix -std=c++2a
|
||||||
|
|
|
@ -21,7 +21,7 @@ void ValidPathInfo::sign(const Store & store, const SecretKey & secretKey)
|
||||||
sigs.insert(secretKey.signDetached(fingerprint(store)));
|
sigs.insert(secretKey.signDetached(fingerprint(store)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithReferenences() const
|
std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithReferences() const
|
||||||
{
|
{
|
||||||
if (! ca)
|
if (! ca)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -30,7 +30,7 @@ std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithRef
|
||||||
[&](const TextHash & th) -> ContentAddressWithReferences {
|
[&](const TextHash & th) -> ContentAddressWithReferences {
|
||||||
assert(references.count(path) == 0);
|
assert(references.count(path) == 0);
|
||||||
return TextInfo {
|
return TextInfo {
|
||||||
th,
|
.hash = th,
|
||||||
.references = references,
|
.references = references,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -42,7 +42,7 @@ std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithRef
|
||||||
refs.erase(path);
|
refs.erase(path);
|
||||||
}
|
}
|
||||||
return FixedOutputInfo {
|
return FixedOutputInfo {
|
||||||
foh,
|
.hash = foh,
|
||||||
.references = {
|
.references = {
|
||||||
.others = std::move(refs),
|
.others = std::move(refs),
|
||||||
.self = hasSelfReference,
|
.self = hasSelfReference,
|
||||||
|
@ -54,7 +54,7 @@ std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithRef
|
||||||
|
|
||||||
bool ValidPathInfo::isContentAddressed(const Store & store) const
|
bool ValidPathInfo::isContentAddressed(const Store & store) const
|
||||||
{
|
{
|
||||||
auto fullCaOpt = contentAddressWithReferenences();
|
auto fullCaOpt = contentAddressWithReferences();
|
||||||
|
|
||||||
if (! fullCaOpt)
|
if (! fullCaOpt)
|
||||||
return false;
|
return false;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue